前段时间伤自尊了,被人三言两语给问蒙圈了,蔫头耷脑好半天愣没缓不过来,俗话说得好呀,刀不磨要生锈人不学要落后,人过三十不学艺人过四十天过午,老老实实做人踏踏实实做学问,证明自己实在不是干技术的料也好早点转行去卖鸡蛋灌饼。
开始今天话题前先澄清一个事,小蓝单车发布麒麟计划,共享单车即将进入精准广告、实时导航、运动数据和快乐骑行时代,联系二个月前《细思极恐的共享单车》的博文,朋友圈及各路粉丝一再追问我和蓝去去的关系,今天正式澄清:本人不是小蓝单车的产品经理,也没有从事相关方面工作,文章内容如有雷同纯属巧合。
回到正题,今天说说超时,做服务一定绕不开大并发、高效率、可靠稳定、快速响应这些关键词,从技术的角度上讲,每个环节每个步骤都有值得大书特书一番的地方,不过个人觉得最最重要的地方还是超时管理,这方面做好就成功一大半了。首先介绍一下应用场景,作为一个服务,对外受理接入请求,请求报文到达以后先解析,理解用户想要干什么,对于当下大多数的互联网业务,往往需要一系列逻辑处理,这边取点标签那边取点参数,汇总起来算一算得到最终结果再给用户返回回去。随着互联网的发展数据量越来越大,数据本地化早已不能满足业务需要,在逻辑处理过程中数次调用远程通信已是再平常不过的事情了,这就产生了上下游多个节点间的数据通信与传输的问题,那么多连接有tcp的有udp的,有长连接的有短连接的,有先发后回的,有后发先回的,有发了不回的,有不要还回的,各种情况都要考虑到,一个字忒乱。
怎样把超时管理起来呢,epoll非常棒但终究是事件触发的,把超时转化为事件是想要达到的目的。成千上万个连接的超时设置的有长有短,最讨厌的是这些连接还是剧烈频繁变动的,对于有序容器管理这些连接成本真的有点高,要是再加把锁实现进(线)程同步,效率有点让人焦虑,既然不能全局有序可不可以局部有序呢,只关心最值而不是顺序本身,根堆的偏序特性是个相当不错的选择,二叉树每次插值调姿开销确实有点大。根堆分大小,取决于比较函数,不是全局有序因此迭代器意义不大,元素追加弹出基本够用了,在用例中比较函数计算(入队事件 + 超时事件 - 当前事件)最小值当做堆顶。
第2行从小根堆中获取一个元素,获取成功就用套接字超时作为epoll_wait超时,否则阻塞直到有事件发生为止。
第11行判断返回值,如果大于零表明有待处理事件发生,等于零表明有超时发生。
第13行至24行对事件处理,是新接入子套接字则加入epoll监听,否则派发任务到工作线程。
第28行至32行清除epoll事件并从根堆中剔除元素,同时返回上游错误信息。
第8行读取消息(既可能是上游消息也可能是下游消息),大于零启动业务处理,否则清除epoll事件并关闭连接。
第9行识别报文格式,用例采用0x04标识示意,具体业务侧应采用更加严格的定义。
第11行调用上半场处理函数。
第15行调用下半场处理函数。
第13行至18行业务实现过程。
第21行或23行为初始化下游连接操作(tcp和udp是个人封装过的),需要注意的是,udp需要采用connect方式初始化,即将对端地址关联到套接字上,以方便read读取。
第28行和29行在发送完毕数据以后将套接字再次注册事件和追加到超时根堆中去。
第32行若tcp连接失败则向上游返回失败信息,udp只能通过超时判断。
第8行至9行清除epoll事件并从根堆中删除元素。
第11行至13行业务实现过程。
第15行至20行根据业务实现结果向上游返回不同的消息。
第6行获取当前的系统时间。
第7行入队时间加上超时时间。
第9行至11行计算当前时间与超时时间的差值。
第13行将差值转换成epoll超时的时间单位。
完整的代码片段http://git.oschina.net/gonglibin/codes/rc87dt0iagk4exqm3psuw66,作为论证和用例代码仅供参考,调试测试通过,根堆封装成对象的时候已经加入锁,epoll对象早年封装的没有考虑同步的问题,代码中通过注释已经提请留意了。
外面下雨了,前些日子的暑热一扫而光,Google I/O 2017开发者大会518开幕了,谷歌宣布kotlin作为一级编程语言,消息一出有小伙伴慌了,其实木必要,kotlin好比加了语法糖的java,跟java原本就有着血浓于水的感情,况且谷歌与oracle豪门之间的恩怨不至于革小码农们的命,编程思想比实现语言更重要,人工智能与VR是好东西,未来有前途,抽空研究研究。