V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
biuyixia
V2EX  ›  程序员

大佬们,用 go 同时发起几万个 get 请求,怎么优化请求函数呢?

  •  1
     
  •   biuyixia · 2022-09-22 09:41:27 +08:00 · 5029 次点击
    这是一个创建于 784 天前的主题,其中的信息可能已经有所发展或是发生改变。

    代码如下:

    func check_url(url string) {
        client := &http.Client{
            Timeout: time.Second * 5,
        }
        req, _ := http.NewRequest("GET", url, nil)
        req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.82 Safari/537.36")
        resp, err := client.Do(req)
        if err == nil {
            defer resp.Body.Close()
    
            status := resp.Status
            if strings.Contains(status, "200") {
                fmt.Println(url,"===请求成功===")
            }
        }
    }
    

    小弟刚学 go ,我需要同时并发几万个 url 请求,调用 check_url 函数,发现有时候明明可以访问的端口,却报 context deadline exceeded (Client.Timeout exceeded while awaiting headers) 错误,怎么优化这个 check_url 函数呢?

    37 条回复    2022-10-08 15:25:27 +08:00
    hxysnail
        1
    hxysnail  
       2022-09-22 09:44:17 +08:00
    你的 Client 设置了 5 秒钟超时,把时间调长一点呗
    biuyixia
        2
    biuyixia  
    OP
       2022-09-22 09:52:26 +08:00
    内网请求,大佬。不需要那么长的超时时间
    biuyixia
        3
    biuyixia  
    OP
       2022-09-22 09:54:52 +08:00
    @hxysnail

    需要对内网进行扫描,单个请求的时候如 8888 ,可以正常请求到判断为 200 状态,当对这个 ip 并发全端口进行扫描的时候,通过打印 resp, err := client.Do(req)的 err 可知:
    Get "...192.168.100.126:8888": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
    biuyixia
        5
    biuyixia  
    OP
       2022-09-22 10:04:52 +08:00
    @licoycn 看样子还是需要调整下函数的超时时间,,服务端没法控制。不知道还没有更优解。。
    morri
        6
    morri  
       2022-09-22 10:10:21 +08:00
    https://github.com/go-resty/resty
    试试用这个 http 工具呢?
    Nazz
        7
    Nazz  
       2022-09-22 10:15:24 +08:00   ❤️ 1
    需要限制并发协程数量
    biuyixia
        8
    biuyixia  
    OP
       2022-09-22 10:15:41 +08:00
    @morri 谢谢大佬。我试试哈
    coderxy
        9
    coderxy  
       2022-09-22 10:16:16 +08:00
    go 的 http client 默认有连接池限制的, 先把那个限制调大一点再试试
    d29107d
        10
    d29107d  
       2022-09-22 10:18:12 +08:00 via Android
    也有可能被请求端承受不了啊
    biuyixia
        11
    biuyixia  
    OP
       2022-09-22 10:18:46 +08:00
    @Nazz 我是通过 Ants github.com/panjf2000/ants ,因为用的同一个函数,通过 NewPoolWithFunc 创建了一个 20000 的协程池。
    seers
        12
    seers  
       2022-09-22 10:27:53 +08:00 via Android
    系统的 open file 数没打开吧,连接满了后面的 tcp 连接没发出去所以超时了,ulimit 确认下
    seers
        13
    seers  
       2022-09-22 10:31:03 +08:00 via Android
    你最后 err 打印下看看错误是啥,不要是 nil 后就不管了
    dcalsky
        14
    dcalsky  
       2022-09-22 10:34:54 +08:00
    optimize your http client transport, increase the number of idle connection for each host in the connection pool.
    runningman
        15
    runningman  
       2022-09-22 10:48:40 +08:00
    go routine
    fyooo
        16
    fyooo  
       2022-09-22 11:24:48 +08:00
    @Nazz +1 大概率是服务器扛不住了,内网服务扛不住那么大并发的。
    shawndev
        17
    shawndev  
       2022-09-22 11:29:08 +08:00
    堪比 ddos
    sadfQED2
        18
    sadfQED2  
       2022-09-22 11:42:14 +08:00   ❤️ 1
    ```
    var client *http.Client = &http.Client{
    Timeout: time.Second * 5,
    }

    func check_url(url string) {

    go func() {
    req, _ := http.NewRequest("GET", url, nil)
    req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.82 Safari/537.36")
    resp, err := client.Do(req)
    if err == nil {
    defer resp.Body.Close()

    status := resp.Status
    if strings.Contains(status, "200") {
    fmt.Println(url, "===请求成功===")
    }
    }
    }()

    }

    ```


    试试这个?你每次都 new 一个 httpclient 我记得好像是不能用上 keepalive 的,使用同一个 client ,保证 keepalive 。然后开协程疯狂请求就完事了。

    但是你确定服务器扛得住?
    aino
        19
    aino  
       2022-09-22 11:50:22 +08:00
    go 同时发起几万个 get 请求并发这么猛的吗,请教下 java 能做到吗,具体怎么做呢
    biuyixia
        20
    biuyixia  
    OP
       2022-09-22 12:26:03 +08:00
    @shawndev 太真实了,哈哈。看来得另想他法了。。。
    biuyixia
        21
    biuyixia  
    OP
       2022-09-22 12:38:52 +08:00
    @sadfQED2 谢大佬。那就 client 共用。
    wheeler
        22
    wheeler  
       2022-09-22 13:08:33 +08:00 via iPhone
    @sadfQED2 #18 keepalive 不是 transport 维护的吗?不指定 transport 都是复用默认的 transport 吧?
    wheeler
        23
    wheeler  
       2022-09-22 13:10:43 +08:00 via iPhone
    @wheeler #22

    // Transport specifies the mechanism by which individual
    HTTP requests are made.
    If nil, DefaultTransport is used.

    https://pkg.go.dev/net/http
    a132811
        24
    a132811  
       2022-09-22 13:34:42 +08:00
    Client.Timeout exceeded while awaiting headers
    可能服务端超时、或者带宽不够吧。
    1. 确认你的网络有没有瓶颈
    2. 确认你的服务真实并发:用 ab/vegeta/gowrk 等工具压测一下
    monkeyWie
        25
    monkeyWie  
       2022-09-22 13:37:04 +08:00
    @aino #19 能啊,上 nio 、或者直接 netty
    p23XnFNH1Wq953rV
        26
    p23XnFNH1Wq953rV  
       2022-09-22 13:47:36 +08:00
    也可能是目标服务器瓶颈, 或者 seers 说的问题
    biuyixia
        27
    biuyixia  
    OP
       2022-09-22 14:02:17 +08:00
    @derrick1 好的大佬。估计就是了
    zbatman
        28
    zbatman  
       2022-09-22 15:26:27 +08:00
    刑啊
    sadfQED2
        29
    sadfQED2  
       2022-09-22 15:30:09 +08:00 via Android
    @wheeler 额,确实是。是我记错了
    lysS
        30
    lysS  
       2022-09-22 15:34:09 +08:00
    接口扫描是吧?不用 GET 、用 HEAD ,更好的方法其实还是用 tcp 请求
    lysS
        31
    lysS  
       2022-09-22 15:37:38 +08:00
    还有啊,扫描不同的地址,连接池是没用的
    iceiceice
        32
    iceiceice  
       2022-09-22 15:39:22 +08:00
    用 fasthttp
    aladdinding
        33
    aladdinding  
       2022-09-22 16:03:43 +08:00
    用同一个 client transport 调节大点
    bthulu
        34
    bthulu  
       2022-09-22 16:40:31 +08:00
    带宽打满了吧, 后面的请求只能排队了, 然后就超时了
    biuyixia
        35
    biuyixia  
    OP
       2022-09-30 09:10:11 +08:00
    @seers 大佬,我想问问你说的 open file 数是什么?还有 ulimit ,跟 http 请求有关系吗,小弟刚学。请指教

    你说的第二个,最后 err 打印下,那个异常端口就是报的 context deadline exceeded (Client.Timeout exceeded while awaiting headers)
    seers
        36
    seers  
       2022-09-30 14:29:04 +08:00
    @biuyixia 不严谨简单的说,在 Linux 里面发起一个 HTTP 请求等同于打开一个文件(一切皆文件,Unix 哲学),系统打开文件数是有上限的,由 ulimit 查看修改,如果限制数太低,后面发起的连接就会进入队列,然后进入队列的请求会超时报错,当然这只是猜测,还需要你多分析。
    biuyixia
        37
    biuyixia  
    OP
       2022-10-08 15:25:27 +08:00
    @seers 谢谢大佬。我这个 windows
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5598 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 32ms · UTC 06:34 · PVG 14:34 · LAX 22:34 · JFK 01:34
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.