Mosquito的优化——epoll优化(七)

117 篇文章 6 订阅
25 篇文章 113 订阅

本文由逍遥子撰写,转发请标注原址:

http://blog.csdn.net/houjixin/article/details/46413583

http://houjixin.blog.163.com/blog/static/3562841020155835146428/#


原版的mosquito在移动互联网情况下,其性能不高,实际运营时一个mosquito实例能支持2万连接就不错了;mosquitto在网络状态不好的情况下,随着用户量的上升,其对cpu消耗将大幅增加,主要的CPU主要消耗在以下几个方面:

(1)Poll机制的缺陷;

(2)Mosquitto内部订阅树机制的缺陷;

(3)其他消息发送,数据结构管理方面的缺陷;

本节将针对这些缺陷提出相应的优化策略和方法。

7.1、poll优化

7.1.1、优化原因

        在mosquitto原始程序中,核心处理流程是对所有的socket进行监听处理,该部分功能主要使用poll来完成,但是它的效率较差,尤其当在线活动用户较少的情况下,性能更差,这一定程序上影响了mosquitto性能,poll效率的低下主要是由于下面3个原因:

       1)  poll在每次监听端口之前,都需要重新注册所有需要监听的socket;

       2)  poll返回的结果中,只会修改有事件发生的socket对应的poll结构体,因此,在使用时需要对所有注册的socket对应的poll结构体进行扫描,才能判断出那些socket有事件发生。

       3)  在内部实现上,poll需要查询所有注册的socket以确定其是否有事件发生。

Poll的上述问题是是其自身实现方式造成的,很难进行优化。针对poll的这些问题,linux实现了一个更高效的实现方式:epoll,相对而言,epoll则不需要poll这些复杂操作,epoll具有以下3个优点:

       1)  epoll中,只需要将被监听的socket注册进epoll一次,后续epoll就会监听它,而不需要每次监听之前重新注册。

       2)  epoll返回结果包含了所有有事件发生的socket,因此处理过程中,扫描所有这些有事件发生的socket即可,而不需要扫描所有注册的socket。

       3)  在内部实现上,epoll不需查询所有注册的socket,它内部是:所有有事件发生的socket自己将自己挂载epoll的就绪队列上,epoll只需返回就绪队列中的socket即可。

        因此,本次优化首先选择对poll进行优化,主要使用epoll替换poll,以提升系统的执行效率。

7.1.2、优化方案:

        Mosquitto中对poll的使用主要集中在文件loop.c的在函数mosquitto_main_loop中,因此本次修改将使用一个新的函数epoll_mosquitto_main_loop来代替它。另外,在使用方法上,epoll的监听函数epoll_wait返回所有就绪socket,而mosquitto程序中需要知道socket对应的conetxt才能完成业务处理,因此,为了支持epoll,需要增加一个hash表t_fd2context来完成socket到其对应conetxt的映射。

        Mosquito的核心处理逻辑主要在函数mosquitto_main_loop中,该函数主要完成了以下功能:

       1)  更新系统topic的信息;

       2)  将所有监听socket放入poll结构体pollfds中;

       3)  扫描所有context,完成下列工作:

               如果context有消息发送,则将消息按照mqtt协议发送出去;

                检查context是否超时;

              将context的socket放入poll的结构体pollfds中。

       4)  扫描所有conetxt,如果context中有消息,则更新消息的时间戳;

       5)  调用poll,轮询poll的结构体pollfds所有的socket;

       6)  处理poll的结果,需完成下面两个工作:

                扫描所有的context,查看其对应的socket是否有事件发生,如果有,则进行读写处理;

               处理所有的监听socket,如果有新的连接进入,则为之创建对应的context

       7)  进行重新加载配置文件等可选择操作。

       在poll的工作过程中,上述操作将会循环执行,使用epoll优化之后的mosquitto的核心流程将会有所改变,为:

       1)  向epoll中注册监听端口,该步骤在循环之前执行;下面2)之后的步骤将会放入循环执行。

       2)  更新系统topic的信息;

       3)  根据策略扫描全部context,完成下面两个工作:

               回收context,并将该context的索引放入空闲索引数组中;

              检查超时,如果超时,则将该context与其socket的映射从hash表t_fd2context中删除;

       4)  调用epoll的epoll_wait函数获取所有的就绪socket;

       5)  对所有的就绪socket进行处理,主要完成下面的工作:

             如果就绪的socket是监听接口,则对监听接口进行处理,为每个新进来的业务socket建立context,并将其注册进epoll;

             如果不是监听socket,则从socket到cotext的hash表t_fd2context中找到该socket对应的context,然后完成相应的处理,如果找不到,则断开此socket的连接。

       6)  进行重新加载配置文件等可选择操作。

