libcurl多线程使用及高并发性能测试

libcurl是一个不错的socket库,而且又是开源的。如果仅仅是简单的HTTP请求,那么只需要几行代码就能轻松实现。不过要用libcurl实现高效、高频率的HTTP请求就需要对libcurl有深入的了解才行。如果阅读英文无障碍的话,那么libcurl自带的示例程序和帮助文档就是最好的老师。

一、多线程HTTP请求

libcurl提供多线程和异步请求来实现大批量HTTP请求,可参见multithread.c和multi-app.c两个示例程序。这两种批量HTTP请求的方式在测试环境下都能正常运行,但使用异步请求总是会出现问题,于是将目标转向多线程请求。

多线程HTTP请求要注意的几个问题:

1. 千万不要在多线程之间共享同一个CURL对象

在libcurl中,第一步要做的就是使用curl_easy_int函数来初始化一个CURL对象,每个CURL对应一个HTTP连接。于是,在批量请求时为了省去每次进行HTTP连接的时间,会对多个HTTP请求使用同一个CURL对象。这在非多线程状态下是不会出问题的,但在多线程下则会出问题。具体原因未知,网上查找到的资料对此解释不太详细。

所以我们需要为每一个线程建立一个CURL对象:

void threadfunc( void *p )
{
    CURL *curl;
    
    curl = curl_easy_init();
    ...
    ...
    ...
    curl_easy_cleanup( curl );
}

2. 避免多个线程中同时调用curl_global_init函数

在多线程环境下,应在主线程中使用curl_global_init和curl_global_cleanup函数。

第一次调用 curl_easy_init()时,curl_easy_init 会调用 curl_global_init,在单线程环境下,这不是问题。但是多线程下就不行了,因为curl_global_init不是线程安全的。在多个线程中调用curl_easy_int,然后如果两个线程同时发现curl_global_init还没有被调用,同时调用 curl_global_init,悲剧就发生了。这种情况发生的概率很小,但可能性是存在的。

int main()
{
    curl_global_init( CURL_GLOBAL_ALL );

    /* 创建多线程代码 */
    ...
    ...

    curl_global_cleanup();
    return 0;
}

3. 域名解析的设定

引用:

libcurl 有个很好的特性,它甚至可以控制域名解析的超时。但是在默认情况下,它是使用alarm + siglongjmp 实现的。用alarm在多线程下做超时,本身就几乎不可能。如果只是使用alarm,并不会导致程序崩溃,但是,再加上siglongjmp,就要命了(程序崩溃的很可怕,core中几乎看不出有用信息),因为其需要一个sigjmp_buf型的全局变量,多线程修改它。(通常情况下,可以每个线程一个 sigjmp_buf 型的变量,这种情况下,多线程中使用 siglongjmp 是没有问题的,但是libcurl只有一个全局变量,所有的线程都会用)。 具体是类似 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30L) 的超时设置(发生在域名解析阶段),导致alarm的使用,如前所述,这在多线程中是有冲突的。解决方式是禁用掉alarm这种超时, curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L)。 这样,多线程中使用超时就安全了。但是域名解析就没了超时机制,碰到很慢的域名解析,也很麻烦。文档的建议是 Consider building libcurl with c-ares support to enable asynchronous DNS lookups, which enables nice timeouts for name resolves without signals. c-ares 是异步的 DNS 解决方案。 参考:http://gcoder.blogbus.com/logs/54871550.html 

4. DNS共享

参考文章:http://blog.csdn.NET/colinw/article/details/6534025

由于每个CURL对象都会连接一次服务器,如果发送1000次HTTP请求都连接到同一服务器,libcurl就会返回大量连接错误和接收错误,为此使用DNS共享是很有必要的。

