并行原理分析(libcurl+epoll)

 

yar框架的并行原理分析(libcurl+epoll)

标签: 框架异步phpcepoll
  76人阅读  评论(0)  收藏  举报
 分类:

yar是一个轻量级的php rpc框架。有意思的是它的并行,其实就是libcurl作为网络库提供http请求,然后用epoll做为事件监听来实现整个异步并行调用的。在此基础上,就是如何利用zend api来对整个逻辑的封装了。我们先抛开zend api,单独看看libcurl 结合 epoll 是如何来做到异步并行调用的。 
先大致熟悉一下libcurl,官网http://curl.haxx.se/libcurl/c/libcurl.html。 
1、Easy interface 
如何发起一个curl调用?直接上码:

#include<stdio.h>
#include<curl/curl.h>
int main(int argc, char *argv[])
{
    CURL *curl;
    CURLcode res;
    curl_global_init(CURL_GLOBAL_ALL);
    curl = curl_easy_init();
    if(curl) {
       curl_easy_setopt(curl, CURLOPT_URL, "http://google.com");
       res = curl_easy_perform(curl);
       if(res != CURLE_OK) {
           fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
       }   
       curl_easy_cleanup(curl);
    }   
    curl_global_cleanup();
    return 0;
}    

大致3个步骤: 
1)、curl_easy_init :获得curl的处理句柄。 
2)、curl_easy_setopt :用来针对网络传输过程中的一些选项设置,比如访问的地址、超时、设置请求回调、数据回写变量等等,详情去查看官网。 
3)、curl_easy_perform:执行curl请求。 
到这里你可以知道,一个curl句柄就是一个请求,既然是网络请求,你肯定猜到libcurl都为每个curl句柄都维护一个socket fd。 
可以看出这种调用方式的特点是最简单,同步调用,但也效率低下。

2、Multi interface 
Multi interface是基于多个curl句柄来实现并发的,大致原理是把多个curl_easy_init 创建的curl加入一个stack里面,然后并行去执行stack里面的curl句柄,来达到并行的目的。 
对于Multi interface,一般有两种使用方法,一种是curl_multi_perform 结合select / poll 方式调用,虽然能实现异步,但终究还是基于select/poll这种模型,需主动轮询fd事件,在特定场合下效率还是没法保证。第二种是”multi_socket”方式,即利用curl_multi_socket_action来替代curl_multi_perform来执行请求,顺便可以获得请求状态的变化,以方便用事件监听的方式来掌控请求读写状态,事件监听的方式有很多成熟的库,出了名的比如libevent,libev,libuv等,咱们等会儿用epoll来说,yar用的也是epoll。 
咱们来讨论multi_socket + epoll方式,直接上码:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include <sys/epoll.h>
#include<curl/curl.h>

typedef struct _global_info {
    int epfd;
    CURLM *multi;
} global_info;

typedef struct _easy_curl_data {
    CURL *curl;
    char data[1024] = {0};
} easy_curl_data;

typedef struct _multi_curl_sockinfo {
    curl_socket_t fd;
    CURL *cp;
} multi_curl_sockinfo;

char curl_cb_data[1024] = {0};

static int sock_cb (CURL *e, curl_socket_t s, int what, void *cbp, void *sockp)
{
    struct epoll_event ev = {0};

    global_info * g = (global_info *) cbp;
    multi_curl_sockinfo  *fdp = (multi_curl_sockinfo *) sockp;

    if (what == CURL_POLL_REMOVE) {
        if (fdp) {
            free(fdp);
        }
        epoll_ctl(g->epfd, EPOLL_CTL_DEL, s, &ev);
    } else {
        if (what == CURL_POLL_IN) {
            ev.events |= EPOLLIN;
        } else if (what == CURL_POLL_OUT) {
            ev.events |= EPOLLOUT;
        } else if (what == CURL_POLL_INOUT) {
            ev.events |= EPOLLIN | EPOLLOUT;
        }

        if (!fpd) {
            fpd = (multi_curl_sockinfo *)malloc(sizeof(multi_curl_sockinfo));
            fpd->fd = s;
            fpd->cp = e;

            epoll_ctl(g->epfd, EPOLL_CTL_ADD, s, &ev);
            curl_multi_assign(g->multi, s, &ev);
        }

    }
    return 0;
}

static void set_curl_opt(CURL *curl)
{
    //set curl options..
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, curl_cb_data);
    //other options..
}

