nethttp模块
Go语言内置的
net/http
包十分的优秀,压力测试数据
# mac 配置 8核 16G内存 # goland 多核模式 16 threads and 200 connections Thread Stats Avg Stdev Max +/- Stdev Latency 5.75ms 14.07ms 224.91ms 90.89% Req/Sec 9.46k 6.90k 100.80k 80.15% 4403567 requests in 30.09s, 596.34MB read Socket errors: connect 0, read 56, write 0, timeout 0 Requests/sec: 146360.93 # 每秒并发数高达 14.6w 是sanic 10进程的3倍 Transfer/sec: 19.82MB
1
2
3
4
5
6
7
8
9
10HTTP客户端有两个非常重要的类型client和request
# 1. Client
- Client 结构体共有四个成员
- Transport 指定独立单次HTTP请求的机制
- CheckRedirect 指定处理重定向策略
- Jar 指定cookie管理器
- Timeout 指定文本类型的执行请求的时间限制
import "time"
type Client struct {
Transport RoundTripper
CheckRedirect func(req *Request, via []*Request)error
Jar CookieJar
Timeout time.Duration
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
- Client类型主要充当浏览器角色; 它拥有一下方法:
- Do get和post都是基于do方法进行的封装
- Head
- Get
- Post
- PostForm
# 1.1 启动client
/*
* @date: 2021/12/6
* @desc: ...
*/
package main
import (
"fmt"
"io/ioutil"
"net"
"net/http"
"time"
)
func main() {
// 创建连接池
transport := &http.Transport{
DialContext: (&net.Dialer{
Timeout: 30 * time.Second, // 连接超时
KeepAlive: 30 * time.Second, // 长连接保持时间
}).DialContext,
MaxIdleConns: 100, // 最大空闲连接
IdleConnTimeout: 90 * time.Second, //空闲超时时间
TLSHandshakeTimeout: 10 * time.Second, //tls 握手超时时间 (https使用)
ExpectContinueTimeout: 1 * time.Second, //100-continue状态码超时时间
}
// 创建客户端
client := http.Client{
Timeout: time.Second * 30, // 请求超时时间
Transport: transport,
}
// 请求数据
resp, err := client.Get("http://127.0.0.1:8080/bye")
if err != nil {
panic(err)
}
defer resp.Body.Close()
// 读取body内容
bds, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
fmt.Println(string(bds))
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 2. Request
request是对请求体的封装吗任何形式的http请求都可以由request来构造, 构造完成之后使用Client发送请求;
type Request struct { Method string //请求方法 Url *url.URL //请求地址 Proto string //协议版本 Header Header //请求头 Body io.ReadCloser //请求体 Form url.Values //解析好的表单数据,包括URL字段的query参数和post或者put的表单数据 //... }
1
2
3
4
5
6
7
8
9
10
# 3. Get和Post的区别
- get是从服务器上获取数据, post是向服务器推送数据
- get和post传递参数的方式不同, get将参数放在url后面,post是将表单数据放在请求体中
- get数据不安全,用户提交的数据用户能看到, post对用户不可见;
- GET请求传输数据量很小, 而POST请求可以传输大量的数据
- POST传输数据可以通过设置编码的方式正确转化中文, 而GET请求传输的数据没有变化
# 3.1 发起GET请求
- 简单请求
- 不带参数
package main
import (
"fmt"
"io"
"io/ioutil"
"net/http"
)
func main() {
get, err := http.Get("http://www.baidu.com")
if err != nil {
fmt.Println("get error", err)
return
}
defer func(Body io.ReadCloser) {
fmt.Println("close error")
err := Body.Close()
if err != nil {
}
}(get.Body)
all, err := ioutil.ReadAll(get.Body)
if err != nil {
fmt.Println("read error")
return
}
fmt.Println(string(all))
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
- 带参数
关于GET请求的参数需要使用Go语言内置的net/url
这个标准库来处理。
func main() {
apiUrl := "http://127.0.0.1:9090/get"
// URL param
data := url.Values{}
data.Set("name", "小王子")
data.Set("age", "18")
u, err := url.ParseRequestURI(apiUrl)
if err != nil {
fmt.Printf("parse url requestUrl failed, err:%v\n", err)
}
u.RawQuery = data.Encode() // URL encode
fmt.Println(u.String())
resp, err := http.Get(u.String())
if err != nil {
fmt.Printf("post failed, err:%v\n", err)
return
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf("get resp failed, err:%v\n", err)
return
}
fmt.Println(string(b))
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
对应的Server端HandlerFunc如下:
func getHandler(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
data := r.URL.Query()
fmt.Println(data.Get("name"))
fmt.Println(data.Get("age"))
answer := `{"status": "ok"}`
w.Write([]byte(answer))
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
- 自定义get 请求
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main() {
client := &http.Client{}
request, err := http.NewRequest("GET", "http://www.baidu.com", nil)
if err != nil {
fmt.Println(err)
}
response, err := client.Do(request)
if err != nil {
fmt.Println(err)
}
fmt.Println(response.StatusCode) // 响应码
res, err := ioutil.ReadAll(response.Body)
fmt.Println(string(res)) // 获取的代码
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 3.2 发起Post请求
- 简单post请求
package main
import (
"fmt"
"io/ioutil"
"net/http"
"strings"
)
// net/http post demo
func main() {
url := "http://127.0.0.1:9090/post"
// 表单数据
//contentType := "application/x-www-form-urlencoded"
//data := "name=小王子&age=18"
// json
contentType := "application/json"
data := `{"name":"小王子","age":18}`
resp, err := http.Post(url, contentType, strings.NewReader(data))
if err != nil {
fmt.Printf("post failed, err:%v\n", err)
return
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf("get resp failed, err:%v\n", err)
return
}
fmt.Println(string(b))
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
对应的Server端HandlerFunc如下:
func postHandler(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
// 1. 请求类型是application/x-www-form-urlencoded时解析form数据
r.ParseForm()
fmt.Println(r.PostForm) // 打印form数据
fmt.Println(r.PostForm.Get("name"), r.PostForm.Get("age"))
// 2. 请求类型是application/json时从r.Body读取数据
b, err := ioutil.ReadAll(r.Body)
if err != nil {
fmt.Printf("read request.Body failed, err:%v\n", err)
return
}
fmt.Println(string(b))
answer := `{"status": "ok"}`
w.Write([]byte(answer))
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- 自定义post请求
package main
import (
"fmt"
"io/ioutil"
"net/http"
"strings"
)
func main() {
resq, err:= http.Post("http://www.baidu.com",
"application/x-www-form-urlencoded",
strings.NewReader("user=admin&pass=admin"))
if err !=nil{
fmt.Println(err)
}
defer resq.Body.Close()
body, err:= ioutil.ReadAll(resq.Body)
if err!=nil{
fmt.Println(err)
}
fmt.Println(body)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 4. curl工具
curl 是一个利用URL语法在命令行工作的文件传输工具, 一般称之为下载工具
Curl 基础命令
curl URL // get请求获取页面, 如果是一个图片,将会下载图片 curl -o URL // 下载到本地 curl -i URL // 显示头信息 curl -v URL // 显示一次http通信的整个过程 curl -X POST --data "data=xxx" URL //发送post请求 curl --user-agent "[USER AGENT]" URL // 添加useragent curl --cokie "cookie" URL //添加cookie curl --head "head" URL //添加请求头
1
2
3
4
5
6
7
8
9
10
# 5. 启动server服务
方式一
package main import ( "fmt" "math/rand" "net/http" "time" ) func indexHandler(w http.ResponseWriter, r *http.Request) { rand.Seed(time.Now().UnixNano()) randNum := rand.Intn(2) if randNum == 0 { time.Sleep(5 * time.Second) } fmt.Fprintf(w, "quick response") } func main() { http.HandleFunc("/", indexHandler) err := http.ListenAndServe(":8000", nil) if err != nil { panic(err) } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25方式二:
package main import ( "log" "net/http" "time" ) const ADDR = "127.0.0.1:8080" func sayBay(w http.ResponseWriter, r *http.Request) { time.Sleep(time.Second * 1) _, err := w.Write([]byte("bye bye!")) if err != nil { return } } func main() { // 创建路由器 mux := http.NewServeMux() // 设置路由规则 mux.HandleFunc("/bye", sayBay) // 创建服务器 server := &http.Server{ Addr: ADDR, WriteTimeout: time.Second * 3, Handler: mux, } // 监听端口并提供服务 log.Println("starting http server at: " + ADDR) log.Fatal(server.ListenAndServe()) }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 常见错误码
- 200 成功状态吗
- 301 临时重定向
- 302 永久重定向
- 400 客户端 语法错误
- 401 客户端身份认证失败
- 403 拒绝访问
- 404 找不到资源
- 500 服务器内部错误
- 501 服务器不支持
- 502 错误的网关
- 503 服务器过载
- 504 网关超时
- 505 http协议错误
上次更新: 2023/04/16, 18:35:33