使用libcurl作http请求线程卡在curl_multi_perform的问题

来源:互联网 发布:开发企业软件平台 编辑:程序博客网 时间:2024/05/21 19:30
centos6.4下,程序使用了libcurl库进行http的请求,进行稳定性测试的时候发现程序卡死了,利用gstack pid发现其中一个线程的堆栈信息如下:
#0  0x0000003e420df283 in poll () from /lib64/libc.so.6
#1  0x0000003e4400c086 in __libc_res_nsend () from /lib64/libresolv.so.2
#2  0x0000003e44008811 in __libc_res_nquery () from /lib64/libresolv.so.2
#3  0x0000003e44008dd0 in __libc_res_nquerydomain () from /lib64/libresolv.so.2
#4  0x0000003e44009a91 in __libc_res_nsearch () from /lib64/libresolv.so.2
#5  0x00002b5c183107d7 in _nss_dns_gethostbyname4_r () from /lib64/libnss_dns.so.2
#6  0x0000003e420cff03 in gaih_inet () from /lib64/libc.so.6
#7  0x0000003e420d2f30 in getaddrinfo () from /lib64/libc.so.6
#8  0x00002b5c0dd3f502 in Curl_getaddrinfo_ex () from /usr/local/probe_http/probe_http.so
#9  0x00002b5c0dd3e5ad in Curl_getaddrinfo () from /usr/local/probe_http/probe_http.so
#10 0x00002b5c0dd40f66 in Curl_resolv () from /usr/local/probe_http/probe_http.so
#11 0x00002b5c0dd41105 in Curl_resolv_timeout () from /usr/local/probe_http/probe_http.so
#12 0x00002b5c0dd4c3b9 in resolve_server () from /usr/local/probe_http/probe_http.so
#13 0x00002b5c0dd521b2 in create_conn () from /usr/local/probe_http/probe_http.so
#14 0x00002b5c0dd53009 in Curl_connect () from /usr/local/probe_http/probe_http.so
#15 0x00002b5c0dd3ca00 in multi_runsingle () from /usr/local/probe_http/probe_http.so
#16 0x00002b5c0dd3d81d in curl_multi_perform () from /usr/local/probe_http/probe_http.so
#17 0x00002b5c0dd2ce57 in process_request() () from /usr/local/probe_http/probe_http.so
#18 0x00002b5c0dd2d2e8 in thread_process_fun(void*) () from /usr/local/probe_http/probe_http.so
#19 0x0000003e42807aa1 in start_thread () from /lib64/libpthread.so.0
#20 0x0000003e420e8aad in clone () from /lib64/libc.so.6

curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout_s);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, timeout_s);

CURLOPT_NOSIGNAL的解释:
When using multiple threads you should set the CURLOPT_NOSIGNAL(3)
option to 1 for all handles. Everything will or might work fine except that
timeouts are not honored during the DNS lookup- which you can work around by
building libcurl with c-ares support. c-ares is a library that provides
asynchronous name resolves.

curl easy模式的下dns resolve的超时是用alarm方式来搞的,在多线程情况下,alarm信号唤醒的线程是不确定的,因此,此处可能存在信号中断某些函数,存在死锁的风险,
因此该标记在多线程中设置是有必要的。

但如果该标记被设置时,如果dns server 迟迟未响应,即挂死状态。

上面提到 c-ares支持dns异步查询,下面就记录下libcurl如何支持c-ares

第一步,下载编译c-ares
https://c-ares.haxx.se/download/c-ares-1.10.0.tar.gz
tar zxvf c-ares-1.10.0.tar.gz
cd c-ares-1.10.0
./configure --prefix=/usr/local/c-ares --enable-shared=no
make && make install

第二步,编译curl 支持 c-ares
1.在 curl 官网http://curl.haxx.se/download.html下载最新版curl-7.51.0.tar.gz
2.解压curl-7.51.0  
    tar zxvf curl-7.51.0.tar.gz
3.cd 到url-7.51.0  目录下   
    ./configure --without-ssl --without-libssh2 --disable-ldap --disable-ldaps --without-librtmp --disable-pop3 --disable-rtsp --disable-imap --disable-gopher --prefix=/usr/local/curl --enable-ares=/usr/local/c-ares
    make && make install

把libcurl编译成静态库,使用libcurl的模块编译成动态库,由此可以直接线上升级使用

检查libcurl是否使用了异步dns,是否出现AsynchDNS字样
[root@localhost /home/jeffrey]# curl -V
curl 7.29.0 (x86_64-redhat-linux-gnu) libcurl/7.51.0 OpenSSL/1.0.1e zlib/1.2.7 c-ares/1.10.0
Protocols: dict file ftp ftps gopher http https imap imaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IPv6 Largefile NTLM NTLM_WB SSL libz

测试是否支持异步dns解析
cat test.cc
    #include <stdio.h>
    #include <curl/curl.h>
    int main()
    {
        curl_version_info_data *info=curl_version_info(CURLVERSION_NOW);
        if (info->features & CURL_VERSION_ASYNCHDNS){
            printf( "enable\n");
        } else{
            printf( "disable\n");
        }
        return 0;
    }

编译:
[root@localhost /home/jeff]# g++ -o test -g test.cc /usr/local/curl/lib/libcurl.a /usr/local/c-ares/lib/libcares.a
[root@localhost /home/jeff]# ./test
enable
0 0