void set_share_handle(CURL* curl_handle)
{
    static CURLSH* share_handle = NULL;
    if (!share_handle)
    {
        share_handle = curl_share_init();
    curl_share_setopt(share_handle, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
    }
    curl_easy_setopt(curl_handle, CURLOPT_SHARE, share_handle);
    curl_easy_setopt(curl_handle, CURLOPT_DNS_CACHE_TIMEOUT, 60 * 5);
}

void threadfunc( void *p )
{
    CURL *curl;
    
    set_share_handle( curl );
    curl = curl_easy_init();
    ...
    ...
    ...
    curl_easy_cleanup( curl );
}

二、接收gzip数据及解压缩gzip

假如一个网页有180KB大小,使用gzip算法压缩后可能就只有60KB大小。目前绝大部分网站都支持gzip,这样用户向网站请求获取的数据是gzip格式的,下载会用户电脑后再由浏览器对gzip数据进行解压,这样可以大大提高网站的浏览速度。

要让libcurl接受gzip编码很简单,只需要加入一行代码:

curl_easy_setopt(curl, CURLOPT_ENCODING, "gzip");

关键的问题是如何解压缩gzip数据,这需要用到zlib库。下面是从网上找的一个解压gzip数据的函数:

/* HTTP gzip decompress */
/* 参数1.压缩数据 2.数据长度 3.解压数据 4.解压后长度 */
int CHttp::httpgzdecompress(Byte *zdata, uLong nzdata, Byte *data, uLong *ndata)
{
    int err = 0;
    z_stream d_stream = {0}; /* decompression stream */
    static char dummy_head[2] = 
    {
        0x8 + 0x7 * 0x10,
        (((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) & 0xFF,
    };
    d_stream.zalloc = (alloc_func)0;
    d_stream.zfree = (free_func)0;
    d_stream.opaque = (voidpf)0;
    d_stream.next_in  = zdata;
    d_stream.avail_in = 0;
    d_stream.next_out = data;
    if(inflateInit2(&d_stream, 47) != Z_OK) return -1;
    while (d_stream.total_out < *ndata && d_stream.total_in < nzdata) 
    {
        d_stream.avail_in = d_stream.avail_out = 1; /* force small buffers */
        if((err = inflate(&d_stream, Z_NO_FLUSH)) == Z_STREAM_END) break;
        if(err != Z_OK )
        {
            if(err == Z_DATA_ERROR)
            {
                d_stream.next_in = (Bytef*) dummy_head;
                d_stream.avail_in = sizeof(dummy_head);
                if((err = inflate(&d_stream, Z_NO_FLUSH)) != Z_OK) 
                {
                    return -1;
                }
            }
            else return -1;
        }
    }
    if(inflateEnd(&d_stream) != Z_OK) return -1;
    *ndata = d_stream.total_out;
    return 0;
}

使用示例:

//解压缩buffer中的数据
int ndesize = 1024000;//此处长度需要足够大以容纳解压缩后数据
char *szdebuffer = new char[ndesize];
memset( szdebuffer, 0, ndesize ); 
int err; //错误变量的定义
/* 执行httpgzdecompress后,会在ndesize中保存解压后的数据长度 */
err = httpgzdecompress( ( Byte* ) szbuffer.c_str(), ( uLong ) szbuffer.size(), ( Byte* ) szdebuffer, ( uLong* ) &ndesize );

if ( err == Z_OK )
{
    /* 成功解压 */
}

参考文章:

安装zlib
http://www.linuxidc.com/Linux/2012-06/61982p2.htm

gzip的压缩与解压缩
http://www.cppblog.com/woaidongmao/archive/2011/06/05/148089.html

三、长连接高并发高性能

1 背景介绍

项目中需要用到Curl频繁调用的情况,发现curl接口调用速度缓慢。为了实现curl高性能,高并发,需要研究如何实现高性能高并发。研究方向有三个。

(1) 长连接。考虑采用长连接的方式去开发。首先研究下长连接和短连接的性能区别。curl内部是通过socket去连接通讯。socket每次连接最为耗时,如果能够复用连接,长时间连接,减少每次socket连接的时间,则可以大大减少时间,提高效率。

(2) 多线程。单个线程下载速度毕竟有限,使用多线程去调用接口。实现高并发高性能,需要考虑资源分配和冲突的问题。

(3) 异步调用。和socket异步调用的原理类似。同步调用会阻塞等待,造成CPU占用率高,电脑卡死等问题。异步调用则是数据接收完成后才会取通知调用成功,处理数据。

2 长短连接实测分析

2.1 长连接参数设置说明

Curl提供了三个参数来设置

/* 设置TCP连接为长连接 */

curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);

/* 设置长连接的休眠时间*/

curl_easy_setopt(curl, CURLOPT_TCP_KEEPIDLE, 120L);

/* 设置心跳发送时间,心使得socket长时间保活,小于KEEPIDLE时间 */

curl_easy_setopt(curl, CURLOPT_TCP_KEEPINTVL, 20L);

/* 设置连接的超时时间,大于心跳时间*/

curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30);

2.2 长短连接区别

2.2.1 短连接

短连接一般分为4步骤:初始化、设置参数、执行请求、清理资源。即使用curl_easy_setopt设置该curl为长连接,因为最后被curl_easy_cleanup(curl),所以这个socket连接会被中断销毁,不会保持长连接。具体步骤如下:

(1)CURL* curl = curl_easy_init();//创建一个curl对象

(2)curl_easy_setopt(curl,……);//可以设置多个参数url,result

(3)res = curl_easy_perform(curl);//执行请求

(4)curl_easy_cleanup(curl);//清除curl

实例代码如下:

int CHttpClient::Get(const std::string & strUrl, std::string & strResponse)

{

int res;

CURL* curl = curl_easy_init();

if (NULL == curl)

{

return CURLE_FAILED_INIT;

}

if (m_bDebug)

{

curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);

curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, OnDebug);

}

