net/http库学习

概念

处理器

  • 处理器:拥有ServeHTTP方法的接口(任何类型)

签名:ServeHTTP(http.ResponseWriter, *http.Request)

  1. ResponseWriter接口
  2. 指向Request结构的指针
  • ServeMux结构(拥有ServeHTTP方法,如上签名)
  • Handler结构
  • 多路复用器 DefaultServeMux(ServeMux结构的实例)

处理器函数

  • 与处理器有相同行为的函数
    • 与ServeHTTP方法有相同的签名

ServeMux

  • HTTP请求多路复用器
    • 接收HTTP请求__并根据请求中的__URL__将请求重定向到正确的__处理器
  • ServeMux结构也实现了ServeHTTP方法,它也是一个处理器
    • ServeMux的ServeHTTP方法,调用与被请求URL相对应的__处理器__的ServeHTTP方法

最简单的Web服务器

import "fmt"
import "net/http"

// 处理器
type HelloHandler struct{}
func ( h *HelloHandler) ServeHTTP ( w http.ResponseWriter, r * http.Request){
    fmt.Fprintf( w, "Hello" )
}

// 处理器函数
func hello( w http.ResponseWriter, r * http.Request){
    fmt.Fprintf( w, "Hello" )
}
    
func main () {
	server := http.Server{
		Addr: "127.0.0.1:8080",
		//Handler: nil, //可以指定处理器
	}
	fmt.Println("hello https://tool.lu/")
	//http.ListenAndServe(":8181", nil)
	//server.ListenAndServe()
	
	// 将 处理器 绑定到DefaultServeMux 
	// Handle是ServeMux结构的方法,而DefaultServeMux是ServeMux的实例
	//hello := HelloHandler{}
	//http.Handle("/hello", &hello)
	
	// 将函数转换为处理器,再将处理器绑定到DefaultServeMux
	//http.HandleFunc( "/hello", hello )
	
	//使用默认的多路复用器DefaultServeMux作为处理器
	server.ListenAndServeTLS("cert.pem", "key.pem")
}

http客户端

http.NewRequest

  • htp.Client -> http.request(http.NewRequest) -> client.Do(request)
  • NewRequest(method, urlStr string, body io.Reader)
    • 第三个参数是请求的body中的内容
  • request.Header.Set
    • 向请求首部添加信息

http.Clinet

  • cient结构api
    • client.get/post/postform
  • client参数配置
    • Transport RoundTripper
    • CheckRedirect func(req Request, via []Request) error
    • Jar CookieJar
    • Timeout time.Duration
  • Transport
    • 为了控制代理、安全套接层设置、保持连接、压缩、超时设置和其它设定,需要创建一个Transport
    • MaxIdleConns
      • 对所有host的最大连接数量
    • MaxIdleConnsPerHost
      • 对__每个host__的最大连接数量
tr := &http.Transport{  
     TLSClientConfig:    &tls.Config{RootCAs: pool},  
     DisableCompression: true,  
}  
client := &http.Client{Transport: tr}  
resp, err := client.Get("https://example.com")


tr := &http.Transport{
            MaxIdleConnsPerHost: 1000, //是否表示最多建立1000个连接?
}
client := &http.Client{
        Transport: tr,
}

http

  • http.Get/Post/Postform

resp.Body.Close()

  • 当客户端使用完response body后必须使用close对其进行关闭

httplib学习

https://github.com/astaxie/beego

概念

  • httplib库主要用来模拟客户端发送HTTP请求
  • 类似于curl工具

使用

  • request对象
  • debug输出
  • 设置clinet的TLS信息

gin学习

package tests

import (
	"encoding/json"
	"fmt"
	"github.com/astaxie/beego/httplib"
	"github.com/gin-gonic/gin"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
	"testing"
	"time"
)

func handleTestGet(c *gin.Context) {
	c.String(http.StatusOK, "test get OK")
}

func handleTestPost(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{"code": 1, "message": "test post OK"})
}

func handleParam(c *gin.Context) {
	name := c.Param("name")
	passwd := c.Param("passwd")
	c.String(http.StatusOK, "name: %s, passwd: %s", name, passwd)
}

func handleQuery(c *gin.Context) {
	name := c.Query("name")
	passwd := c.Query("passwd")
	c.String(http.StatusOK, "name: %s, passwd: %s", name, passwd)
}

func handleHTTPLib(c *gin.Context) {
	c.IndentedJSON(200, gin.H{"code": 1, "data": "ok"})
}

func runtBasicGinServer() {
	fmt.Print("aa")

	router := gin.Default()

	router.GET("/test_get", handleTestGet)
	router.POST("/test_post", handleTestPost)

	router.GET("/test_param/:name/*passwd", handleParam)

	router.GET("/test_query", handleQuery)

	router.GET("/test_httplib", handleHTTPLib)

	group := router.Group("/v1")
	group.GET("/test_group", handleTestGet)

	router.Run(":6543")
}

func printGetResp(resp *http.Response) {
	defer resp.Body.Close()
	bodyBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Printf("read body err %s\n", err.Error())
	}
	log.Printf("resp body is: %+v\n", string(bodyBytes))
}

