1. 程式人生 > >go語言 http學習

go語言 http學習

get ram stax serve config lan asi html text

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中文文檔

go語言 http學習