当前位置: 首页 > 编程日记 > 正文

Envoy源码分析之Dispatcher

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

摘要: Dispatcher 在Envoy的代码中Dispatcher是随处可见的,可以说在Envoy中有着举足轻重的地位,一个Dispatcher就是一个EventLoop,其承担了任务队列、网络事件处理、定时器、信号处理等核心功能。

Dispatcher

在Envoy的代码中Dispatcher是随处可见的,可以说在Envoy中有着举足轻重的地位,一个Dispatcher就是一个EventLoop,其承担了任务队列、网络事件处理、定时器、信号处理等核心功能。在Envoy threading model这篇文章所提到的EventLoop(Each worker thread runs a “non-blocking” event loop)指的就是这个Dispatcher对象。这个部分的代码相对较独立,和其他模块耦合也比较少,但重要性却不言而喻。下面是与Dispatcher相关的类图,在接下来会对其中的关键概念进行介绍。

Dispatcher 和 Libevent

Dispatcher本质上就是一个EventLoop,Envoy并没有重新实现,而是复用了Libevent中的event_base,在Libevent的基础上进行了二次封装并抽象出一些事件类,比如FileEvent、SignalEvent、Timer等。Libevent是一个C库,而Envoy是C++,为了避免手动管理这些C结构的内存,Envoy通过继承unique_ptr的方式重新封装了这些libevent暴露出来的C结构。

通过CSmartPtr就可以将Libevent中的一些C数据结构的内存通过RAII机制自动管理起来,使用方式如下:

在Libevent中无论是定时器到期、收到信号、还是文件可读写等都是事件,统一使用event类型来表示,Envoy中则将event作为ImplBase的成员,然后让所有的事件类型的对象都继承ImplBase,从而实现了事件的抽象。

SignalEvent

SignalEvent的实现很简单,通过evsignal_assign来初始化事件,然后通过evsignal_add添加事件使事件成为未决状态(关于Libevent事件状态见附录)。

Timer

Timer事件暴露了两个接口一个用于关闭Timer,另外一个则用于启动Timer,需要传递一个时间来设置Timer的到期时间间隔。

创建Timer的时候会通过evtimer_assgin对event进行初始化,这个时候事件还处于未决状态而不会触发,需要通过event_add添加到Dispatcher中才能被触发。

disableTimer被调用时其内部会调用event_del来删除事件,使事件成为非未决状态,enableTimer被调用时则间接调用event_add使事件成为未决状态,这样一旦超时时间到了就会触发超时事件。

上面的代码在计算timer时间timeval的时候实现的并不优雅,应该避免使用像1000000这样的不具备可读性的数字常量,社区中有人建议可以改成如下的形式。

FileEvent

socket套接字相关的事件被封装为FileEvent,其上暴露了二个接口:activate用于主动触发事件,典型的使用场景比如: 唤醒EventLoop、Write Buffer有数据,可以主动触发下可写事件(Envoy中的典型使用场景)等;setEnabled用于设置事件类型,将事件添加到EventLoop中使其成为未决状态。

任务队列

Dispatcher的内部有一个任务队列,也会创建一个线程专们处理任务队列中的任务。通过Dispatcher的post方法可以将任务投递到任务队列中,交给Dispatcher内的线程去处理。

post方法将传递进来的callback所代表的任务,添加到post_callbacks_所代表的类型为vector<callback>的成员表变量中。如果post_callbacks_为空的话,说明背后的处理线程是处于非活动状态,这时通过post_timer_设置一个超时时间时间为0的方式来唤醒它。post_timer_在构造的时候就已经设置好对应的callback为runPostCallbacks,对应代码如下:

runPostCallbacks是一个while循环,每次都从post_callbacks_中取出一个callback所代表的任务去运行,直到post_callbacks_为空。每次运行runPostCallbacks都会确保所有的任务都执行完。显然,在runPostCallbacks被线程执行的期间如果post进来了新的任务,那么新任务直接追加到post_callbacks_尾部即可,而无需做唤醒线程这一动作。

DeferredDeletable

最后讲一下Dispatcher中比较难理解也很重要的DeferredDeletable,它是一个空接口,所有要进行延迟析构的对象都要继承自这个空接口。在Envoy的代码中像下面这样继承自DeferredDeletable的类随处可见。

那何为延迟析构呢?用在哪个场景呢?延迟析构指的是将析构的动作交由Dispatcher来完成,所以DeferredDeletable和Dispatcher密切相关。Dispatcher对象有一个vector保存了所有要延迟析构的对象。

