zoukankan      html  css  js  c++  java
  • 使用go-retryablehttp包实现http“链接池”效果

    前言

      在go中使用http的方式获取数据时每次通常都会创建一个http的Client对象处理请求,但是如果一次任务中请求的非常频繁,每一次请求都要创建一个Client对象的话势必会造成链接资源的浪费。

      在实际中我们知道有一种“链接池”的概念,就是说提前在链接池中创建好链接,每一次请求前都从这个“链接池”中获取链接,请求处理完毕后不释放链接而是将这个链接重新放入链接池中,以便下一次请求使用,这样便十分有效的利用了链接资源,同时也有效的降低了服务器的负载。

    第三方包实现

      Go中有一个第三方包go-retryablehttp能够实现上述的效果。

    Demo

      自己做了一个demo:

      utils.go中主要实现了链接池:

    package utils
    
    import (
        "crypto/tls"
        "github.com/hashicorp/go-retryablehttp"
        "net"
        "net/http"
        "sync"
        "time"
    )
    
    const (
        ConnectTimeout = 10 * time.Second
        RequestTimeout = 30 * time.Second
    )
    
    // 加一个锁 防止多线程同时写入字典的情况
    var muClient sync.Mutex
    
    func GetHttpClient(tag string, config *tls.Config) *http.Client {
        muClient.Lock()
        // 连接池字典
        clientMap := make(map[string]*retryablehttp.Client)
    
        // 带证书认证的结构
        transportMap := make(map[string]*http.Transport)
    
        defer muClient.Unlock()
        // 如果是一个带证书的请求,在这里处理
        if config != nil {
            if _, ok := transportMap[tag]; !ok {
                transportMap[tag] = NewTransport()
                transportMap[tag].TLSClientConfig = config
            }
            return &http.Client{
                Transport: transportMap[tag],
                Timeout:   RequestTimeout,
            }
        }
        if _, ok := clientMap[tag]; !ok {
            clientMap[tag] = NewRetryHttpClient()
        }
        return clientMap[tag].StandardClient()
    }
    
    // 使用http连接池
    func NewTransport() *http.Transport {
        dialContext := (&net.Dialer{
            Timeout:   ConnectTimeout,
            KeepAlive: 30 * time.Second,
        }).DialContext
    
        return &http.Transport{
            Proxy:                 http.ProxyFromEnvironment,
            DialContext:           dialContext,
            MaxIdleConns:          100,
            MaxConnsPerHost:       25,
            IdleConnTimeout:       30 * time.Second,
            TLSHandshakeTimeout:   ConnectTimeout,
            ExpectContinueTimeout: 1 * time.Second,
        }
    }
    
    func NewRetryHttpClient() *retryablehttp.Client {
        retryClient := retryablehttp.NewClient()
        retryClient.RetryMax = 10
        retryClient.Logger = nil
        return retryClient
    }
    utils.go

      main.go中使用链接池并发处理http请求:

    package main
    
    import (
        "fmt"
        "http_pool/utils"
        "io/ioutil"
        "log"
        "net/http"
        "sync"
    )
    
    func main() {
    
        // 使用 waitGroup开goroutine
        wait := sync.WaitGroup{}
        for i := 0; i < 10; i++ {
            wait.Add(1)
            go getBaidu(i, &wait)
        }
        // 等子goroutine走完了再走主的
        wait.Wait()
        fmt.Println("------ 所有goroutine均请求完成 ------")
    }
    
    // 测试请求百度链接的代码 ———— TODO 里面使用 "http链接池" 做优化
    func getBaidu(i int, wait *sync.WaitGroup) {
        // 在这里写 wait.Done()
        defer wait.Done()
    
        url := "http://www.baidu.com"
        req, err := http.NewRequest("GET", url, nil)
    
        if err != nil {
            log.Panic("err1: ", err)
        }
    
        req.Header.Add("cache-control", "no-cache")
        req.Header.Add("Postman-Token", "f4a59d50-9672-4710-a8c0-abb8a453b199")
    
        // TODO:注意!不使用"http链接池"会在程序运行期间产生大量无效的链接资源,给服务器增加不必要的负担
        // res, err := http.DefaultClient.Do(req)
    
        // 使用 "http链接池" 可以优化http链接资源
        httpClient := utils.GetHttpClient("get_baidu", nil)
        res, err := httpClient.Do(req)
    
        if err != nil {
            log.Panic("err2: ", err)
        }
    
        defer res.Body.Close()
        // body, err := ioutil.ReadAll(res.Body)
        _, err = ioutil.ReadAll(res.Body)
        if err != nil {
            log.Panic("err3: ", err)
        }
        fmt.Printf("%d 号Gorountine成功请求了百度!
    ", i)
        //fmt.Println(res)
        //fmt.Println(string(body))
    }
    main.go

    结果

      由于没有实际场景,所以使用链接池降低服务器链接资源的效果没法演示(实际中可以在程序跑起来后使用 netstat 命令查看效果),这里展示一下上述程序的执行结果:

    ~~~

  • 相关阅读:
    Mayan游戏 (codevs 1136)题解
    虫食算 (codevs 1064)题解
    靶形数独 (codevs 1174)题解
    黑白棋游戏 (codevs 2743)题解
    神经网络 (codevs 1088) 题解
    The Rotation Game (POJ 2286) 题解
    倒水问题 (codevs 1226) 题解
    银河英雄传说 (codevs 1540) 题解
    生日蛋糕 (codevs 1710) 题解
    第一章 1.11 高阶函数
  • 原文地址:https://www.cnblogs.com/paulwhw/p/14476356.html
Copyright © 2011-2022 走看看