libcurl:
在c++要发送http/ https请求,我相信很多人会用到libcurl的库,该库的提供了两种接口
curl_easy_perform 阻塞接口
curl_multi_perform 非阻塞接口
其中,大多数如果是客户端请求,则一般使用curl_easy_perform接口。
由一些场景,比如,有一个服务,需要接收客户端的请求,然后,统一由服务端像另一个http服务器请求数据,这时候,服务端再使用阻塞接口,则会显示捉襟见肘。
我们通常的做法是要使用libcurl的curl_multi_perform 接口,该接口由几种实现,可以通过select或者poll,但都避免不了,再异步的过程中,我们必须处理消息通信的映射关系,而且有可能你原有的设计,就是通过同步接口多线程的实现方式,这时候,是否由一种方式,不修改为异步的情况下,而不阻塞,这就需要使用协程。
libco是基于线程的,所以,最好如果你的系统使用到第三方的其他库,但是你又不想影响到其他第三方库的运行,那这时候你就需要只在你的那个线程开启协程,也就是说,只有你那个线程会取hook系统调用,这样,你的系统的其他网络库或者第三方库的IO操作,将不会受到任何影响。
libco可以在每个线程中启动,下面我写了个简单的测试程序
int main(int argc, char* argv[])
{
curl_global_init(CURL_GLOBAL_ALL);
ArrayLockFreeQueue<long> vRspList;
ArrayLockFreeQueue<long> vReqList;
std::thread first(TestThread, &vReqList, &vRspList);
int nCnt = 0;
long nReqCount = 0;
while (1)
{
long data;
if (vRspList.try_dequeue(data))
{
printf("GetData from Queue = %d %ld\n", data, GetTickMS());
}
else
{
usleep(1000);
printf("pushData to Queue = %d \n", nReqCount);
vReqList.enqueue(nReqCount++);
}
}
curl_global_cleanup();
return 0;
}
其中,定义了两个无锁队列,请求和响应,若想看无锁队列实现,在我的前面文章无锁队列有分享了相关的实现方法。
启动了线程
int EndEventLoop(void* p)
{
return -1;
}
void TestThread(ArrayLockFreeQueue<long>* pReqList, ArrayLockFreeQueue<long>* pRspList)
{
long data = 0;
while (true)
{
if (pReqList->try_dequeue(data))
{
stCoRoutine_t* consumer_routine;
co_create(&consumer_routine, nullptr, Producer, pRspList);
co_resume(consumer_routine);
}
co_eventloop(co_get_epoll_ct(), EndEventLoop, NULL);
}
}
不断的从队列拉数据,这里我用到了co_eventloop,但是我不想让它一直在里面循环,导致我取不到队列数据。所以这里腾讯的libco提供了一种返回机制,我用了
然后就是生产者的实现
void* Producer(void* pArgs)
{
co_enable_hook_sys();
CURL* curl;
CURLcode res;
curl = curl_easy_init();
if (curl) {
//curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/");
// curl_easy_setopt(curl, CURLOPT_URL, "https://www.baidu.com/");
curl_easy_setopt(curl, CURLOPT_URL, "http://192.168.88.130/");
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
long nStart = GetTickMS();
// printf("perform begin %ld\n", nStart);
res = curl_easy_perform(curl);
if (res != 0)
{
static int nErrCount = 0;
printf("curl_ret = %d %d\n", res, ++nErrCount);
}
// printf("perform end ret=%d, %ld\n", res, GetTickMS() - nStart);
curl_easy_cleanup(curl);
if (pArgs)
{
ArrayLockFreeQueue<long>* pList = (ArrayLockFreeQueue<long>*)pArgs;
static int nSend = 0;
// printf("push data to queue %ld\n", GetTickMS());
pList->enqueue(nSend++);
}
}
return NULL;
}
这里我只是简单的将一个数字返回,实际上应用的时候,我们可以返回一个结果集指针,这个都是很灵活的,没有任何限制。
这个代码只是分享大概的逻辑实现。可以做一些修改后应用到真实的环境中。