一种高吞吐数据流处理方法与流程

文档序号:12694533阅读:309来源:国知局
一种高吞吐数据流处理方法与流程

本发明涉及高性能网络流量处理领域,具体涉及一种高吞吐数据流处理方法。



背景技术:

在目前的网络项目中,经常会进行多层次的架构设计,如分为前端数据采集层、数据汇聚和处理层、数据存储层等,处于数据汇聚和处理层上的服务器通常需要处理几百个前端的连接,上千兆比特每秒的瞬时流量,并且还存在“浪涌”现象,即流量的峰谷值相差巨大,峰值流量过大导致服务器接收和处理数据不及时,甚至宕机,因此对服务器提出了较高的性能要求。为了保证实时、可靠的接收和处理来自前端服务器的数据流,目前已有的方案主要是对数据汇聚和处理层的服务器进行硬件扩容,利用服务器集群来保证高吞吐数据的实时处理,但是这并不能充分发挥服务器的单机处理能力,造成了资源的浪费。为了提高服务器单机的数据接收和处理能力,目前设计架构时的常见的方法有:

(1)接收线程对应客户端,每个接收线程对应唯一的接收队列,对应唯一的处理线程:

这种方法适用于每个客户端不间断发送大量数据流给汇聚服务器,缺点是单客户端对应了单一的接收线程和单一的处理线程,不能够平衡接收线程数和处理线程数的比例,当接收数据的速度大于处理数据的速度时,会造成接收队列积压数据,处理不及时;当接收队列的速度远小于处理数据的速度时,造成处理线程数过多,资源浪费;

(2)接收线程对应客户端,每个接收线程对应唯一的接收队列,使用处理线程池,加锁形式读写队列数据:

这种方法在接收数据时,单客户端对应单一的接收线程和接收队列,处理数据时,采用处理线程池,接收线程加写锁对队列写入数据,处理线程加读锁和互斥锁读出数据进行处理。这种方式的缺点是当客户端处于间歇式的发送数据时,接收线程数过多,造成资源浪费;另外频繁的加锁和解锁造成额外的开销,导致性能的降低;

(3)接收线程池,对应若干接收队列,使用处理线程池,加锁形式读写队列数据:

这种方式在接收数据时使用了线程池,客户端不再对应唯一的接收线程,并通过异步的方式快速从系统缓冲区中读取数据,避免了IO阻塞等待;处理时采用了线程池,可以配置接收线程和处理线程的比例,缺点是接收线程池和处理线程池对队列处于多读多写的方式,需要加互斥锁,造成的系统开销较大。



技术实现要素:

针对上述已有方法存在的缺点和问题,本发明公开了一种高吞吐数据流处理方法。

为了实现上述目的,本发明采用以下技术方案:

一种高吞吐数据流处理方法,具体步骤包括:

(1)读入接收层,调度层,业务层和转发层的配置参数;

(2)根据读入的配置参数,创建接收层的客户端状态维护线程、监听线程、读写事件监控线程、数据接收线程,业务层的业务线程,调度层的调度线程,转发层的发送线程;并将上述不同的线程分别绑定到不同的cpu核心上运行;

(3)如果有客户端请求连接,则通过监听线程判断接收的是否是合法的套接字,将合法的套接字加入套接字监听集合并在客户端状态表中记录该套接字的索引位置,已接收数据长度,时间戳和客户端计数器;如果套接字监听集合中有套接字状态改变,则通过读写事件监控线程判断套接字监听集合中状态发生改变的套接字是否产生读写事件,并将产生读事件的套接字放入待接收套接字队列中;如果待接收套接字队列不为空,则通过数据接收线程从待接收套接字队列中取出套接字,并将从客户端状态表中获取的该套接字的完整数据包放入数据接收队列,同时更新客户端状态表中该套接字的已接收数据长度和时间戳;客户端状态维护线程遍历客户端状态表中每个套接字的时间戳,如果套接字接收的时间戳超过预设的超时时间,则关闭该套接字,并删除该套接字在客户端状态表中的数据信息;如果数据接收队列不为空,调度线程则从数据接收队列中取出数据,放入对应的任务队列;如果任务队列不为空,业务处理线程则从自己对应的任务队列中取出任务数据进行处理,并将缓存的处理结果放入结果队列;如果结果队列不为空,发送线程采用轮询的方式从各个结果队列中取出数据发送给远端系统,在读结果队列时加读锁和互斥锁,完成后解锁。

