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

2023-11-11

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;
}

如果你对上面的代码看完后没啥感觉,这里重点说一下:
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 才搞清楚并且运行正常起来,亲测可用..你可以大胆参照上面的来。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

yar框架的并行原理分析(libcurl+epoll) 的相关文章

  • 如何尝试/捕获所有异常

    我正在完成由其他人启动的 UWP 应用程序 该应用程序经常崩溃 我总是陷入困境应用程序 at if global System Diagnostics Debugger IsAttached global System Diagnostic
  • 获取尚未实例化的类的函数句柄

    我对 C 相当陌生 我想做的事情可能看起来很复杂 首先 我想获取一些函数的句柄以便稍后执行它们 我知道我可以通过以下方式实现这一目标 List
  • 无法解析远程名称 - webclient

    我面临这个错误 The remote name could not be resolved russgates85 001 site1 smarterasp net 当我请求使用 Web 客户端读取 html 内容时 出现错误 下面是我的代
  • TcpClient 在异步读取期间断开连接

    我有几个关于完成 tcp 连接的问题 客户端使用 Tcp 连接到我的服务器 在接受客户端后listener BeginAcceptTcpClient ConnectionEstabilishedCallback null 我开始阅读netw
  • 两种类型的回发事件

    1 我发现了两篇文章 每篇文章对两种类型的回发事件的分类都略有不同 一位资源说两种类型的回发事件是Changed事件 其中控件实现 IPostbackDataHandler 当数据在回发之间更改时触发 然后Raised事件 其中控件实现 I
  • 为什么 clang 使用 -O0 生成低效的 asm(对于这个简单的浮点和)?

    我正在 llvm clang Apple LLVM 版本 8 0 0 clang 800 0 42 1 上反汇编此代码 int main float a 0 151234 float b 0 2 float c a b printf f c
  • 预处理后解析 C++ 源文件

    我正在尝试分析c 使用我定制的解析器的文件 写在c 在开始解析之前 我想摆脱所有 define 我希望源文件在预处理后可以编译 所以最好的方法是运行C Preprocessor在文件上 cpp myfile cpp temp cpp or
  • OpenCV 2.4.3 中的阴影去除

    我正在使用 OpenCV 2 4 3 最新版本 使用内置的视频流检测前景GMG http docs opencv org modules gpu doc video html highlight gmg gpu 3a 3aGMG GPU算法
  • 为什么具有相同名称但不同签名的多个继承函数不会被视为重载函数?

    以下代码片段在编译期间产生 对 foo 的调用不明确 错误 我想知道是否有任何方法可以解决此问题而不完全限定对 foo 的调用 include
  • 默认析构函数做了多少事情

    C 类中的默认析构函数是否会自动删除代码中未显式分配的成员 例如 class C public C int arr 100 int main void C myC new C delete myC return 0 删除 myC 会自动释放
  • 分配器感知容器和propagate_on_container_swap

    The std allocator traits模板定义了一些常量 例如propagate on container copy move assign让其他容器知道它们是否应该在复制或移动操作期间复制第二个容器的分配器 我们还有propag
  • C++11 动态线程池

    最近 我一直在尝试寻找一个用于线程并发任务的库 理想情况下 是一个在线程上调用函数的简单接口 任何时候都有 n 个线程 有些线程比其他线程完成得更快 并且到达的时间不同 首先我尝试了 Rx 它在 C 中非常棒 我还研究了 Blocks 和
  • 从远程托管上的 PHP 获取 PHP 错误日志

    是否有 PHP 函数或其他方式以字符串形式获取 PHP 错误日志 我需要这个 因为我无法访问在其他人的服务器上运行的站点的错误日志 他提出通过电子邮件将错误日志发送给我 但这不太方便 有什么方法可以将错误日志输出到 PHP 页面吗 我意识到
  • 将字符串分解为标记,保持引用的子字符串完整

    我不知道我在哪里看到它 但是谁能告诉我如何使用 php 和 regex 来完成这个任务 this is a string that has quoted text inside 我希望能够像这样爆炸它 0 this 1 is 2 a 3 s
  • PHP 启动:无法加载动态库 php5.4.3/ext/php_ffmpeg.dll 不是有效的 Win32 应用程序

    再会 我尝试在 Windows 7 计算机上安装 dll 文件 php ffmpeg 但不断收到此错误 29 Jan 2013 11 37 00 UTC PHP Warning PHP Startup Unable to load dyna
  • C++、三元运算符、std::cout

    如何使用 C 用三元运算符编写以下条件 int condition1 condition2 condition3 int double result int or double std cout lt lt condition1 resul
  • 如何在PHP中获取div中的所有链接

    我想从另一个网站打开一个页面 并提取一个中的所有链接 href div of class layout 2 2 在此页面中 我如何使用 PHP 来做到这一点 我想复制layout 2 2中的每个链接this https url 网页 这是我
  • node-mongodb-native的插入性能

    我正在使用 MongoDB 测试 Node js 的性能 我知道其中每一个都很好 彼此独立 但我正在尝试一些测试来感受它们 我遇到了这个问题 但无法确定来源 问题 我正在尝试在单个 Node js 程序中插入 1 000 000 条记录 它
  • 如何使用 PHP 查找字符串中字符的序列模式?

    假设我有随机的文本块 EAMoAAQAABwEBAAAAAAAAAAAAAAABAgMFBgcIBAkBAQABBQEBAAAAAAAAAAAAAAAGAgMEBQcBCBAAAQMDAgMEBQcIBQgGCwEAAQACAxEEBSEG
  • 在 Xamarin 中获取 OutOfMemoryException

    java lang OutOfMemoryError 考虑增加 JavaMaximumHeapSize Java 执行时内存不足 java exe 我的 Visualstudio Xamarin 项目出现内存不足异常 请帮助我如何解决此问题

