curl_multi异步高并发服务实现

1 curl_multi_poll方式实现异步curl

1.1 函数调用步骤

(1) curl_multi_init初始化一个multi handle

(2) curl_easy_init 初始化一个easy handle

(3) curl_easy_setopt 给easyhandle设置各种参数

(4) curl_multi_add_handle添加到multihandle

(5) curl_multi_perform异步执行请求,每次执行返回对列中正在运行的数量,为0时,表示执行结束,结束并不意味着所有的请求都成功了,也可能执行失败了。所以需要循环执行该函数。为了减少循环执行的CPU占用率,可以使用curl_multi_poll函数或者curl_multi_fdset配合select函数来判断是否有结果返回,通知读取数据,减少CPU占用。curl_multi_timeout可以为select提供一个合适的超时时间。

(6) curl_multi_info_read 读取返回结果消息队列中的消息,重复调用,直到消息队列为空。返回数据中有个easy handle 用来标识是哪个请求。

(7) curl_multi_remove_handle 将执行结束的easyhandle从multihandle中移除,表示multihandle不再管理此easyhand,可以销毁释放,也可以修改请求连接url和参数,重新加入,复用连接。

(8) curl_easy_cleanup 执行结束后,先清除easy handle

(9) curl_multi_cleanup 执行这个函数,清除multi handle

1.2 实现方案

(1) 实现一个服务,程序调用服务的添加任务接口addTask不断的加入任务。

(2) 创建一个线程1不断的从任务队列中取出任务,分配easy handle,给easy_hand设置url等参数。然后添加到multihandle上,去执行请求;并将easyhand和任务之间用map保存起来,表示正在进行的任务;

(3) 创建线程2不断的select或者curl_multi_poll或者curl_multi_wait或者查看multihandle的状态,看是否有数据返回,有数据返回则读取数据。curl_multi_poll和curl_multi_wait比select更好,可以解决连接上限为1024个的问题。curl_multi_poll和curl_multi_wait区别有两个,一个是curl_multi_poll在被等待的时间内,可以调用curl_multi_wakeup激活,curl_multi_poll会加速返回。而curl_multi_wait无法被激活,只能等到有事件触发,或者超时返回。另外一个区别是如果没有文件描述符可以等待,curl_multi_wait会立刻返回,而curl_multi_poll一定要等到超时时间才能返回。

(4) 读取数据会返回easyhand,用easyhand去map中查找对应的任务;然后根据不同的任务属性去处理数据,调用回调函数,将数据返回给程序。

1.3 遇到的问题

(1) 出现崩溃,可能是多线程调用libcurl接口的原因;

(2) curl_multi_add_handle添加easyhand返回失败,errocode:8,CURLM_RECURSIVE_API_CALL,错误原因是从回调内部调用API函数。没有找到解决办法,可能和多线程调用有关。设置超时时间curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30); 超时设置为0时会出现崩溃,设置成0表示不超时;并且设置不发出信号,curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);解决问题。

(3) 当使用的easyhand太多时,200个,会出现错误码CURLE_COULDNT_CONNECT ,7的错误,意识无法连接()到主机或代理。connect refused。连接数量太多,需要创建太多socket连接,而服务器端创建的连接数量有限,导致失败。

(4) 一次向multihand添加1000条任务,curl_multi_perform执行返回任务从1000降到0后,并不是所有分任务都执行完了,读取的数据也就600条左右,需要多次调用curl_multi_info_read去读取数据。


2 线程安全

仍然存在libcurl调用到openssl后产生的崩溃,经调查,openssl 1.0.2 不是线程安全的,若要启动线程安全机制,需要自定义实现 openssl 用到的全局锁(大概是41个),并提供一个锁定/释放的 callback 函数,具体可以参考 OpenSSL_1_0_2l\crypto\threads\mttest.c 里的 thread_setup/thread_cleanup/win32_locking_callback 3个函数的实现。或者改用 openssl 1.1.0


libcurl 的声明如下: curl.haxx.se/libcurl/c/


参考文章:

curl_multi异步高并发服务实现 - 一字千金 - 博客园 (cnblogs.com)

(6条消息) libcurl返回curlcode说明_努力努力学习中的博客-CSDN博客_curl_easy_recv

(6条消息) multi接口的使用_赤水无泪的博客-CSDN博客_multi接口

CURLOPT_NOSIGNAL

curlpp/example14.cpp at v0.8.1 · jpbarrette/curlpp · GitHub

发布于 2022-08-25 12:40