进一步地,步骤(1)中,所述参数包括:接收层可使用的cpu核心数recv_cpu_num,接收层线程数recv_thread_num,接收队列的长度recv_q_len,监听的端口号recv_listen_port,接收数据超时时间recv_timeout;调度层可使用的cpu核心数move_cpu_num,调度层线程数move_thread_num;业务层可使用的cpu核心数bsns_cpu_num,业务层线程数bsns_thread_num,任务队列的长度bsns_task_q_len,结果队列的长度bsns_result_q_len;转发层可使用的cpu核心数send_cpu_num,转发层线程数send_thread_num,转发目的IP及端口send_dst_ip/send_dst_port,响应http报文长send_back_http_len。

进一步地,步骤(2)中,将不同的线程分别绑定到不同的cpu核心上运行包括以下几种情况:

(2.1)如果recv_cpu_num等于1,那么客户端状态维护线程、监听线程、读写事件监控线程、数据接收线程都绑定于0号核心;

(2.2)如果recv_cpu_num大于1,那么客户端状态维护线程、监听线程绑定于0号核心,读写事件监控线程、数据接收线程绑定于1~recv_cpu_num-1号核心;

(2.3)如果move_cpu_num大于等于1,那么调度线程都绑定于recv_cpu_num~recv_cpu_num+move_cpu_num-1号核心;

(2.4)如果bsns_cpu_num等于1,那么所有业务线程都绑定于recv_cpu_num+move_cpu_num核心上;

(2.5)如果bsns_cpu_num大于1,那么应急业务线程绑定于recv_cpu_num+move_cpu_num号核心,其他业务线程绑定于recv_cpu_num+move_cpu_num+1~recv_cpu_num+move_cpu_num+bsns_cpu_num-1号核心;

(2.6)如果send_cpu_num大于等于1,那么所有的发送线程都绑定于recv_cpu_num+move_cpu_num+bsns_cpu_num~recv_cpu_num+move_cpu_num+bsns_cpu_num+send_cpu_num-1号核心。

进一步地,步骤(2)中,将不同的线程分别绑定到不同的cpu核心运行的方法,包括:Linux系统下使用API:pthread_setaffinity_np(),Windows系统下使用API:SetThreadAffinityMask()。

进一步地,步骤(3)中,将合法的套接字加入套接字监听集合的同时,在客户端状态表中,将该套接字索引位置设置为1,表示有效,将该套接字已接收数据长度重置为0,时间戳设置为当前时间,客户端计数器加1。

进一步地,步骤(3)中,所述读写事件监控线程遍历套接字监听集合中每个状态发生改变的套接字,判断套接字是否产生读事件,如果是,则将该套接字在状态表中的时间戳更新为当前时间,并将该套接字放入待接收套接字队列,如果待接收套接字队列已满,则将套接字重新放入套接字监听集合,并记录待接收套接字队列满的日志信息;如果套接字产生的是写事件,则忽略该事件,将套接字重新放入套接字监听集合;如果是其他非正常事件,则关闭套接字,在客户端状态表中,将该套接字索引位置设置为0,表示失效,将客户端计数器减1。

进一步地,步骤(3)中,所述数据接收线程从客户端状态表中不断接收取出的套接字的已接收数据长度及保存的半包数据,后续接收的数据追加在它后面,每达到一个完整的数据包,则将其放入数据接收队列;如果套接字数据已经读取完,并且最后的接收的数据包不完整,则更新客户端状态表中该套接字已接收数据长度为当前接收的半包长度,并将半包数据存入客户端状态表;如果最后接收的数据包完整,则将接收数据长度置为0;最后更新套接字的时间戳为当前时间。

进一步地,步骤(3)中,在客户端状态表中将被关闭的套接字的索引位置置0,客户端计数器减1;遍历完客户端状态表后的客户端状态维护线程进入休眠(Sleep)状态。

进一步地,步骤(3)中,所述调度线程编号为i时,负责从编号为i+n*move_thread_num的数据接收队列中取出数据,放入编号为i+m*move_thread_num的任务队列;在将数据放入任务队列的过程中,如果该任务队列已满,则将m+1,尝试放入下一个相应的任务队列,如果该调度线程对应的任务队列都已满,则放入应急任务队列中;0<=n<=move_per_inq,move_per_inq表示每个队列当前的任务数量,0<=m<=bsns_thread_num/move_thread_num,每次调度成功则将n和m都加1。

进一步地,步骤(3)中,所述业务处理线程从任务队列中取出任务进行处理,直至缓存的处理结果达到阈值数量或超过某个超时时间,将缓存的处理结果一次性放入结果队列,等待被发送,在将结果放入结果队列时,给结果队列加写锁,完成后解锁。

本发明的有益效果如下:

与已公开的方法相比,具有如下优点:

1)接收线程数和处理线程数比例可根据业务需求配置平衡,充分利用系统的CPU和内存资源;

2)不同类型的线程分配在不同cpu核上运行,防止cpu操作密集型的线程干扰IO操作密集型的线程;