随机推荐

  • 数字化时代,如何从战略设计到架构来打造智慧银行?

    导语 随着人工智能 大数据 云计算等技术向纵深发展 数字化转型已成为银行发展的 必答题 调整战略规划和架构重组 加大信息科技投入 推进科技人才队伍建设 持续推出数字化产品 近年来 深化数字化转型 以科技赋能金融服务已成为不少银行的发力点 今
  • python环境变量配置

    python现在的版本 主要是python2和python3两个大版本 这两个版本有很大的不同 当我们在自己电脑上同时安装了python2 x和python3 x版本的解释器的时候 就需要对环境变量的配置进行一定的修改 大概解释一下 我对环
  • 什么是 前端&后端,始端&末端

    前端 后端 前端和后端是指不同的开发领域和技术 前端指的是用户可见的界面 比如网页 移动应用 游戏等 使用的技术包括HTML CSS JavaScript等 后端指的是用户看不见的部分 比如服务器 数据库 业务逻辑等 使用的技术包括Java
  • 数据结构笔记--图

    数据结构笔记 图 图 本章总结 图的概念 基本术语 抽象数据类型 图的存储结构 邻接矩阵表示法 数组 无向图 有向图 有权图 邻接矩阵的优缺点 代码实现 邻接表表示法 链式 结构 无向图 有向图 邻接表的优缺点 邻接矩阵与邻接表的对比 代码
  • 详解spring的IOC控制反转和DI依赖注入

    转载 详解spring的IOC控制反转和DI依赖注入 2018 06 05 15 45 34 jiuqijack 阅读数 2945 文章标签 spring IOC控制反转 DI依赖注入 更多 分类专栏 spring
  • 关于Android的不同分辨率图片适配

    看了几篇相关的博客 根据自己的实际开发 总结了一下 首先要搞清楚 图片的分辨率单位是像素 也就是px 比如72x72的图片 就是长宽都是72px 手机屏幕的分辨率跟图片类似 但是它还有个很重要的指标 dpi 叫做像素密度 代表1英寸长度的屏
  • 【C++】C++11 STL算法(三):分隔操作(Partitioning operations)、排序操作(Sorting operations)

    目录 分隔操作 Partitioning operations 一 is partitioned 1 原型 2 说明 3 官网demo 二 partition 1 原型 2 说明 3 官方demo 三 partition copy 1 原型
  • Spring MVC静态资源映射

    Spring MVC静态资源映射 静态资源映射 使用容器的默认Servlet location mapping cache period order Spring MVC需要对RESTful风格的URL提供支持 而真正的RESTful风格的
  • C++11中std::thread线程实现暂停(挂起)功能

    一 封装Thread类 我们基于C 11中与平台无关的线程类std thread 封装Thread类 并提供start stop pause resume 线程控制方法 为了让线程在暂停期间 处于休眠 不消耗CPU 我们使用C 11提供的锁
  • Amazon Gamelift游戏托管服务

    Amazon GameLift是亚马逊云科技推出的一种用于托管专用游戏服务器的托管服务 它可以安全地预置实例 在正在运行的实例上部署游戏服务器 在游戏服务器队列间对流量进行负载均衡 监控实例和游戏服务器的运行状况 并在无需人工干预的情况下替
  • jaffe 数据库百度网盘下载

    供学术研究讨论使用 禁止商用 如有引用请注明jaffe论文出处 链接 https pan baidu com s 1Rc18GnVqP7WRmayFAhrtYA 提取码 2jq8
  • 前端常用的几种加密方法

    目前在前端开发中基本都会用到加密 最常见的就是登录密码的加密 接下来会为大家介绍几种加密方法 md5 加密 MD5 加密后的位数有两种 16 位与 32 位 默认使用32位 16 位实际上是从 32 位字符串中取中间的第 9 位到第 24
  • 《C和C++安全编码》读书笔记(一)

    第一章 夹缝求生 1 1 衡量危险 生产不安全软件系统的风险可以从历史风险和潜在的未来风险两方面进行评估 威胁的来源 黑客 内部人员 罪犯 竞争情报从业者 恐怖分子 信息战士 CERT CC 美国计算机紧急事件响应小组协调中心 监控漏洞信息
  • 图解通信原理与案例分析-21:4G LTE多天线技术--天线端口、码流、分集Diveristy、波束赋形BF、空分复用MIMO、空分多址

    目录 前言 第1章 MIMO多天线技术概述 1 1 三大目的 1 2 六大分类 第2章 单天线SISO 单输入单输出 2 1 概述 2 2 实现原理 多路 异频 发送 接收 对 第3章 接收分集MISO 多输入单输出 冗余接收 3 1 概述
  • 受够了初级排序算法,今天来个效率高的——归并排序。

    受够了初级排序算法 今天来个效率高的 归并排序 前情回顾 在前几篇文章中我们学习了选择排序 插入排序 以及插入排序的优化版希尔排序 但是他们的时间复杂度都是O N 2 现在我们终于迎来了我们算法效率大幅度提升的 时间复杂度为O NlogN
  • 【vue】vue组件之间相互调用问题处理

    背景 最近再使用抽屉组件的时候 遇到一个棘手的问题 就是抽屉是座位详情功能使用的 这就会带来一个问题 可能在 用户详情抽屉 中需要调用 订单详情抽屉 而 订单详情抽屉 也可能调用 用户详情抽屉 简单来说过就是A组件调用B组件 B组件又调用A
  • 私有化部署即时通讯平台,完美替代飞书和钉钉的SaaS系统

    在当今快速发展的数字化时代 企业对于安全 灵活 可定制的即时通讯平台需求不断增长 作为一家领先的品牌 WorkPlus专注于提供私有化部署的即时通讯平台 完美替代飞书和钉钉的SaaS系统 本文将重点介绍WorkPlus如何通过创新的解决方案
  • iOS开发中的敏捷方法

    敏捷开发是现在比较流行的软件开发方法 因为方法决定效率 好的方法可以大大地提高开发效率 什么是敏捷 Agile 敏捷源于2001年美国犹他州雪鸟滑雪圣地的一次聚会 聚会是敏捷方法发起者和实践者的聚会 他们经过两天的讨论 通一份简明扼要的 敏
  • ExtJS Sencha CMD 打包异常报错分析处理 - 2

    今天使用 sencha app build testing 进行编译 然后运行的时候 出现错误 app js dc 1666874035180 81578 Uncaught TypeError t 1 is not a function a
  • yar框架的并行原理分析(libcurl+epoll)

    yar是一个轻量级的php rpc框架 有意思的是它的并行 其实就是libcurl作为网络库提供http请求 然后用epoll做为事件监听来实现整个异步并行调用的 在此基础上 就是如何利用zend api来对整个逻辑的封装了 我们先抛开ze