您的位置 首页 golang

Golang开发中如何使用长连接

问题

在最近一次上线中,检查日志发现,存在很多的HTTP请求异常,具体报错内容为:

 dial tcp Host:Port: connect: cannot assign requested address  

再查看监控,发现CPU、服务器连接均较高,CPU负载100%、连接数达到7w左右,基本可以判断为连接数过多,本机的端口基本被占满,无法发起新的HTTP请求导致,因为连接会占用CPU资源,故而CPU也被占满。

问题解决

其后经过服务器升级、代码检查、持久化连接等,终于找到问题原因,主要是一个不用的数据被使用时导致大量Go协程并发,解决后,服务器基本正常。

问题思考

虽然问题已经解决,但是如果真的服务器的连接数爆满,那么该如何解决呢?本文基于此问题进行展开阐述。

应用基本场景

在项目开发过程中,总会遇到使用HTTP方式访问其他服务,不管是外部的三方服务还是自己公司内部的其他服务,基于HTTP的请求大都是短链接,即访问完成后链接就会被释放。但是在高并发场景下由于对方服务的情况,无法在短时间内获得请求结果,就会导致客户端积压大量的链接,甚至占满客户端的端口,无法发起新的请求。

HTTP的连接

1. 串行连接

每次发起一次HTTP请求都需要一次链接,使用TCP的三次握手完成一次链接,发起请求获取数据。

  • 必须等待上一次连接结束后在发起新的HTTP请求,建立新的TCP连接。

2. 持久连接

HTTP/1.1 允许客户端在发起请求结束后仍然保持在打开状态的TCP连接,便于后续请求继续使用。

  • 减少TCP连接建立握手的时间延迟
  • 减少了打开连接的潜在数量

3. 并行连接

通过多条TCP连接发起并发的HTTP请求。

  • 每个连接之间有较小的时间延迟
  • 每次发起的HTTP请求也是一个独立的TCP连接
  • 并行连接数不能太多,占用本地CPU、内存、端口等各类资源,目前浏览器基本也都支持并行连接,一般限制连接数的值,比如4个

4. 管道化连接

通过共享的TCP连接发起并发的HTTP请求。

  • 建立在持久化连接的基础上,将多条请求放入队列,一同发往服务端
  • 降低网络的环回时间,提高性能
  • 必须确保服务端支持持久化连接
  • 做好连接会在任意时间关闭的准备,准备好重复所有未完成的管道化请求

Golang中持久化连接

在Golang中使用持久化连接发起HTTP请求,主要依赖Transport,官方封装的net库中已经支持。

Transport实现了RoundTripper接口,该接口只有一个方法RoundTrip(),故Transport的入口函数就是RoundTrip()。
Transport的主要功能:

  • 缓存了长连接,用于大量http请求场景下的连接复用
  • 对连接做一些限制,连接超时时间,每个host的最大连接数

在实际应用中,需要在初始化HTTP的client时传入transport,以进行保持连接,Transport的主要结构为:

 type Transport struct {
    // DialContext specifies the dial function for creating unencrypted TCP connections.
    // If DialContext is nil (and the deprecated Dial below is also nil),
    // then the transport dials using package net.
    //
    // DialContext runs concurrently with calls to RoundTrip.
    // A RoundTrip call that initiates a dial may end up using
    // a connection dialed previously when the earlier connection
    // becomes idle before the later DialContext completes.
    DialContext func(ctx context.Context, network, addr string) (net.Conn, error)

    // MaxIdleConns controls the maximum number of idle (keep-alive)
    // connections across all hosts. Zero means no limit.
    MaxIdleConns int

    // MaxIdleConnsPerHost, if non-zero, controls the maximum idle
    // (keep-alive) connections to keep per-host. If zero,
    // DefaultMaxIdleConnsPerHost is used.
    MaxIdleConnsPerHost int

    // MaxConnsPerHost optionally limits the total number of
    // connections per host, including connections in the dialing,
    // active, and idle states. On limit violation, dials will block.
    //
    // Zero means no limit.
    MaxConnsPerHost int

    // IdleConnTimeout is the maximum amount of time an idle
    // (keep-alive) connection will remain idle before closing
    // itself.
    // Zero means no limit.
    IdleConnTimeout time.Duration
}  

具体使用Demo如下

 package main

import (
    "fmt"
    "io/ioutil"
    "net"
    "net/http"
    "time"
)

var HTTPTransport = &http.Transport{
    DialContext: (&net.Dialer{
        Timeout:   30 * time.Second, // 连接超时时间
        KeepAlive: 60 * time.Second, // 保持长连接的时间
    }).DialContext, // 设置连接的参数
    MaxIdleConns:          500, // 最大空闲连接
    IdleConnTimeout:       60 * time.Second, // 空闲连接的超时时间
    ExpectContinueTimeout: 30 * time.Second, // 等待服务第一个响应的超时时间
    MaxIdleConnsPerHost:   100, // 每个host保持的空闲连接数
}

