关于开源:C-Workflow异步调度框架-性能优化网络篇
C++ Workflow异步调度框架 - 性能优化网络篇时隔(鸽)一年半,Workflow架构系列又回来惹~尽管搁笔许久,但咱们我的项目简直每天都在更新代码! GitHub是主战场,欢送大家在github关注一手信息,这段时间的海量性能更新,都在散落在文档、issue、以及我起初的其余文章和答复中了。 过来一年的提交动静!真的!没有!偷懒! 明天整的活,是被催更最多的,Workflow中最重要的优化——网络。 通信器能够说是Workflow的前身,是我老大从自研分布式存储模块中演变进去的,并且因为老大很少看其余我的项目的做法,因而个人感觉其中有许多翻新点值得分享,如果大家看腻了千篇一律的做法,兴许这里能够和你产生一些思维的碰撞。因而本篇欢送大家探讨、交换,以及指出执笔的我写得不对的中央~ P.S. 这个系列是我在2020年7月刚开源时积攒的一些鶸鶸的思路,省得一些起初意识的开发者不太理解,这里附上一些鶸鶸的链接: C++ Workflow异步调度框架 - 根本介绍篇 C++ Workflow异步调度框架 - 架构设计篇 C++ Workflow异步调度框架 - 性能优化上篇 而后咱们从底向上,开始明天的话题——网络优化。 我的项目地址GitHub https://github.com/sogou/workflow我的项目地址Gitee https://gitee.com/sogou/workflow 一、和事件循环不一样的全新玩法乏味的新货色放第一局部说:Workflow应用epoll的形式有什么不同? 答案是线程模型。 咱们罕用epoll提供的三个接口:create、ctl、wait。连贯多了的时候,异步要做的就是用尽可能少的线程去治理fd,以节俭创立销毁线程的overhead以及线程所占用的内存和对资源的争抢。 所以高性能网络框架,都要治理着本人的多个线程(或者nginx的多过程)对epoll进行操作,并对下层提供原子性的语义。 好,咱们当初给n个网络线程去操作epoll,全局这么多fd怎么调配和治理呢? 咱们以前都见过的通用的做法是事件循环,用one loop per thread的形式进行调配和治理的。 以下我形容一下我弱弱的几点了解: 如果是server,是被动方,那么要做好accept工作如果是client,是被动方,那么要做好connect工作这些都是要从全局的角度来散发fd而后依照这n个线程以后的负载量分发给一个人,这个人来全面负责这个fd的:吃(增)喝(删)拉(改)撒(等) 而Workflow的形式不一样: 散发局部咱们先简略地对fd进行n取模,毕竟建设连贯大家也是异步做的呀,连贯的响应曾经能够交给网络线程去做了而后这个网络线程就持续做期待这个被调配的fd以及响应它的所有事件并且,敲黑板~,如果一个线程在epoll_wait,另一个线程向epoll里增加,删除或批改fd这在Workflow里都是惯例操作,因为epoll、kqueue都是反对这个特色的 所以看到这里边最大的区别是什么了吗? 事件循环是通过eventfd或者其余形式打断epoll_wait来增加fd。显然,这个做法在很多场景下其实对性能是有影响的。 如果对一个的操作有变动,Workflow怎么做呢?咱们会通过一个pipe事件告诉这个poller thread。 举个例子。如果要删除一个fd,那么如果他人把fd从epoll删除,删除之后就没有契机通知该poller thread去做它要做的事件(最典型的,比方,删掉对应的上下文或者调用钩子等)。所以要借助pipe事件来告诉“删除”这件小事儿,而这个等这个poller thread下次有正事儿要做的时候,再一并处理就完了,无需当初叫醒它就为了干点小事儿。 好奇宝宝你可能会问:fd间接取模难道不会不平均吗? 这里有个很重要的设计上的优化理念。 Workflow从来不做空跑QPS之王,Workflow做的是一个跑得又快又稳的通用企业级框架,所以贯通整个我的项目一个设计理念就是面向全局优化: 即,比起尽可能优化一个申请失去最优性能,咱们更偏向于优化整体的申请失去最优性能。 如果零碎自身很忙,那么其实连进来的大部分fd都会比拟忙碌,因而临时还不须要去做散发,取模就够用了。毕竟每个优化步骤都是有点小开销,到底优化谁,这是个十分compromise的事件。 这个优化思路前面还会继续看到~ 尽管这种线程模型的新做法,不肯定会成为Workflow高性能的最决定性因素,但却是我集体感觉最值得分享的新思路,能够让咱们这些临时还没有把底层吃透、没方法上来就翻新的入门开发者,也看看业内当初有了不一样的眼前一亮的乏味计划,也让咱们能够不要那么塌实,不要为了疾速出问题节约了本人的思考机会,而应该大胆设计,小心实现。 二、比proactor走得更远:音讯的语义设计上一部分讲的,除了封装多线程以外,网络库还要提供咱们所设计的接口。 而Workflow的另一个不同点在于,它不是网络库,而是从网络模块到下层具体协定、工作流都有的成型框架。所以提供的接口语义并不是proactor、reactor,Workflow的语义是以音讯为单位的。 为了简略起见,这里以收音讯为例: Reactor是有事件来了,我通知你,你负责去读出来;(epoll所提供的性能)Proactor是你给我一片内存,我把数据读出来了之后通知你,一次通信的音讯可能你是要读好几次能力读完的;(iocp,以及很多网络库的做法)Workflow是别管事件来了和读多少几次,我会帮你把你要的残缺音讯都收好了,再叫你;(也就是下层的每一个工作)这显然更加合乎人类的天然思维,接口的简洁和易用也是咱们对Workflow始终以来的保持。 咱们仍然从底向上,看看一个音讯长什么样: 1.pollet_message_t struct __poller_message poller_message_t;struct __poller_message{ int (*append)(const void *, size_t *, poller_message_t *); char data[0];};最底层很简略,一个钩子,以及一片内存。 ...