3)异步方式对数据接收和处理,提升系统整体吞吐率;

4)对接收的数据通过无锁的任务调度方法尽可能快的将其送入业务处理线程进行处理,减少锁开销,提高数据处理的实时性。

附图说明

图1是本发明高吞吐数据流处理原理框图。

图2显示本发明客户端状态表结构。

图3是本发明异步数据接收流程图。

图4是本发明数据调度流程图。

具体实施方式

针对高吞吐数据流的实时高效处理问题,本发明的总体思路是通过异步数据接收的方式,使用少量的接收线程应对尽可能多的客户端数据发送请求,充分利用接收线程的cpu时间;对接收的数据通过无锁的任务调度方法尽可能快的将其送入业务处理线程进行处理,保证数据处理的实时性;另外,由于数据接收是一种IO密集型操作,而数据处理是一种cpu密集型操作,将两层分离,分别运行在不同范围的cpu核上,保证数据的接收和处理过程互不干扰,不会出现数据处理线程长时间占用cpu,数据接收线程得不到系统调度运行的情况。方法的原理图如图1,下面介绍本方法的具体实施过程:

(1)读入配置参数:读入由用户设置的各项配置信息,包括接收层可使用的cpu核心数recv_cpu_num,接收层线程池大小(即接收层线程数)recv_thread_num,接收队列的长度recv_q_len,监听的端口号recv_listen_port,接收数据超时时间recv_timeout;调度层可使用的cpu核心数move_cpu_num,调度层线程数move_thread_num;业务层可使用的cpu核心数bsns_cpu_num,业务层线程数bsns_thread_num,任务队列的长度bsns_task_q_len,结果队列的长度bsns_result_q_len,其他与具体业务相关的参数;转发层可使用的cpu核心数send_cpu_num,转发层线程数send_thread_num,转发目的IP及端口send_dst_ip/send_dst_port,响应http报文长send_back_http_len;上述四层参数的设置可通过实际线上测试估计出;

(2)根据读入参数,创建接收层的客户端状态维护线程、监听线程、读写事件监控线程、数据接收线程,业务层的业务线程,调度层的调度线程,转发层的发送线程;并将上述线程绑定到特定的cpu核心上运行,隔离各层的操作,保证处理的实时性;各线程的核数分配方法如下:

(2.1)如果recv_cpu_num等于1,那么客户端状态维护线程、监听线程、读写事件监控线程、数据接收线程都绑定于0号核心;

(2.2)如果recv_cpu_num大于1,那么客户端状态维护线程、监听线程绑定于0号核心,读写事件监控线程、数据接收线程绑定于1~recv_cpu_num-1号核心,即读写事件监控线程、每个数据接收线程可以由操作系统调度运行在1~recv_cpu_num-1号任何核心上;

(2.3)如果move_cpu_num大于等于1,那么调度线程都绑定于recv_cpu_num~recv_cpu_num+move_cpu_num-1号核心,即每个调度线程可以由操作系统调度运行在recv_cpu_num~recv_cpu_num+move_cpu_num-1核心上;

(2.4)如果bsns_cpu_num等于1,那么所有业务线程都绑定于recv_cpu_num+move_cpu_num核心上;

(2.5)如果bsns_cpu_num大于1,那么应急业务线程绑定于recv_cpu_num+move_cpu_num号核心,其他业务线程绑定于recv_cpu_num+move_cpu_num+1~recv_cpu_num+move_cpu_num+bsns_cpu_num-1号核心;

(2.6)如果send_cpu_num大于等于1,那么所有的发送线程都绑定于recv_cpu_num+move_cpu_num+bsns_cpu_num~recv_cpu_num+move_cpu_num+bsns_cpu_num+send_cpu_num-1号核心;

举例:假设接收层可使用的cpu核心数recv_cpu_num为3,调度层可使用的cpu核心数move_cpu_num为2,业务处理层可使用的cpu核心数bsns_cpu_num为15,转发层可使用的cpu核心数send_cpu_num为3:

则:客户端状态维护线程、监听线程绑定于0号核心,读写事件监控线程、数据接收线程绑定于1~2号核心;调度线程都绑定于3~4号核心;应急业务线程绑定于5号核心;其他业务线程绑定于6~19号核心;所有的发送线程都绑定于20~22号核心。

绑定线程至cpu的方法,Linux下可使用API:pthread_setaffinity_np(),Windows系统下可使用API:SetThreadAffinityMask()。

(3)如果有客户端请求连接,转入步骤(4);如果套接字监听集合中有套接字状态改变,产生读写事件,转入步骤(5);如果待接收套接字队列不为空,转入步骤(6);如果客户端状态维护线程阻塞时间超过预设超时间,转入步骤(8);如果数据接收队列不为空,转入步骤(9);如果任务队列不为空,转入步骤(11);如果结果队列不为空,转入步骤(12);

