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

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

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

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所示。

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

图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,则读取该结构体上的数据,然后对数据进行处理。

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

图7-2 epoll事件处理流程

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

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所示。

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

图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,则读取该结构体上的数据,然后对数据进行处理。

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

图7-2 epoll事件处理流程