to_delete_1_和to_delete_2_就是用来存放所有的要延迟析构的对象,这里使用两个vector存放,为什么要这样做呢?。current_to_delete_始终指向当前正要析构的对象列表,每次执行完析构后就交替指向另外一个对象列表,来回交替。

上面的代码在执行对象析构的时候先使用to_delete来指向当前正要析构的对象列表,然后将current_to_delete_指向另外一个列表,这样在添加延迟删除的对象时,就可以做到安全的把对象添加到列表中了。因为deferredDelete和clearDeferredDeleteList都是在同一个线程中运行,所以current_to_delete_是一个普通的指针,可以安全的更改指针指向另外一个,而不用担心有线程安全问题。

当有要进行延迟析构的对象时,调用deferredDelete即可,这个函数内部会通过current_to_delete_把对象放到要延迟析构的列表中,最后判断下当前要延迟析构的列表大小是否是1,如果是1表明这是第一次添加延迟析构的对象,那么就需要通过deferred_delete_timer_把背后的线程唤醒执行clearDeferredDeleteList函数。这样做的原因是避免多次唤醒,因为有一种情况是线程已经唤醒了正在执行clearDeferredDeleteList,在这个过程中又有其他的对象需要析构而加入到vector中。

到此为止deferredDelete的实现原理就基本分析完了,可以看出它的实现和任务队列的实现很类似,只不过一个是循环执行callback所代表的任务,另一个是对对象进行析构。最后我们来看一下deferredDelete的应用场景,却“为何要进行延迟析构?”在Envoy的源代码中经常会看到像下面这样的代码片段。

传递给Dispatcher的callback都是通过裸指针的方式进行回调,如果进行回调的时候对象已经析构了,就会出现野指针的问题,我相信C++水平还可以的同学都会看出这个问题,除非能在逻辑上保证Dispatcher的生命周期比所有对象都短,这样就能保证在回调的时候对象肯定不会析构,但是这不可能成立的,因为Dispatcher是EventLoop的核心。

一个线程运行一个EventLoop直到线程结束,Dispatcher对象才会析构,这意味着Dispatcher对象的生命周期是最长的。所以从逻辑上没办法保证进行回调的时候对象没有析构。可能有人会有疑问,对象在析构的时候把注册的事件取消不就可以避免野指针的问题吗? 那如果事件已经触发了,callback正在等待运行呢? 又或者callback运行了一半呢?前者libevent是可以保证的,在调用event_del的时候可以把处于等待运行的事件取消掉,但是后者就无能为力了,这个时候如果对象析构了,那行为就是未定义了。沿着这个思路想一想,是不是只要保证对象析构的时候没有callback正在运行就可以解决问题了呢?是的,只要保证所有在执行中的callback执行完了,再做对象析构就可以了。可以利用Dispatcher是顺序执行所有callback的特点,向Dispatcher中插入一个任务就是用来对象析构的,那么当这个任务执行的时候是可以保证没有其他任何callback在运行。通过这个方法就完美解决了这里遇到的野指针问题了。

或许有人又会想,这里是不是可以用shared_ptr和shared_from_this来解这个呢? 是的,这是解决多线程环境下对象析构的秘密武器,通过延长对象的生命周期,把对象的生命周期延长到和callback一样,等callback执行完再进行析构,同样可以达到效果,但是这带来了两个问题,第一就是对象生命周期被无限拉长,虽然延迟析构也拉长了生命周期,但是时间是可预期的,一旦EventLoop执行了clearDeferredDeleteList任务就会立刻被回收,而通过shared_ptr的方式其生命周期取决于callback何时运行,而callback何时运行这个是没办法保证的,比如一个等待socket的可读事件进行回调,如果对端一直不发送数据,那么callback就一直不会被运行,对象就一直无法被析构,长时间累积会导致内存使用率上涨。第二就是在使用方式上侵入性较强,需要强制使用shared_ptr的方式创建对象。

总结