(4)监听线程判断accept的是否是合法的套接字(值大于0),不合法则转入步骤(3);否则,将该套接字加入套接字监听结合,实现时采用了epoll库,调用的API为operate_fd_epoll(),参数模式为“EPOLLIN|EPOLLONESHOT”,同时在客户端状态表(图2所示)中,设置该套接字索引位置设置为1,表示有效,重置状态表中该套接字已接收数据长度为0,时间戳设置为当前时间,客户端计数器加1;转入步骤(3);

(5)读写事件监控线程遍历套接字监听集合中每个状态发生改变的套接字,判断套接字是否产生读事件,如果是,则将该套接字在状态表中的时间戳更新为当前时间,并将该套接字放入待接收套接字队列,如果队列已满,则将套接字重新放入套接字监听集合,并记录待接收套接字队列满的日志信息;如果套接字产生的是写事件,则忽略该事件,将套接字重新放入套接字监听集合;如果是其他非正常事件,则关闭套接字,在客户端状态表中,将该套接字索引位置设置为0,表示失效,将客户端计数器减1,转入步骤(3);

(6)数据接收线程从待接收套接字队列中取出套接字,从客户端状态表中该套接字的已接收数据长度及保存的半包数据,后续接收的数据追加在它后面;转入步骤(7);

(7)不断接收套接字数据,每达到一个完整的数据包,则将其放入数据接收队列;如果套接字数据已经读取完,并且最后的接收的数据包不完整,则更新客户端状态表中该套接字已接收数据长度为当前接收的半包长度,并将半包数据存入状态表;如果最后接收的数据包完整,则将接收数据长度置为0;最后更新套接字的时间戳为当前时间,返回步骤(3);

异步数据接收的流程图可以参考图3,recv_len表示当前数据包已接收的长度,recv_buffer表示当前已接收的数据,new_recv_len表示一次从套接字中接收的数据长度。

(8)客户端状态维护线程遍历状态表中每个套接字的时间戳,判断与当前时间间隔是否超过30分钟,如果超过,则判定该套接字已经意外断开,关闭该套接字,并在状态表中将该套接字的索引位置置0,客户端计数器减1;遍历完则线程进入Sleep状态,转入步骤(3);

(9)编号为i的调度线程,负责从编号为i+n*move_thread_num(0<=n<=move_per_inq)的数据接收队列中取出数据,放入编号为i+m*move_thread_num(0<=m<=bsns_thread_num/move_thread_num)的任务队列;在将数据放入任务队列的过程中,如果该任务队列已满,则将m+1,尝试放入下一个相应的任务队列,如果该调度线程对应的任务队列都已满,则放入应急任务队列中;每次调度成功则将n和m都加1;由于对数据接收队列和任务队列的操作都是一读一写模式,因此避免了队列读写时加锁的开销,调度完数据转入步骤(3);

调度方法的流程图如图4所示,举例如下:

假设数据接收队列in_q编号0~9,任务队列dst_q编号0-4,每个调度线程最多处理3个接收队列,即move_per_inq取值3,共需要4个调度线程,下面用move_thead表示调度线程:

move_thread[0]负责将in_q[0]、in_q[4]、in_q[8]中的数据拷贝至dst_q[0]、dst_q[4],每次都是一次性拷贝尽量多的数据,如in_q[0]中目前有10个数据包,dst_q[0]中目前空余容量为5,则一次性从in_q中取出5个元素拷贝至dst_q[0];如果在拷贝过程中,dst_q[0]以满,则拷贝至dst[1],dst[1]也满,则将数据拷贝至应急队列中;每次拷贝完成,则拷贝下一个源队列in_q至下一个目的队列dst_q中;

move_thread[1]负责将in_q[1]、in_q[5]、in_q[9]中的数据拷贝至dst_q[1]中;

move_thread[2]负责将in_q[2]、in_q[6]中的数据拷贝至dst_q[2]中;

move_thread[3]负责将in_q[3]、in_q[7]中的数据拷贝至dst_q[3]中。

(10)业务处理线程取出自己对应任务队列中的任务数据进行处理,并缓存处理结果;转入步骤(11);

(11)如果任务队列不为空,从任务队列中取出任务进行处理,直至缓存的结果达到阈值数量(4000)或超过某个超时时间(30s),将缓存的结果一次性放入结果队列,等待被发送,在将结果放入队列时,给队列加写锁,完成后解锁;如果任务队列为空,则表示没有待处理的任务,转入步骤(3)进入等待状态;

(12)发送线程采用轮询的方式从各个结果队列中取出数据发送给远端系统,在读结果队列时加读锁和互斥锁,完成后解锁。

当前第1页1 2 3 
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1