curl_easy_setopt(curl, CURLOPT_URL, strUrl.c_str());

curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);

curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, OnWriteData);

curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&strResponse);

/* enable TCP keep-alive for this transfer */

curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);

/* keep-alive idle time to 120 seconds */

curl_easy_setopt(curl, CURLOPT_TCP_KEEPIDLE, 120L);

/* interval time between keep-alive probes: 60 seconds */

curl_easy_setopt(curl, CURLOPT_TCP_KEEPINTVL, 20L);

curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30);

/**

* 当多个线程都使用超时处理的时候,同时主线程中有sleep或是wait等操作。

* 如果不设置这个选项,libcurl将会发信号打断这个wait从而导致程序退出。

*/

//curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);

curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 20);

res = curl_easy_perform(curl);

if (res != 0)

{

//FIRE_ERROR(" Get error %d", res);

}

//CurlMutiTreadMutex::GetInstance()->muti_curl_easy_cleanup(curl);

curl_easy_cleanup(curl);

return res;

}

2.2.2 长连接

长连接是我们创建了curl对象之后,不立刻使用curl_easy_cleanup清理掉,而是保存起来,下一个请求,只要重新设置url,执行请求,就可以复用以前的socket连接。

示例代码如下

头文件

CURL* GetCurl();

CURL* CreateCurl();

void PutCurl(CURL* curl);

QVector m_VectCurl;

QMutex m_mutex;

源文件

CURL* RestClientPool::GetCurl()

{

CURL* curl = NULL;

m_mutex.lock();

if (m_VectCurl.size()>0)

{

curl = m_VectCurl.front();

m_VectCurl.pop_front();

}

m_mutex.unlock();

if(curl==NULL)

{

curl = CreateCurl();

}

return curl;

}

CURL* RestClientPool::CreateCurl()

{

CURL* curl = curl_easy_init();

if (NULL == curl)

{

return NULL;

}

if (m_bDebug)

{

curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);

curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, OnDebug);

}

//curl_easy_setopt(curl, CURLOPT_URL, strUrl.c_str());

curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);

curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, OnWriteData);

//curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&strResponse);

/* enable TCP keep-alive for this transfer */

curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);

/* keep-alive idle time to 120 seconds */

curl_easy_setopt(curl, CURLOPT_TCP_KEEPIDLE, 300L);

/* interval time between keep-alive probes: 60 seconds */

curl_easy_setopt(curl, CURLOPT_TCP_KEEPINTVL, 20L);

curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30);

/**

* 当多个线程都使用超时处理的时候,同时主线程中有sleep或是wait等操作。

* 如果不设置这个选项,libcurl将会发信号打断这个wait从而导致程序退出。

*/

curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);

curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 20);

return curl;

}

void RestClientPool::PutCurl(CURL* curl)

{

m_mutex.lock();

m_VectCurl.push_back(curl);

m_mutex.unlock();

}

int RestClientPool::Get(const std::string & strUrl, std::string & strResponse)

{

int res;

//CURL* curl = CurlMutiTreadMutex::GetInstance()->muti_curl_easy_init();

CURL* curl = GetCurl();

if (NULL == curl)

{

return CURLE_FAILED_INIT;

}

curl_easy_setopt(curl, CURLOPT_URL, strUrl.c_str());

curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&strResponse);

res = curl_easy_perform(curl);

if (res != 0)

{

printf("req error %d",res);

}

PutCurl(curl);

return res;

}

2.3 长短连接测试分析

用上述的长连接和短连接进行测试,分四种情况进行测试分析。

(1) shot连接循环调用1000次url1;

(2) long连接循环调用1000次url1;

(3) long连接循环调用1000次url2;

(4) long连接循环调用1000次,每次循环中各调用一次url1和一次url2;

测试程序代码

#include

#include"RestClientPool.h"

#include "RestClient.h"

#include

#include

using namespace std;

int main(int argc, char *argv[])