Dispatcher总的来说其实现还是比较简单明了的,比较容易验证其正确性,同样功能也相对较弱,和chromium的MessageLoop、boost的asio都是相似的用途,但是功能上差得比较多。好在这是专门给Envoy设计的,而且Envoy的场景也比较单一,不必做成那么通用的。另外一个我觉得比较奇怪的是,为什么在DeferredDeletable的实现中要用to_delete_1_和to_delete_2_两个队列交替来存放,其实按照我的理解一个队列即可,因为clearDeferredDeleteList和deferredDelete是保证在同一个线程中执行的,就和Dispatcher的任务队列一样,用一个队列保存所有要执行的任务,循环的执行即可。但是Envoy中没有这样做,我理解这样设计的原因可能是因为相比于任务队列来说延迟析构的重要性更低一些,大量对象的析构如果保存在一个队列中循环的进行析构势必会影响其他关键任务的执行,所以这里拆分成两个队列,多个任务交替的执行,就好比把一个大任务拆分成了好几个小任务顺序来执行。

附录

  • Libevent状态转换图

阿里云双十一1折拼团活动:已满6人,都是最低折扣了

【满6人】1核2G云服务器99.5元一年298.5元三年 2核4G云服务器545元一年 1227元三年

【满6人】1核1G MySQL数据库 119.5元一年

【满6人】3000条国内短信包 60元每6月

参团地址:http://click.aliyun.com/m/1000020293/

原文链接

转载于:https://my.oschina.net/u/1464083/blog/2413900

相关文章:

Windows 8 快捷键大全

为什么80%的码农都做不了架构师&#xff1f;>>> win8快捷键大全&#xff1a; Windows 键 X &#xff1a;Windows快捷菜单 Windows 键 C &#xff1a;显示个性分类和时钟 Windows 键 I &#xff1a;打开“设置”个性分类 Windows 键 K &#xff1a;打开“设备”…

Activex、OLE、COM、OCX、DLL之间区别、联系

转自&#xff1a;http://baike.baidu.com/view/393671.htm 概述 .ocx是ocx控件的扩展名,与.exe .dll同属于PE文件。下面说说什么是ocx控件! OCX 是对象类别扩充组件。 如果你用过Visual Basic或者Delphi一类的可视化编程工具&#xff0c;那么对控件这个概念一定不会陌生&#…

不写一行代码,也能玩转Kaggle竞赛?

整理 | Jane 出品 | AI科技大本营&#xff08;ID&#xff1a;rgznai100&#xff09;【导读】AI科技大本营会给大家分享一些 Kaggle 上的资源&#xff0c;如 Kaggle 开放的数据集&#xff0c;也会分享一些好的竞赛方案或有意义的竞赛经验&#xff0c;帮助大家成长。今天&#xf…

认识flask框架-2

1.json:基于键值对的字符串&#xff0c;轻量级的数据交互格式&#xff0c;用来传输数据 2.json模块 dumps:把字典转化成json字符串。 loads&#xff1a;把json字符串转成字典。 dump、load操作的是文件对象。 jsonify可以返回json字符串&#xff0c;会修改响应的类型为applicat…

Sencha-概念-Layouts(布局)(官网文档翻译8)

Sencha-概念-Layouts(布局)&#xff08;官网文档翻译8&#xff09; 介绍和HBox 布局描述了在您的应用程序的组件的大小和位置。例如&#xff0c;一个电子邮件客户端可能具有固定到左边的消息的列表&#xff0c;以说&#xff0c;可用的宽度的三分之一&#xff0c;和一个消息观看…

Photoshop图像处理操作汇总

1、给图像添加外边框&#xff0c;保持图像原有大小&#xff1a; 点击图层-->新建-->图层&#xff0c;弹出新图层对话框&#xff0c;点击确定&#xff0c;点按"Ctrl A”键将图像全部选中&#xff0c;再点击编辑>描边&#xff0c;弹出描边对话框&#xff0c;在宽…

人工智能进军餐饮:AI调酒,越喝越有

作者 | 神经小姐姐来源 | 转载自HyperAI超神经&#xff08;ID:HyperAI&#xff09;导读&#xff1a;“吃”&#xff0c;现在已经成了一种文化&#xff0c;对食物的不懈追求&#xff0c;可以说是人类历史的一种推动力。从烹饪的进化&#xff0c;到现在花样百出的美食&#xff0c…

LaTeX - 带圈数字

2019独角兽企业重金招聘Python工程师标准>>> 法I. by zepinglee \documentclass[UTF8]{ctexart} \XeTeXcharclass①1 \XeTeXcharclass②1 \XeTeXcharclass③1 \XeTeXcharclass④1 \XeTeXcharclass⑤1 \XeTeXcharclass⑥1 \XeTeXcharclass⑦1 \XeTeXcharclass⑧1 \Xe…

