You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

LibCurl C++ HTTP/2多路复用场景下如何控制请求发送速率

libcurl HTTP/2多路复用请求限流实现方案

问题答复

  • 存在可实现每3毫秒发送一批多路复用请求的方案,核心逻辑为增量添加easy handle到multi handle而非一次性添加所有请求
  • 将main完整逻辑封装到for循环中加延迟的方案不可行,会导致每次循环重建TCP连接,丢失HTTP/2多路复用的优势,额外的握手开销也会拖慢整体性能

实现思路

全程复用同一个multi handle维持单TCP连接,不一次性将所有请求的easy handle加入multi handle,而是分批增量添加,每添加一批后插入3毫秒延迟再添加下一批,请求发送过程全程异步非阻塞,无需等待服务端响应即可继续发送下一批请求,完美匹配需求。

多路复用是HTTP/2中的一种机制,可通过单个TCP连接异步发送多个HTTP请求、接收对应响应,是HTTP/2协议的核心特性。


修改后代码

#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <chrono>
#include <string>
#include <algorithm>

/* somewhat unix-specific */
#include <sys/time.h>
#include <unistd.h>

/* curl stuff */
#include <curl/curl.h>
#include <curl/mprintf.h>

#ifndef CURLPIPE_MULTIPLEX
#define CURLPIPE_MULTIPLEX 0
#endif

struct CURLMsg *msg;

struct transfer {
    CURL *easy;
    unsigned int num;
    std::string contents;
};

#define NUM_HANDLES 1000 // 总请求数上限,可根据需求调整
const int BATCH_SIZE = 3; // 每批发送的请求数,可调整
const int SEND_INTERVAL_US = 3000; // 发送间隔3毫秒,单位微秒

static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) {
    transfer *t = (transfer *)userp;
    size_t realsize = size * nmemb;    
    t->contents.append((const char*)contents, realsize);
    return realsize;
}

static void setup(struct transfer *t, int num)
{
    CURL *hnd;
    hnd = t->easy = curl_easy_init();
    curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
    curl_easy_setopt(hnd, CURLOPT_WRITEDATA, (void *)t);
    /* set the same URL */
    curl_easy_setopt(hnd, CURLOPT_URL, "https://someurl.xyz");
    /* HTTP/2 please */
    curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
    /* 调试用跳过证书校验,生产环境按需开启 */
    curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L);
    curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYHOST, 0L);
#if (CURLPIPE_MULTIPLEX > 0)
    /* 等待连接复用确认,避免新建多余连接 */
    curl_easy_setopt(hnd, CURLOPT_PIPEWAIT, 1L);
#endif
}

int main() {
    struct transfer trans[NUM_HANDLES];
    CURLM *multi_handle;
    int i;
    int still_running = 0;
    int total_transfers = 1000; // 实际要发送的总请求数,需小于等于NUM_HANDLES
    int added_count = 0; // 已添加到multi handle的请求数

    /* 初始化所有easy handle,暂不加入multi */
    for(i = 0; i < total_transfers; i++) {
        setup(&trans[i], i);
    }

    multi_handle = curl_multi_init();
    curl_multi_setopt(multi_handle, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);
    curl_multi_setopt(multi_handle, CURLMOPT_MAX_TOTAL_CONNECTIONS, 1L); // 强制只用1个连接,确保多路复用生效

    // 主循环
    do {
        // 增量添加请求批次
        if (added_count < total_transfers) {
            int add_num = std::min(BATCH_SIZE, total_transfers - added_count);
            for (int j = 0; j < add_num; j++) {
                curl_multi_add_handle(multi_handle, trans[added_count + j].easy);
            }
            added_count += add_num;
            usleep(SEND_INTERVAL_US); // 批次间插入3毫秒延迟
        }

        CURLMcode mc = curl_multi_perform(multi_handle, &still_running);
        if(still_running) {
            //  poll超时设为1毫秒,提升请求添加时机的精准度
            mc = curl_multi_poll(multi_handle, NULL, 0, 1, NULL);
        }
        if(mc) {
            break;
        }

        // 处理返回响应
        do {
            int queued;
            msg = curl_multi_info_read(multi_handle, &queued);
            if ((msg) && (msg->msg == CURLMSG_DONE) && (msg->data.result == CURLE_OK)) {
                curl_off_t dl_size;
                curl_easy_getinfo(msg->easy_handle, CURLINFO_SIZE_DOWNLOAD_T, &dl_size);
                // 按需处理响应内容,这里省略原打印逻辑
            }
        } while (msg);

    } while (still_running || added_count < total_transfers); // 所有请求发完、所有响应收完才退出

    // 资源清理
    for(i = 0; i < total_transfers; i++) {
        curl_multi_remove_handle(multi_handle, trans[i].easy);
        curl_easy_cleanup(trans[i].easy);
    }
    curl_multi_cleanup(multi_handle);
        
    return 0;
}

参数调整说明

  • 修改BATCH_SIZE可调整每批发送的请求数,配合3毫秒间隔可精准控制整体QPS
  • 修改SEND_INTERVAL_US可调整批次间隔,单位为微秒
  • 强制设置CURLMOPT_MAX_TOTAL_CONNECTIONS为1,可确保所有请求都走同一个HTTP/2连接,完全发挥多路复用的优势

内容的提问来源于stack exchange,提问作者p.luck

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 06:42:01