{

QCoreApplication a(argc, argv);

CHttpClient m_shotclient;

RestClientPool m_longClient;

QDateTime StartTime = QDateTime::currentDateTime();

string strUrl = "http://qt.gtimg.cn/q=sz002415";

string strUrl2= "http://hq.sinajs.cn/list=sz002415";

string strResponse = "";

for (int i=0;i<1000;i++)

{

m_longClient.Get(strUrl, strResponse);

m_longClient.Get(strUrl, strResponse);

}

QDateTime timeEnd = QDateTime::currentDateTime();

int time = timeEnd.toTime_t()- StartTime.toTime_t();

printf("using time %d", time);

return a.exec();

}

2.3.1 短连接调用url1

如下图所示,短连接每次调用都会创建一个socket连接。

libcurl多线程使用及高并发性能测试_第1张图片

输出

[WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 127.0.0.1:60102[WSPConnect] Socket ip 127.0.0.1:60104线程 0x89b4 已退出,返回值为 0 (0x0)。 [WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 127.0.0.1:60107[WSPConnect] Socket ip 127.0.0.1:60109线程 0x8de8 已退出,返回值为 0 (0x0)。 [WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 127.0.0.1:60112[WSPConnect] Socket ip 127.0.0.1:60114线程 0x7d20 已退出,返回值为 0 (0x0)。 [WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 127.0.0.1:60118[WSPConnect] Socket ip 127.0.0.1:60120线程 0x7e1c 已退出,返回值为 0 (0x0)。 [WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 127.0.0.1:60124[WSPConnect] Socket ip 127.0.0.1:60126线程 0xa328 已退出,返回值为 0 (0x0)。 [WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 127.0.0.1:60129[WSPConnect] Socket ip 127.0.0.1:60132线程 0x9a68 已退出,返回值为 0 (0x0)。 [WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 127.0.0.1:60137[WSPConnect] Socket ip 127.0.0.1:60140线程 0xbd80 已退出,返回值为 0 (0x0)。 [WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 127.0.0.1:60151[WSPConnect] Socket ip 127.0.0.1:60153线程 0x7360 已退出,返回值为 0 (0x0)。 [WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 127.0.0.1:60156[WSPConnect] Socket ip 127.0.0.1:60158线程 0xbfac 已退出,返回值为 0 (0x0)。 [WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 127.0.0.1:60161[WSPConnect] Socket ip 127.0.0.1:60163线程 0xd18 已退出,返回值为 0 (0x0)。 [WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 127.0.0.1:60166[WSPConnect] Socket ip 127.0.0.1:60168线程 0x8ca8 已退出,返回值为 0 (0x0)。 [WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 127.0.0.1:60171[WSPConnect] Socket ip 127.0.0.1:60174线程 0xbc88 已退出,返回值为 0 (0x0)。 [WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 127.0.0.1:60177[WSPConnect] Socket ip 127.0.0.1:60179线程 0x90b0 已退出,返回值为 0 (0x0)。 [WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 127.0.0.1:60183[WSPConnect] Socket ip 127.0.0.1:60185线程 0x8c38 已退出,返回值为 0 (0x0)。 [WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 127.0.0.1:60189[WSPConnect] Socket ip 127.0.0.1:60191线程 0xa8d0 已退出,返回值为 0 (0x0)。 [WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 127.0.0.1:60194[WSPConnect] Socket ip 127.0.0.1:60196线程 0x76a0 已退出,返回值为 0 (0x0)。 [WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 127.0.0.1:60200[WSPConnect] Socket ip 127.0.0.1:60202线程 0x7c6c 已退出,返回值为 0 (0x0)。 [WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 127.0.0.1:60205[WSPConnect] Socket ip 127.0.0.1:60208线程 0x8618 已退出,返回值为 0 (0x0)。 [WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 127.0.0.1:60211[WSPConnect] Socket ip 127.0.0.1:60213线程 0xa300 已退出,返回值为 0 (0x0)。 [WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 127.0.0.1:60218[WSPConnect] Socket ip 127.0.0.1:60220线程 0xa3f8 已退出,返回值为 0 (0x0)。 [WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 127.0.0.1:60223[WSPConnect] Socket ip 127.0.0.1:60225线程 0xb81c 已退出,返回值为 0 (0x0)。 [WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 127.0.0.1:60228[WSPConnect] Socket ip 127.0.0.1:60230线程 0xa554 已退出,返回值为 0 (0x0)。 [WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 127.0.0.1:60233[WSPConnect] Socket ip 127.0.0.1:60235线程 0xa0f0 已退出,返回值为 0 (0x0)。

2.3.2 长连接调用url1

如下图所示,长连接调用1000次url,只创建了一个socket连接。所用的时间也大幅减少,只有27秒的时间。

libcurl多线程使用及高并发性能测试_第2张图片

输出

WSPStartup ===> D:\Project\CurlHighSpeed\Win32\Release\CurlHighSpeed.exe使用链式SPI[WSPConnect] Socket ip 127.0.0.1:60584[WSPConnect] Socket ip 127.0.0.1:60586WSPStartup ===> D:\Project\CurlHighSpeed\Win32\Release\CurlHighSpeed.exe“CurlHighSpeed.exe”(Win32): 已加载“C:\Program Files (x86)\Sangfor\SSL\ClientComponent\2_SangforNsp.dll”。模块已生成,不包含符号。 “CurlHighSpeed.exe”(Win32): 已卸载“C:\Program Files (x86)\Sangfor\SSL\ClientComponent\2_SangforNsp.dll” “CurlHighSpeed.exe”(Win32): 已加载“C:\Program Files (x86)\Sangfor\SSL\ClientComponent\2_SangforNsp.dll”。模块已生成,不包含符号。 “CurlHighSpeed.exe”(Win32): 已加载“C:\Windows\SysWOW64\dbghelp.dll”。“包括”/“排除”设置禁用了加载功能。 “CurlHighSpeed.exe”(Win32): 已加载“C:\Windows\SysWOW64\rasadhlp.dll”。“包括”/“排除”设置禁用了加载功能。 “CurlHighSpeed.exe”(Win32): 已加载“C:\Windows\SysWOW64\FWPUCLNT.DLL”。“包括”/“排除”设置禁用了加载功能。 “CurlHighSpeed.exe”(Win32): 已加载“C:\Windows\SysWOW64\bcrypt.dll”。“包括”/“排除”设置禁用了加载功能。 线程 0x9adc 已退出,返回值为 0 (0x0)。 [WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 120.204.10.232:80[WSPConnect] Socket ip 120.204.10.232:80“CurlHighSpeed.exe”(Win32): 已加载“C:\Windows\SysWOW64\uxtheme.dll”。“包括”/“排除”设置禁用了加载功能。

2.3.3 长连接调用url2

如下图所示调用不同的url2,调用1000次,用时40秒。所用的时间和url1是不同的,这个和请求的服务器以及请求的数据不一致,所以会有不同的耗时。

libcurl多线程使用及高并发性能测试_第3张图片

WSPStartup ===> D:\Project\CurlHighSpeed\Win32\Release\CurlHighSpeed.exe使用链式SPI[WSPConnect] Socket ip 127.0.0.1:58122[WSPConnect] Socket ip 127.0.0.1:58126

WSPStartup ===> D:\Project\CurlHighSpeed\Win32\Release\CurlHighSpeed.exe“CurlHighSpeed.exe”(Win32): 已加载“C:\Program Files (x86)\Sangfor\SSL\ClientComponent\2_SangforNsp.dll”。模块已生成,不包含符号。

2.3.4 长连接调用两次不同的url

如下图所示,长连接调用两个不同的url。会创建两个socket连接。不会因为切换不同的url,重新创建socket连接。对于每个url会对应一个socket连接。用时82秒,之前分别调用url1和url2所用的时间之和是27+40=67秒,多出来的15秒时间,应该是连接之间的切换时间,所以为了减少时间,可以一种url,用一个curl对象,避免切换。

libcurl多线程使用及高并发性能测试_第4张图片

WSPStartup ===> D:\Project\CurlHighSpeed\Win32\Release\CurlHighSpeed.exe使用链式SPI[WSPConnect] Socket ip 127.0.0.1:58345[WSPConnect] Socket ip 127.0.0.1:58347

2.3.5 总结分析

调用情况 用时 connect连接次数 请求次数 单次用时
Shot连接url1 147秒 1000次 1000 0.147
Long连接url1 27秒 1次 1000 0.027
Long 调用url2 40秒 1次 1000 0.04
Long url1和url2 82秒 2次 2000 0.041

综上所述可以得出结论:

(1) curl初始化,设置参数、调用url、清理cleanup,整个过程会创建一个socket连接。可以先创建,设置为长连接,不清理cleanup,重复使用该curl对象,复用已创建的curl对象和socket连接。可以提高5倍的速度。

(2) 调用不同的url,会因为服务器性能和请求数据量,耗时也会不同。

(3) 一个长连接curl调用两个不同的url(不同的网址),会创建两个socket连接。保持两个socket长连接。不会因为切换不同的url,而重复创建socket连接。切换连接会造成耗时,降低速度20%左右。所以对不同的url,可以用不用的对象和连接,避免切换。提高性能。

3. 源码下载链接

https://download.csdn.net/downl


 


原文链接:https://blog.csdn.net/markqian86/article/details/122320116

你可能感兴趣的:(c++,libcurl,c++,网络协议,http)