func printPostResp(resp *http.Response) {
	defer resp.Body.Close()
	bodyBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Printf("read body err %s\n", err.Error())
	}
	type body struct {
		Code    int    `json:"code"`
		Message string `json:"message"`
	}
	respBody := body{}
	err = json.Unmarshal(bodyBytes, &respBody)
	if err != nil {
		log.Printf("unmarshal body err %s\n", err.Error())
	}
	log.Printf("resp body is: %+v\n", respBody)
}

func TestBasicClient(t *testing.T) {
	go runtBasicGinServer()
	time.Sleep(time.Second * 5)
	resp, err := http.Get("http://127.0.0.1:6543/test_get")
	if err != nil {
		log.Printf("get resp err %s\n", err.Error())
	}
	printGetResp(resp)

	resp, err = http.Post("http://127.0.0.1:6543/test_post", "", strings.NewReader(""))
	if err != nil {
		log.Printf("get resp err %s\n", err.Error())
	}
	printPostResp(resp)

	resp, err = http.Get("http://127.0.0.1:6543/test_param/name=Bob/passwd=1234")
	if err != nil {
		log.Printf("get resp err %s\n", err.Error())
	}
	printGetResp(resp)

	resp, err = http.Get("http://127.0.0.1:6543/test_param/name=Bob/")
	if err != nil {
		log.Printf("get resp err %s\n", err.Error())
	}
	printGetResp(resp)

	resp, err = http.Get("http://127.0.0.1:6543/test_param/name=Bob")
	if err != nil {
		log.Printf("get resp err %s\n", err.Error())
	}
	printGetResp(resp)

	resp, err = http.Get("http://127.0.0.1:6543/test_query?name=Alice&passwd=123")
	if err != nil {
		log.Printf("get resp err %s\n", err.Error())
	}
	printGetResp(resp)

	resp, err = http.Get("http://127.0.0.1:6543/v1/test_group")
	if err != nil {
		log.Printf("get resp err %s\n", err.Error())
	}
	printGetResp(resp)

	res := struct {
		Code    int    `json:"code"`
		Message string `json:"message"`
	}{}

	if err := httplib.Get("http://127.0.0.1:6543/test_httplib").ToJSON(&res); err != nil {
		log.Println(err.Error())
	}

	log.Printf("%+v", res)

}

func TestReuseHTTPLink(t *testing.T) {
	go runtBasicGinServer()
	time.Sleep(time.Second * 5)

	tr := &http.Transport{
		MaxIdleConnsPerHost: 100,
		MaxIdleConns:        100,
	}
	c := http.Client{Transport: tr}

	url := "http://127.0.0.1:6543/test_get"

	/*
		连接数,
		当前 无剩余 可用连接时 会创建;
		当前 有剩余 可用连接则 不创建
	*/
	// use channel to control http port numbers
	ch := make(chan struct{}, 100)

	for i := 0; i < 5000; i++ {
		go func(i int) {
			ch <- struct{}{}
			defer func() {
				<-ch
			}()
			req, err := http.NewRequest("GET", url, nil)

			if err != nil {
				log.Printf("get req error %s", err.Error())
			}
			resp, err := c.Do(req)
			if err != nil {
				log.Printf("do req error %s", err.Error())
			}
			defer resp.Body.Close()

			bodyBytes, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				log.Printf("read body error %s", err.Error())
			}

			log.Printf("%d body: %s", i, string(bodyBytes))
		}(i)
		//time.Sleep(time.Microsecond * 50)
		//time.Sleep(time.Microsecond * 50)
	}

	time.Sleep(time.Second * 10)
}

func TestSeqDo(t *testing.T) {
	go runtBasicGinServer()
	time.Sleep(time.Second * 5)

	c := http.Client{}

	url := "http://127.0.0.1:6543/test_get"

	/*
		defaul reuse http link
		there is one link to 6543
	*/
	for i := 0; i < 5000; i++ {
		req, err := http.NewRequest("GET", url, nil)

		if err != nil {
			log.Printf("get req error %s", err.Error())
		}
		resp, err := c.Do(req)
		if err != nil {
			log.Printf("do req error %s", err.Error())
		}
		defer resp.Body.Close()

		bodyBytes, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			log.Printf("read body error %s", err.Error())
		}

		log.Printf("%d body: %s", i, string(bodyBytes))
	}

	time.Sleep(time.Second * 10)
}

func TestSeqHTTPLib(t *testing.T) {
	go runtBasicGinServer()
	time.Sleep(time.Second * 5)

	url := "http://127.0.0.1:6543/test_get"

	/*
		???netstat -anp | grep 6543 | grep ESTABLISHED
	*/
	for i := 0; i < 5000; i++ {
		bodyString, err := httplib.Get(url).String()
		if err != nil {
			log.Printf("httplib get error %s", err.Error())
		}
		log.Printf("%d body: %s", i, bodyString)
	}

	time.Sleep(time.Second * 10)
}

binding学习

github.com/gin-gonic/gin/binding

HTTPS服务

参考文献

《Go Web 编程》
Go语言_HTTP包
深入Go语言网络库的基础实现
golang中发送http请求的几种常见情况
Go语言net/http 解读
go net/http Client使用——长连接客户端的使用
https://github.com/astaxie/beego
beego中文文档

posted on 2018-06-18 20:32  chenguang9239  阅读(294)  评论(0编辑  收藏  举报