int main(int argc, char *argv[])
{
    char *urls[3] = {"https://google.com", "http://qq.com", "http://xxx.com"};

    curl_global_init(CURL_GLOBAL_ALL);
    global_info g;
    memset(&g, 0, sizeof(global_info));
    g.epfd = epoll_create(10);
    g.multi = curl_multi_init();

    int i=0;
    for(;i<3;i++) {
        CURL *curl;
        curl = curl_easy_init();
        set_curl_opt(curl);
        curl_multi_add_handle(g.multi, curl);
    }

    curl_multi_setopt(multi, CURLMOPT_SOCKETFUNCTION, sock_cb);
    curl_multi_setopt(multi->cm, CURLMOPT_SOCKETDATA, &g);

    int running_count;
    struct epoll_event events[10]
    while (CURLM_CALL_MULTI_PERFORM == curl_multi_socket_action(g.multi, CURL_SOCKET_TIMEOUT, 0, &running_count));

    if (running_count) {
        do {
            nfds = epoll_wait(g.epfd, events, 10, 500);
            if(nfds > 0) {
                int z=0;
                for (;z<nfds; z++) {
                    if (events[i].events & EPOLLIN) {
                        curl_multi_socket_action(g.multi, CURL_CSELECT_IN, events[i].data.fd, &running_count);
                    } else if (events[i].events & EPOLLOUT) {
                        curl_multi_socket_action(g.multi, CURL_CSELECT_OUT, events[i].data.fd, &running_count);
                    }
                }
            }
        } while (running_count);
    }

    curl_global_cleanup();
    return 0;
}

以上代码就是libcurl + epoll实现异步并行调用的模板,可以稍作修改就能放到你的项目中运行。 
如果你对上面的代码看完后没啥感觉,这里重点说一下: 
curl_multi_init 创建multi curl 句柄,这个句柄是来管理easy_curl_init 创建 curl句柄的,你可以理解为一个大池子,这个池子里面存放着多个curl句柄。一个multi curl代表一个池子,多个multi curl 互不影响。之后multi curl 也需要设置一些选项,通过curl_multi_setopt 函数来设置,最关键的2个分别是CURLMOPT_SOCKETFUNCTION 选项和 CURLMOPT_TIMERFUNCTION选项。这两个选项设置了sock calback 和 timer callback ,看名字你也大概猜出各自代表的意思,没错,我们知道一个curl句柄里面维护着一个socket fd ,当这个fd状态变化的时候是靠这个sock callback来通知的,而timer callback ,是libcurl用来向调用方通知超时回调的。只需要设置一下函数指针就好。具体用法看官网。 
我们知道通过CURLMOPT_SOCKETFUNCTION 选项来设置sock callback来获知sock的状态了,这点非常重要,但是对于这个fd是何时读、写,对于libcurl来说它是无能为力的。我们需要一个能够监听事件的库,这时候epoll就华丽登场了。epoll在这这里如何使用?使其就是把sock calback 通知的sock fd 状态,同步一份给epoll就行了(其实就是上面代码的sock_cb函数)。然后,剩下的就是epoll event loop了,epoll_wait 返回可用fd后,你就可以进行相关的读写操作了,读写操作用这个curl_multi_socket_action来搞定。还记得之前说的curl_easy_init 创建curl句柄吧?创建的时候是通过curl_easy_setopt 来设置选项的,其中CURLOPT_WRITEFUNCTION 和 CURLOPT_WRITEDATA 分别设置当curl执行完成后回调函数和写入变量,你可以在这个回调函数里面进行curl执行结果的处理。所以,当调用curl_multi_socket_action的时候,就会触发这2个选项的作用了。一个完整的sock_multi + epoll的异步执行调用就是这样。

看到这里,再回去看上面的代码,估计问题不大了。

OK,剩下的事情就是把这块的逻辑整合zend api ,如果你熟悉php扩展的编写,熟悉zend api,再结合上面的分析,然后去看yar源码,就畅通无阻了。想想要不要完整的把yar的源码分析写出来,但一想到一堆zend api , 想想还是算了…不过有个很好的编写PHP扩展的模板,请参照这个项目https://github.com/linkaisheng/edge,这是一个接口平台的php框架,纯C编写,里面用到了各种zend api ,包括类的创建、属性操作、函数调用、垃圾回收、返回值、对象复制等等等等,为了弄清楚这些用法,我亲身经历了N多个segfault 才搞清楚并且运行正常起来,亲测可用..你可以大胆参照上面的来。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
VS是微软公司开发的一款集成开发环境,而libcurl、zlib和openssl都是开源软件库。 libcurl是一个用于支持网页传输协议的客户端软件库。它支持多种传输协议,包括HTTP、FTP、SMTP等,可以方便地实现网络数据的传输和通信。libcurl提供了简单易用的API接口,具有高度的可移植性和灵活性,广泛应用于各种网络应用中。 zlib是一个用于数据压缩和解压缩的库。它可以将数据进行压缩,使其占用更少的存储空间,同时可以提高数据的传输效率。zlib可以与其他软件库配合使用,例如libcurl,以实现网络数据的传输和压缩。 openssl是一个开源的加密解密库。它提供了各种安全协议和算法的实现,包括SSL/TLS协议、RSA、AES等,用于保护网络通信的安全性。openssl被广泛应用于网络安全领域,用于实现加密通信、数字证书的生成和管理等功能。 在使用方面,VS主要用于开发和编译程序,可以方便地创建和管理项目,提供了丰富的开发工具和调试功能。而libcurl、zlib和openssl是用于程序开发过程中的库文件,可以被程序调用以实现特定功能。在网络应用开发中,常常需要使用libcurl来进行网络传输,同时可以结合zlib进行数据的压缩和解压缩,而openssl可以提供数据的安全加密和解密功能。 总之,VS是一个开发环境,而libcurl、zlib和openssl是开发中常用的软件库,它们在不同领域发挥着重要的作用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值