akcms折腾记

这几天闲着无聊&#xff0c;找cms折腾一下&#xff0c;先是搞了大名鼎鼎的dedecms&#xff0c;不过那繁琐真不是盖的&#xff0c;想修改个模板不知从哪里动手。虽然现在工作的后台也是由dedecms改过来的&#xff0c;但平时也就发发文章而已。 然后就找到了这个神器&#xff1a;…

VC++ OCX 控件注册

转自&#xff1a;http://www.cctry.com/thread-5334-1-1.html 方法一&#xff1a;在dos或Windows命令行下运行&#xff1a;regsvr32 ocxname.ocx 注册 示例&#xff1a; regsvr32 netshare.ocx //注册netshare.ocx控件 regsvr32 /u netshare.ocx //解除netshare.ocx控件…

程序猿生存指南-4 借钱风波

借钱风波&#xff08;10&#xff09;临近春节&#xff0c;我的朋友圈被各个公司的年终奖刷屏。土豪互联网公司有送现金的&#xff0c;有送豪车的&#xff0c;还有送别墅的。它们享受着移动互联网的红利&#xff0c;赚得盆满钵满。不过翻看公司请媒体写的那些报道&#xff0c;就…

赛灵思:人工智能芯片发展方向与误区

演讲嘉宾 | 姚颂&#xff0c;赛灵思人工智能业务资深总监整理 | 夕颜人工智能发展过程中&#xff0c;算力是一个重要的因素&#xff0c;算力就像是 AI 的燃油&#xff0c;没有燃油&#xff0c;AI 哪也去不了。而为 AI 应用提供算力的&#xff0c;正是各种各样的芯片。而近几年&…

Python 多线程抓取网页 牛人 use raw socket implement http request great

Python 多线程抓取网页 - 糖拌咸鱼 - 博客园Python 多线程抓取网页最近&#xff0c;一直在做网络爬虫相关的东西。 看了一下开源C写的larbin爬虫&#xff0c;仔细阅读了里面的设计思想和一些关键技术的实现。1、larbin的URL去重用的很高效的bloom filter算法&#xff1b; 2、DN…

从特斯拉到英伟达,那些端到端自动驾驶研发系统有何不同?

作者 | 黄浴&#xff0c;奇点首席科学家兼总裁来源 | 转载自知乎专栏自动驾驶与视觉感知导读&#xff1a;近日&#xff0c;吴恩达的 Drive.ai 被苹果收购的消息给了自动驾驶领域一记警钟&#xff0c;但这个领域的进展和成果犹在。本文将介绍一些端到端的自动驾驶研发系统&#…

vc6中进行多行注释和反注释的方法

1、利用工具中自带的按钮实现&#xff1a; Tools-->Customize-->Add-ins and Macro Files-->将SAMPLE项选中-->Commands-->Category中选择Macros-->在Commands中将CommentOut拖到工具栏&#xff0c;再选中一个Images&#xff0c;点击OK即可。 这种方法是使…

一看就懂!【英雄联盟锐雯】与 Python 详解设计模式之门面模式

【网络配图】设计模式&#xff08;Design Pattern&#xff09;是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。使用设计模式的目的&#xff1a;为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。设计模式使代码编写真正工程化&#xff1b;设计模…

无法挂载 NTFS格式的分区:mount: unknown filesystem type ‘ntfs’

问题&#xff1a; # mount –t ntfs /dev/sdb1 /mnt/usb mount: unknown filesystem type ‘ntfs’ 这是由于CentOS release 5.3(Final)上无法识别NTFS格式的分区。解决办法&#xff1a; 通过使用 ntfs-3g 来解决。 打开ntfs-3g的下载点http://w…

vs2008 ActiveX控件测试容器的生成以及调试ActiveX控件

1、ActiveX控件测试容器的生成&#xff1a;用TSTCON生成测试容器 http://msdn.microsoft.com/zh-cn/library/f9adb5t5(vvs.90).aspx 在搜索里搜索tstcon,双击TstCon文件夹&#xff0c;将此文件夹里的东西全部复制到另外一个新的空文件里&#xff0c;然后打开解决方案 tstcon.…

百度自动驾驶新突破:获首批T4牌照,升级Apollo 5.0,将进行复杂城市场景路测...

