8. nethttp模块

  • Go语言内置的net/http包十分的优秀,

  • 压力测试数据

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    # 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
  • HTTP客户端有两个非常重要的类型client和request

1. Client

  • Client 结构体共有四个成员
    1. Transport 指定独立单次HTTP请求的机制
    2. CheckRedirect 指定处理重定向策略
    3. Jar 指定cookie管理器
    4. Timeout 指定文本类型的执行请求的时间限制
1
2
3
4
5
6
7
8
9
import "time"

type Client struct {
	Transport RoundTripper
	CheckRedirect func(req *Request, via []*Request)error
	Jar CookieJar
	Timeout time.Duration

}
  • Client类型主要充当浏览器角色; 它拥有一下方法:
    • Do get和post都是基于do方法进行的封装
    • Head
    • Get
    • Post
    • PostForm
image-20211206214457663

1.1 启动client

image-20211206214745422

 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
/*
 * @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))

}

2. Request

  • request是对请求体的封装吗任何形式的http请求都可以由request来构造, 构造完成之后使用Client发送请求;

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    type Request struct {
    	Method string		//请求方法
    	Url *url.URL 		//请求地址
    	Proto string 		//协议版本
    	Header Header 		//请求头
    	Body io.ReadCloser 	//请求体
    	Form url.Values 	//解析好的表单数据,包括URL字段的query参数和post或者put的表单数据
    	//...
    
    }

3. Get和Post的区别

  1. get是从服务器上获取数据, post是向服务器推送数据
  2. get和post传递参数的方式不同, get将参数放在url后面,post是将表单数据放在请求体中
  3. get数据不安全,用户提交的数据用户能看到, post对用户不可见;
  4. GET请求传输数据量很小, 而POST请求可以传输大量的数据
  5. POST传输数据可以通过设置编码的方式正确转化中文, 而GET请求传输的数据没有变化

3.1 发起GET请求

  1. 简单请求
  • 不带参数
 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
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))

}
  • 带参数

关于GET请求的参数需要使用Go语言内置的net/url这个标准库来处理。

 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
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))
}

对应的Server端HandlerFunc如下:

1
2
3
4
5
6
7
8
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. 自定义get 请求
 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 (
	"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))  // 获取的代码

}

3.2 发起Post请求

  1. 简单post请求
 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
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))
}

对应的Server端HandlerFunc如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
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. 自定义post请求
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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)

}

4. curl工具

  • curl 是一个利用URL语法在命令行工作的文件传输工具, 一般称之为下载工具

  • Curl 基础命令

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    
    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  //添加请求头
    

5. 启动server服务

  • 方式一

     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 (
    	"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
    26
    27
    28
    29
    30
    31
    32
    33
    
    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())
    }

常见错误码

  • 200 成功状态吗
  • 301 临时重定向
  • 302 永久重定向
  • 400 客户端 语法错误
  • 401 客户端身份认证失败
  • 403 拒绝访问
  • 404 找不到资源
  • 500 服务器内部错误
  • 501 服务器不支持
  • 502 错误的网关
  • 503 服务器过载
  • 504 网关超时
  • 505 http协议错误
Buy me a coffee~
Fred 支付宝支付宝
Fred 微信微信
0%