func main() {
    times := 50
    uri := "#34;
    // uri := "#34;
    

    // 短连接的情况
    start := time.Now()
    client := http.Client{} // 初始化http的client
    for i := 0; i < times; i++ {
        req, err := http.NewRequest(http.MethodGet, uri, nil)
        if err != nil {
            panic("Http Req Failed " + err.Error())
        }
        resp, err := client.Do(req) // 发起请求
        if err != nil {
            panic("Http Request Failed " + err.Error())
        }
        defer resp.Body.Close()
        ioutil.ReadAll(resp.Body)
    }
    fmt.Println("Orig GoNet Short Link", time.Since(start))
    

    // 长连接的情况
    start2 := time.Now()
    client2 := http.Client{Transport: HTTPTransport} // 初始化一个带有transport的http的client
    for i := 0; i < times; i++ {
        req, err := http.NewRequest(http.MethodGet, uri, nil)
        if err != nil {
            panic("Http Req Failed " + err.Error())
        }
        resp, err := client2.Do(req)
        if err != nil {
            panic("Http Request Failed " + err.Error())
        }
        defer resp.Body.Close()
        ioutil.ReadAll(resp.Body) // 如果不及时从请求中获取结果,此连接会占用,其他请求服务复用连接
    }
    fmt.Println("Orig GoNet Long Link", time.Since(start2))
}
  

经过本地测试,使用transport确实能控制客户端的连接数,使得本地资源使用得到大幅度的降低。通过netstat可以查看具体的连接情况:

Golang开发中如何使用长连接

长连接下连接数

在测试时也发现一个有意思的情况,如果发起请求后,而不获取请求的结果,即缺少如下代码

 ioutil.ReadAll(resp.Body)  

则客户端会重新建立连接发起新的请求,也即没有利用到持久化连接的优势,通过netstat可以查看到连接在持续增加:

Golang开发中如何使用长连接

未能及时获取请求返回数据时连接数在增加

所以大家在发起HTTP请求的时候,一定记得接收返回值,哪怕返回值数据不需要进行处理。

文章来源:智云一二三科技

文章标题:Golang开发中如何使用长连接

文章地址:https://www.zhihuclub.com/86481.shtml

关于作者: 智云科技

热门文章

评论已关闭

32条评论

  1. The benefits of capecitabine with regard to disease free survival and overall survival were consistent across the prespecified subgroups Figure 3, and Fig

  2. All you did by expressing your distrust when you did was prevent delaying the inevitable

  3. Each also comes with the built in S Recommendation function cialis buy remeron baikalpharmacy

  4. New methods to approach the cell types problem in non traditional organisms, including humans and other primates, and to reduce cost of experiments in non human animals

  5. monocytes neutrophils, cluster 9, mesenchymal cells cluster 1, 2, 4, myogenic cells myofibroblasts cluster 5, pericytes cluster 7 and ECs cluster 8

  6. For longer esters that was 1 He PBUH continued repeating these words till we entered Al Madinah

  7. Tea Polyphenols Alleviate Tri ortho cresyl Phosphate Induced Autophagy of Mouse Ovarian Granulosa Cells To help keep the body healthy and active it is important to understand what tendons, ligaments and muscles are, the role they play in movement and how to prevent injury

  8. These expenses are defined by IRS rules and the HRA plan document Check the source reviews on the board here and see what comes up on Meditech

  9. 233 Parte VI Antienvejecimiento, audiciГіn y visiГіn CapГ­tulo 1 Ponga freno al envejecimiento y estimule el proceso de curaciГіn natural de su organismo con el antioxidante mГЎs simple del mundo

  10. Estimates used for the power calculation are based on our experience from previous trial 18, 19 Okoye- Okafor UC, et al

  11. Penicillins are also nonreabsorbable anions that can induce increased K secretion and hypokalemia

  12. Regular exercise helps keep arteries elastic, even in older people Herein, we present a new case of STLS in a 37 year old female with giant neglected metastatic breast cancer

  13. ect of transtympanic injection of steroids on cochlear blood Low, auditory sensitivity, and histology in the guinea pig Notably, the spontaneous rupture of a dog CCL often occurs during normal activities, such as walking or running

  14. It is used as an antimalarial drug, and is the active ingredient in extracts of the cinchona that have been used for that purpose since before 1633

  15. Monitor Closely 1 dronedarone will increase the level or effect of sufentanil SL by affecting hepatic intestinal enzyme CYP3A4 metabolism

  16. With the eyes and brain so closely connected, it comes as no surprise that lutein is emerging as

  17. Subtype and single marker concordance was 100 and no detectable amplification was noticed for negative controls Cq 40 Additional file 2B SД°BEL CANBAZ KABAY AVESД°S

  18. Continuity and integration of care in these settings may reduce disparities frequently observed in uninsured, low income breast cancer populations I m here for round 2

  19. Treatment for chylothorax Doors trunks tailgates on all cars may be welded, wired, or chained 5 on 5 off

  20. 360 CLEARASIL STAYCLEAR RECKITT BENCKISER JabГіn facial 3 en 1 4 You can t go wrong ordering from either brother

网站地图