作者 | KYLE WIGGERS等编译 | 夕颜出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;最近&#xff0c;百度自动驾驶项目终于有了进展。先是上周悄然发布了 Apollo 的最新版本 Apollo 5.0&#xff0c;引入一些新功能&#xff1b;昨天下午&#xff0c;北京市自动驾驶测试…

折叠屏就要来了,适配逼死 Android 开发?

1. 异型屏还没适配好&#xff0c;折叠屏就要来了&#xff0c;Android 的碎片化&#xff0c;让开发者又多掉了不少头发。 北京时间 11 月 8 日&#xff0c;三星在旧金山向开发者发布了一款可折叠屏幕手机。并表示&#xff0c;新技术需要开发者调整其 App&#xff0c;以确保在手机…

nginx虚拟目录配置

2019独角兽企业重金招聘Python工程师标准>>> 今天搞了N久的虚拟目录配置&#xff0c;在几乎要放弃的时侯偶然看到一篇文章&#xff0c;将我的问题搞定 原贴地址&#xff1a;http://blog.sina.com.cn/s/blog_6c2e6f1f0100l92h.html 我的需求是这样的&#xff0c;系…

阿里达摩院刷新纪录,开放域问答成绩比肩人类水平,超微软、Facebook

出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;近日&#xff0c;由阿里巴巴达摩院语言技术实验室研发的 Multi-Doc Enriched BERT 模型在微软的 MS MARCO 数 据评测任务&#xff0c;Passage Retrieval Task&#xff08;文档检索排序&#xff09;和 Q&A Task&…

解决windows7下vs2008不能正常编译ActiveX控件的问题

在windows7系统中&#xff0c;vs2008环境下用MFC ActiveX Control生成的控件不能正常编译&#xff0c;但是在xp系统中却能正常编译&#xff0c;解决方法&#xff1a; 1、在windows7中编译&#xff0c;提示错误为&#xff1a;fatal error LNK1000: Internal error during IncrB…

什么是shell【TLCL】

常用命令 datecaldf——report file system disk space usagefree——display amount of free and used memory in the systemexit——退出终端Ctrl-Alt-F1 到 Ctrl-Alt-F6 访问后台终端会话&#xff0c;AltF7返回图形桌面转载于:https://www.cnblogs.com/songdechiu/p/9943599…

Linux终端的几个常用快捷方式,记下!

一、初识linux的终端种类&#xff1a;本地、远程    查看本终端命令&#xff1a;     #tty 命令&#xff0c;看到当前所处的终端     #&#xff08;w&#xff09;who 命令&#xff0c;看到系统中所有登录的用户 其中&#xff0c;tty 终端为表示在本地命令行模式下打开…

从0到1 | 文本挖掘的传统与深度学习算法

一、什么是文本挖掘&#xff1f;讨论文本挖掘之前&#xff0c;我们要先说一下数据挖掘的概念&#xff0c;因为文本挖掘是数据挖掘的一个分支。数据挖掘&#xff08;Data Mining&#xff09;指从大量的数据中通过算法搜索隐藏在其中信息的过程。而文本挖掘就是从文本数据中获取有…

window7系统中64位安装matalbR2009b后出现乱码的解决方案

转自&#xff1a;http://blog.csdn.net/shaoguangleo/archive/2010/11/29/6042194.aspx MATLAB 中默认的字体是 Monospaced &#xff08;即等宽字体&#xff09;&#xff0c;这是一种非常适合用于显示程序源代码的字体。但Monospaced 是种逻辑字体&#xff0c;它在不同语言和操…

【电路】pmic芯片设计细节

VIO_IN供电https://e2e.ti.com/support/power-management/f/196/t/712146?tisearche2e-sitesearch&keymatchtps65916 Note that every GPIO will be configured as an input for the first 6ms after VCC is supplied, which is the time it takes for the device to init…

sql server 的一些记录

下面记录一些用到sql server查询时候用到的函数。 charindex,在一个表达式中搜索另一个表达式并返回其起始位置&#xff08;如果找到&#xff09;。http://msdn.microsoft.com/zh-cn/library/ms186323.aspx CHARINDEX ( expressionToFind ,expressionToSearch [ , start_locati…

[WPF] UserControl vs CustomControl

原文:[WPF] UserControl vs CustomControl介绍 WPF中有两种控件&#xff1a;UserControl和CustomControl&#xff0c;但是这两者有什么区别呢&#xff1f;这篇博客中将介绍两者之间的区别&#xff0c;这样可以在项目中合理的使用它们。 UserControl 将多个WPF控件(例如&#xf…