由于epoll和poll的工作方式不同,因此需要对上述流程进行修改以使其能适应epoll的要求,主要修改之处包括:

       1)  Socket注册方式;poll中每次循环都需要将socket重新注册入poll,而epoll只需要开始注册一次即可;

       2)  返回结果处理;poll中需要对所有注册的socket结构体进行扫描,才能判断某个socket是否有数据处理;epoll直接返回就绪socket,因此无需全部遍历注册的socket结构体。

       3)  增加socket到对应context的映射,由于epoll直接返回就绪socket,而mosquitto中需要找到该socket对应的context才能进行上层应用的处理,因此需要增加一个hash表完成socket到context的映射。

       4)  修改消息发送部分的功能

 

7.1.3、具体实现

       Epoll的优化也采用原来的单线程结构,并使用一个大循环“while”完成对任务的处理,该大循环被放在函数epoll_mosquitto_main_loop中,该函数与函数mosquitto_main_loop(该函数是使用poll时的主要业务处理)的参数完全相同,并在函数mosquitto_main_loop中调用epoll_mosquitto_main_loop,因此程序中原来调用poll的主业务处理函数mosquitto_main_loop的地方将会被转向调用epoll的主业务处理函数epoll_mosquitto_main_loop,从而实现对epoll功能的调用,系统的流程如下图5-1所示。


图7-1 使用epoll之后的系统流程图

 

1、socket注册

       epoll在使用时只需将待监控的socket加入一次即可,这一点与poll在使用方法上有差别,在mosquitto程序中,需要epoll监控的socket包括监听socket和业务socket;Socket的注册过程由函数reg_socket完成,注册socket的监听类型为EPOLLIN,采用默认的水平触发模式。

       1)  监听socket,此类型socket负责接收客户端的新连接,例如程序中默认的1883端口对应的socket,客户端将使用该端口连接到mosquitto。在函数epoll_mosquitto_main_loop的主循环开始直接之前完成注册,只进行一次注册,后续不再重复注册;

       2)  业务socket,该类型socket负责完成客户端和mosquitto之间的业务数据的传输;业务socket将在新连接进入时完成注册,此过程在函数epoll_loop_handle_result中完成。

2、Epoll的事件处理

       Epoll事件处理将由函数epoll_loop_handle_result来完成,在该函数中将循环扫描epoll返回的所有就绪socket,并对每个就绪的socket进行处理,处理的方式为:

       1)  如果为监听socket,则首先调用mqtt3_socket_accept函数对新连接进来的socket进行处理,处理过程包括:为socket创建对应的context等;其次将socket注册入epoll。

       2)  如果监听端口为业务socket,则读取该结构体上的数据,然后对数据进行处理。


图7-2 epoll事件处理流程

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
针对mosquitto消息发送机制中需要全扫描所有context造成的低效问题,进行了一次优化。该优化增加了一个hash表,用于保存所有带消息的context。在消息发送时,只需要扫描这个hash表中的context,而不需要扫描全部的context,从而提升系统的运行效率。具体操作是将带消息的context存储在hash表中,在发送消息时只对这个hash表进行扫描,以确定哪些context有消息需要发送。这样可以减少不必要的扫描操作,提高系统的性能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [Mosquito优化——其他优化(九)](https://blog.csdn.net/hjx_1000/article/details/46413941)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [mosquitto-1.4.11-add:只增加新功能,,未做性能优化,最小程度修改原始文件;附加功能可通过配置文件打开...](https://download.csdn.net/download/weixin_42107374/16070072)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值