本發(fā)明涉及高性能網(wǎng)絡(luò)流量處理領(lǐng)域,具體涉及一種高吞吐數(shù)據(jù)流處理方法。
背景技術(shù):
在目前的網(wǎng)絡(luò)項目中,經(jīng)常會進行多層次的架構(gòu)設(shè)計,如分為前端數(shù)據(jù)采集層、數(shù)據(jù)匯聚和處理層、數(shù)據(jù)存儲層等,處于數(shù)據(jù)匯聚和處理層上的服務(wù)器通常需要處理幾百個前端的連接,上千兆比特每秒的瞬時流量,并且還存在“浪涌”現(xiàn)象,即流量的峰谷值相差巨大,峰值流量過大導(dǎo)致服務(wù)器接收和處理數(shù)據(jù)不及時,甚至宕機,因此對服務(wù)器提出了較高的性能要求。為了保證實時、可靠的接收和處理來自前端服務(wù)器的數(shù)據(jù)流,目前已有的方案主要是對數(shù)據(jù)匯聚和處理層的服務(wù)器進行硬件擴容,利用服務(wù)器集群來保證高吞吐數(shù)據(jù)的實時處理,但是這并不能充分發(fā)揮服務(wù)器的單機處理能力,造成了資源的浪費。為了提高服務(wù)器單機的數(shù)據(jù)接收和處理能力,目前設(shè)計架構(gòu)時的常見的方法有:
(1)接收線程對應(yīng)客戶端,每個接收線程對應(yīng)唯一的接收隊列,對應(yīng)唯一的處理線程:
這種方法適用于每個客戶端不間斷發(fā)送大量數(shù)據(jù)流給匯聚服務(wù)器,缺點是單客戶端對應(yīng)了單一的接收線程和單一的處理線程,不能夠平衡接收線程數(shù)和處理線程數(shù)的比例,當接收數(shù)據(jù)的速度大于處理數(shù)據(jù)的速度時,會造成接收隊列積壓數(shù)據(jù),處理不及時;當接收隊列的速度遠小于處理數(shù)據(jù)的速度時,造成處理線程數(shù)過多,資源浪費;
(2)接收線程對應(yīng)客戶端,每個接收線程對應(yīng)唯一的接收隊列,使用處理線程池,加鎖形式讀寫隊列數(shù)據(jù):
這種方法在接收數(shù)據(jù)時,單客戶端對應(yīng)單一的接收線程和接收隊列,處理數(shù)據(jù)時,采用處理線程池,接收線程加寫鎖對隊列寫入數(shù)據(jù),處理線程加讀鎖和互斥鎖讀出數(shù)據(jù)進行處理。這種方式的缺點是當客戶端處于間歇式的發(fā)送數(shù)據(jù)時,接收線程數(shù)過多,造成資源浪費;另外頻繁的加鎖和解鎖造成額外的開銷,導(dǎo)致性能的降低;
(3)接收線程池,對應(yīng)若干接收隊列,使用處理線程池,加鎖形式讀寫隊列數(shù)據(jù):
這種方式在接收數(shù)據(jù)時使用了線程池,客戶端不再對應(yīng)唯一的接收線程,并通過異步的方式快速從系統(tǒng)緩沖區(qū)中讀取數(shù)據(jù),避免了IO阻塞等待;處理時采用了線程池,可以配置接收線程和處理線程的比例,缺點是接收線程池和處理線程池對隊列處于多讀多寫的方式,需要加互斥鎖,造成的系統(tǒng)開銷較大。
技術(shù)實現(xiàn)要素:
針對上述已有方法存在的缺點和問題,本發(fā)明公開了一種高吞吐數(shù)據(jù)流處理方法。
為了實現(xiàn)上述目的,本發(fā)明采用以下技術(shù)方案:
一種高吞吐數(shù)據(jù)流處理方法,具體步驟包括:
(1)讀入接收層,調(diào)度層,業(yè)務(wù)層和轉(zhuǎn)發(fā)層的配置參數(shù);
(2)根據(jù)讀入的配置參數(shù),創(chuàng)建接收層的客戶端狀態(tài)維護線程、監(jiān)聽線程、讀寫事件監(jiān)控線程、數(shù)據(jù)接收線程,業(yè)務(wù)層的業(yè)務(wù)線程,調(diào)度層的調(diào)度線程,轉(zhuǎn)發(fā)層的發(fā)送線程;并將上述不同的線程分別綁定到不同的cpu核心上運行;
(3)如果有客戶端請求連接,則通過監(jiān)聽線程判斷接收的是否是合法的套接字,將合法的套接字加入套接字監(jiān)聽集合并在客戶端狀態(tài)表中記錄該套接字的索引位置,已接收數(shù)據(jù)長度,時間戳和客戶端計數(shù)器;如果套接字監(jiān)聽集合中有套接字狀態(tài)改變,則通過讀寫事件監(jiān)控線程判斷套接字監(jiān)聽集合中狀態(tài)發(fā)生改變的套接字是否產(chǎn)生讀寫事件,并將產(chǎn)生讀事件的套接字放入待接收套接字隊列中;如果待接收套接字隊列不為空,則通過數(shù)據(jù)接收線程從待接收套接字隊列中取出套接字,并將從客戶端狀態(tài)表中獲取的該套接字的完整數(shù)據(jù)包放入數(shù)據(jù)接收隊列,同時更新客戶端狀態(tài)表中該套接字的已接收數(shù)據(jù)長度和時間戳;客戶端狀態(tài)維護線程遍歷客戶端狀態(tài)表中每個套接字的時間戳,如果套接字接收的時間戳超過預(yù)設(shè)的超時時間,則關(guān)閉該套接字,并刪除該套接字在客戶端狀態(tài)表中的數(shù)據(jù)信息;如果數(shù)據(jù)接收隊列不為空,調(diào)度線程則從數(shù)據(jù)接收隊列中取出數(shù)據(jù),放入對應(yīng)的任務(wù)隊列;如果任務(wù)隊列不為空,業(yè)務(wù)處理線程則從自己對應(yīng)的任務(wù)隊列中取出任務(wù)數(shù)據(jù)進行處理,并將緩存的處理結(jié)果放入結(jié)果隊列;如果結(jié)果隊列不為空,發(fā)送線程采用輪詢的方式從各個結(jié)果隊列中取出數(shù)據(jù)發(fā)送給遠端系統(tǒng),在讀結(jié)果隊列時加讀鎖和互斥鎖,完成后解鎖。
進一步地,步驟(1)中,所述參數(shù)包括:接收層可使用的cpu核心數(shù)recv_cpu_num,接收層線程數(shù)recv_thread_num,接收隊列的長度recv_q_len,監(jiān)聽的端口號recv_listen_port,接收數(shù)據(jù)超時時間recv_timeout;調(diào)度層可使用的cpu核心數(shù)move_cpu_num,調(diào)度層線程數(shù)move_thread_num;業(yè)務(wù)層可使用的cpu核心數(shù)bsns_cpu_num,業(yè)務(wù)層線程數(shù)bsns_thread_num,任務(wù)隊列的長度bsns_task_q_len,結(jié)果隊列的長度bsns_result_q_len;轉(zhuǎn)發(fā)層可使用的cpu核心數(shù)send_cpu_num,轉(zhuǎn)發(fā)層線程數(shù)send_thread_num,轉(zhuǎn)發(fā)目的IP及端口send_dst_ip/send_dst_port,響應(yīng)http報文長send_back_http_len。
進一步地,步驟(2)中,將不同的線程分別綁定到不同的cpu核心上運行包括以下幾種情況:
(2.1)如果recv_cpu_num等于1,那么客戶端狀態(tài)維護線程、監(jiān)聽線程、讀寫事件監(jiān)控線程、數(shù)據(jù)接收線程都綁定于0號核心;
(2.2)如果recv_cpu_num大于1,那么客戶端狀態(tài)維護線程、監(jiān)聽線程綁定于0號核心,讀寫事件監(jiān)控線程、數(shù)據(jù)接收線程綁定于1~recv_cpu_num-1號核心;
(2.3)如果move_cpu_num大于等于1,那么調(diào)度線程都綁定于recv_cpu_num~recv_cpu_num+move_cpu_num-1號核心;
(2.4)如果bsns_cpu_num等于1,那么所有業(yè)務(wù)線程都綁定于recv_cpu_num+move_cpu_num核心上;
(2.5)如果bsns_cpu_num大于1,那么應(yīng)急業(yè)務(wù)線程綁定于recv_cpu_num+move_cpu_num號核心,其他業(yè)務(wù)線程綁定于recv_cpu_num+move_cpu_num+1~recv_cpu_num+move_cpu_num+bsns_cpu_num-1號核心;
(2.6)如果send_cpu_num大于等于1,那么所有的發(fā)送線程都綁定于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系統(tǒng)下使用API:pthread_setaffinity_np(),Windows系統(tǒng)下使用API:SetThreadAffinityMask()。
進一步地,步驟(3)中,將合法的套接字加入套接字監(jiān)聽集合的同時,在客戶端狀態(tài)表中,將該套接字索引位置設(shè)置為1,表示有效,將該套接字已接收數(shù)據(jù)長度重置為0,時間戳設(shè)置為當前時間,客戶端計數(shù)器加1。
進一步地,步驟(3)中,所述讀寫事件監(jiān)控線程遍歷套接字監(jiān)聽集合中每個狀態(tài)發(fā)生改變的套接字,判斷套接字是否產(chǎn)生讀事件,如果是,則將該套接字在狀態(tài)表中的時間戳更新為當前時間,并將該套接字放入待接收套接字隊列,如果待接收套接字隊列已滿,則將套接字重新放入套接字監(jiān)聽集合,并記錄待接收套接字隊列滿的日志信息;如果套接字產(chǎn)生的是寫事件,則忽略該事件,將套接字重新放入套接字監(jiān)聽集合;如果是其他非正常事件,則關(guān)閉套接字,在客戶端狀態(tài)表中,將該套接字索引位置設(shè)置為0,表示失效,將客戶端計數(shù)器減1。
進一步地,步驟(3)中,所述數(shù)據(jù)接收線程從客戶端狀態(tài)表中不斷接收取出的套接字的已接收數(shù)據(jù)長度及保存的半包數(shù)據(jù),后續(xù)接收的數(shù)據(jù)追加在它后面,每達到一個完整的數(shù)據(jù)包,則將其放入數(shù)據(jù)接收隊列;如果套接字數(shù)據(jù)已經(jīng)讀取完,并且最后的接收的數(shù)據(jù)包不完整,則更新客戶端狀態(tài)表中該套接字已接收數(shù)據(jù)長度為當前接收的半包長度,并將半包數(shù)據(jù)存入客戶端狀態(tài)表;如果最后接收的數(shù)據(jù)包完整,則將接收數(shù)據(jù)長度置為0;最后更新套接字的時間戳為當前時間。
進一步地,步驟(3)中,在客戶端狀態(tài)表中將被關(guān)閉的套接字的索引位置置0,客戶端計數(shù)器減1;遍歷完客戶端狀態(tài)表后的客戶端狀態(tài)維護線程進入休眠(Sleep)狀態(tài)。
進一步地,步驟(3)中,所述調(diào)度線程編號為i時,負責(zé)從編號為i+n*move_thread_num的數(shù)據(jù)接收隊列中取出數(shù)據(jù),放入編號為i+m*move_thread_num的任務(wù)隊列;在將數(shù)據(jù)放入任務(wù)隊列的過程中,如果該任務(wù)隊列已滿,則將m+1,嘗試放入下一個相應(yīng)的任務(wù)隊列,如果該調(diào)度線程對應(yīng)的任務(wù)隊列都已滿,則放入應(yīng)急任務(wù)隊列中;0<=n<=move_per_inq,move_per_inq表示每個隊列當前的任務(wù)數(shù)量,0<=m<=bsns_thread_num/move_thread_num,每次調(diào)度成功則將n和m都加1。
進一步地,步驟(3)中,所述業(yè)務(wù)處理線程從任務(wù)隊列中取出任務(wù)進行處理,直至緩存的處理結(jié)果達到閾值數(shù)量或超過某個超時時間,將緩存的處理結(jié)果一次性放入結(jié)果隊列,等待被發(fā)送,在將結(jié)果放入結(jié)果隊列時,給結(jié)果隊列加寫鎖,完成后解鎖。
本發(fā)明的有益效果如下:
與已公開的方法相比,具有如下優(yōu)點:
1)接收線程數(shù)和處理線程數(shù)比例可根據(jù)業(yè)務(wù)需求配置平衡,充分利用系統(tǒng)的CPU和內(nèi)存資源;
2)不同類型的線程分配在不同cpu核上運行,防止cpu操作密集型的線程干擾IO操作密集型的線程;
3)異步方式對數(shù)據(jù)接收和處理,提升系統(tǒng)整體吞吐率;
4)對接收的數(shù)據(jù)通過無鎖的任務(wù)調(diào)度方法盡可能快的將其送入業(yè)務(wù)處理線程進行處理,減少鎖開銷,提高數(shù)據(jù)處理的實時性。
附圖說明
圖1是本發(fā)明高吞吐數(shù)據(jù)流處理原理框圖。
圖2顯示本發(fā)明客戶端狀態(tài)表結(jié)構(gòu)。
圖3是本發(fā)明異步數(shù)據(jù)接收流程圖。
圖4是本發(fā)明數(shù)據(jù)調(diào)度流程圖。
具體實施方式
針對高吞吐數(shù)據(jù)流的實時高效處理問題,本發(fā)明的總體思路是通過異步數(shù)據(jù)接收的方式,使用少量的接收線程應(yīng)對盡可能多的客戶端數(shù)據(jù)發(fā)送請求,充分利用接收線程的cpu時間;對接收的數(shù)據(jù)通過無鎖的任務(wù)調(diào)度方法盡可能快的將其送入業(yè)務(wù)處理線程進行處理,保證數(shù)據(jù)處理的實時性;另外,由于數(shù)據(jù)接收是一種IO密集型操作,而數(shù)據(jù)處理是一種cpu密集型操作,將兩層分離,分別運行在不同范圍的cpu核上,保證數(shù)據(jù)的接收和處理過程互不干擾,不會出現(xiàn)數(shù)據(jù)處理線程長時間占用cpu,數(shù)據(jù)接收線程得不到系統(tǒng)調(diào)度運行的情況。方法的原理圖如圖1,下面介紹本方法的具體實施過程:
(1)讀入配置參數(shù):讀入由用戶設(shè)置的各項配置信息,包括接收層可使用的cpu核心數(shù)recv_cpu_num,接收層線程池大小(即接收層線程數(shù))recv_thread_num,接收隊列的長度recv_q_len,監(jiān)聽的端口號recv_listen_port,接收數(shù)據(jù)超時時間recv_timeout;調(diào)度層可使用的cpu核心數(shù)move_cpu_num,調(diào)度層線程數(shù)move_thread_num;業(yè)務(wù)層可使用的cpu核心數(shù)bsns_cpu_num,業(yè)務(wù)層線程數(shù)bsns_thread_num,任務(wù)隊列的長度bsns_task_q_len,結(jié)果隊列的長度bsns_result_q_len,其他與具體業(yè)務(wù)相關(guān)的參數(shù);轉(zhuǎn)發(fā)層可使用的cpu核心數(shù)send_cpu_num,轉(zhuǎn)發(fā)層線程數(shù)send_thread_num,轉(zhuǎn)發(fā)目的IP及端口send_dst_ip/send_dst_port,響應(yīng)http報文長send_back_http_len;上述四層參數(shù)的設(shè)置可通過實際線上測試估計出;
(2)根據(jù)讀入?yún)?shù),創(chuàng)建接收層的客戶端狀態(tài)維護線程、監(jiān)聽線程、讀寫事件監(jiān)控線程、數(shù)據(jù)接收線程,業(yè)務(wù)層的業(yè)務(wù)線程,調(diào)度層的調(diào)度線程,轉(zhuǎn)發(fā)層的發(fā)送線程;并將上述線程綁定到特定的cpu核心上運行,隔離各層的操作,保證處理的實時性;各線程的核數(shù)分配方法如下:
(2.1)如果recv_cpu_num等于1,那么客戶端狀態(tài)維護線程、監(jiān)聽線程、讀寫事件監(jiān)控線程、數(shù)據(jù)接收線程都綁定于0號核心;
(2.2)如果recv_cpu_num大于1,那么客戶端狀態(tài)維護線程、監(jiān)聽線程綁定于0號核心,讀寫事件監(jiān)控線程、數(shù)據(jù)接收線程綁定于1~recv_cpu_num-1號核心,即讀寫事件監(jiān)控線程、每個數(shù)據(jù)接收線程可以由操作系統(tǒng)調(diào)度運行在1~recv_cpu_num-1號任何核心上;
(2.3)如果move_cpu_num大于等于1,那么調(diào)度線程都綁定于recv_cpu_num~recv_cpu_num+move_cpu_num-1號核心,即每個調(diào)度線程可以由操作系統(tǒng)調(diào)度運行在recv_cpu_num~recv_cpu_num+move_cpu_num-1核心上;
(2.4)如果bsns_cpu_num等于1,那么所有業(yè)務(wù)線程都綁定于recv_cpu_num+move_cpu_num核心上;
(2.5)如果bsns_cpu_num大于1,那么應(yīng)急業(yè)務(wù)線程綁定于recv_cpu_num+move_cpu_num號核心,其他業(yè)務(wù)線程綁定于recv_cpu_num+move_cpu_num+1~recv_cpu_num+move_cpu_num+bsns_cpu_num-1號核心;
(2.6)如果send_cpu_num大于等于1,那么所有的發(fā)送線程都綁定于recv_cpu_num+move_cpu_num+bsns_cpu_num~recv_cpu_num+move_cpu_num+bsns_cpu_num+send_cpu_num-1號核心;
舉例:假設(shè)接收層可使用的cpu核心數(shù)recv_cpu_num為3,調(diào)度層可使用的cpu核心數(shù)move_cpu_num為2,業(yè)務(wù)處理層可使用的cpu核心數(shù)bsns_cpu_num為15,轉(zhuǎn)發(fā)層可使用的cpu核心數(shù)send_cpu_num為3:
則:客戶端狀態(tài)維護線程、監(jiān)聽線程綁定于0號核心,讀寫事件監(jiān)控線程、數(shù)據(jù)接收線程綁定于1~2號核心;調(diào)度線程都綁定于3~4號核心;應(yīng)急業(yè)務(wù)線程綁定于5號核心;其他業(yè)務(wù)線程綁定于6~19號核心;所有的發(fā)送線程都綁定于20~22號核心。
綁定線程至cpu的方法,Linux下可使用API:pthread_setaffinity_np(),Windows系統(tǒng)下可使用API:SetThreadAffinityMask()。
(3)如果有客戶端請求連接,轉(zhuǎn)入步驟(4);如果套接字監(jiān)聽集合中有套接字狀態(tài)改變,產(chǎn)生讀寫事件,轉(zhuǎn)入步驟(5);如果待接收套接字隊列不為空,轉(zhuǎn)入步驟(6);如果客戶端狀態(tài)維護線程阻塞時間超過預(yù)設(shè)超時間,轉(zhuǎn)入步驟(8);如果數(shù)據(jù)接收隊列不為空,轉(zhuǎn)入步驟(9);如果任務(wù)隊列不為空,轉(zhuǎn)入步驟(11);如果結(jié)果隊列不為空,轉(zhuǎn)入步驟(12);
(4)監(jiān)聽線程判斷accept的是否是合法的套接字(值大于0),不合法則轉(zhuǎn)入步驟(3);否則,將該套接字加入套接字監(jiān)聽結(jié)合,實現(xiàn)時采用了epoll庫,調(diào)用的API為operate_fd_epoll(),參數(shù)模式為“EPOLLIN|EPOLLONESHOT”,同時在客戶端狀態(tài)表(圖2所示)中,設(shè)置該套接字索引位置設(shè)置為1,表示有效,重置狀態(tài)表中該套接字已接收數(shù)據(jù)長度為0,時間戳設(shè)置為當前時間,客戶端計數(shù)器加1;轉(zhuǎn)入步驟(3);
(5)讀寫事件監(jiān)控線程遍歷套接字監(jiān)聽集合中每個狀態(tài)發(fā)生改變的套接字,判斷套接字是否產(chǎn)生讀事件,如果是,則將該套接字在狀態(tài)表中的時間戳更新為當前時間,并將該套接字放入待接收套接字隊列,如果隊列已滿,則將套接字重新放入套接字監(jiān)聽集合,并記錄待接收套接字隊列滿的日志信息;如果套接字產(chǎn)生的是寫事件,則忽略該事件,將套接字重新放入套接字監(jiān)聽集合;如果是其他非正常事件,則關(guān)閉套接字,在客戶端狀態(tài)表中,將該套接字索引位置設(shè)置為0,表示失效,將客戶端計數(shù)器減1,轉(zhuǎn)入步驟(3);
(6)數(shù)據(jù)接收線程從待接收套接字隊列中取出套接字,從客戶端狀態(tài)表中該套接字的已接收數(shù)據(jù)長度及保存的半包數(shù)據(jù),后續(xù)接收的數(shù)據(jù)追加在它后面;轉(zhuǎn)入步驟(7);
(7)不斷接收套接字數(shù)據(jù),每達到一個完整的數(shù)據(jù)包,則將其放入數(shù)據(jù)接收隊列;如果套接字數(shù)據(jù)已經(jīng)讀取完,并且最后的接收的數(shù)據(jù)包不完整,則更新客戶端狀態(tài)表中該套接字已接收數(shù)據(jù)長度為當前接收的半包長度,并將半包數(shù)據(jù)存入狀態(tài)表;如果最后接收的數(shù)據(jù)包完整,則將接收數(shù)據(jù)長度置為0;最后更新套接字的時間戳為當前時間,返回步驟(3);
異步數(shù)據(jù)接收的流程圖可以參考圖3,recv_len表示當前數(shù)據(jù)包已接收的長度,recv_buffer表示當前已接收的數(shù)據(jù),new_recv_len表示一次從套接字中接收的數(shù)據(jù)長度。
(8)客戶端狀態(tài)維護線程遍歷狀態(tài)表中每個套接字的時間戳,判斷與當前時間間隔是否超過30分鐘,如果超過,則判定該套接字已經(jīng)意外斷開,關(guān)閉該套接字,并在狀態(tài)表中將該套接字的索引位置置0,客戶端計數(shù)器減1;遍歷完則線程進入Sleep狀態(tài),轉(zhuǎn)入步驟(3);
(9)編號為i的調(diào)度線程,負責(zé)從編號為i+n*move_thread_num(0<=n<=move_per_inq)的數(shù)據(jù)接收隊列中取出數(shù)據(jù),放入編號為i+m*move_thread_num(0<=m<=bsns_thread_num/move_thread_num)的任務(wù)隊列;在將數(shù)據(jù)放入任務(wù)隊列的過程中,如果該任務(wù)隊列已滿,則將m+1,嘗試放入下一個相應(yīng)的任務(wù)隊列,如果該調(diào)度線程對應(yīng)的任務(wù)隊列都已滿,則放入應(yīng)急任務(wù)隊列中;每次調(diào)度成功則將n和m都加1;由于對數(shù)據(jù)接收隊列和任務(wù)隊列的操作都是一讀一寫模式,因此避免了隊列讀寫時加鎖的開銷,調(diào)度完數(shù)據(jù)轉(zhuǎn)入步驟(3);
調(diào)度方法的流程圖如圖4所示,舉例如下:
假設(shè)數(shù)據(jù)接收隊列in_q編號0~9,任務(wù)隊列dst_q編號0-4,每個調(diào)度線程最多處理3個接收隊列,即move_per_inq取值3,共需要4個調(diào)度線程,下面用move_thead表示調(diào)度線程:
move_thread[0]負責(zé)將in_q[0]、in_q[4]、in_q[8]中的數(shù)據(jù)拷貝至dst_q[0]、dst_q[4],每次都是一次性拷貝盡量多的數(shù)據(jù),如in_q[0]中目前有10個數(shù)據(jù)包,dst_q[0]中目前空余容量為5,則一次性從in_q中取出5個元素拷貝至dst_q[0];如果在拷貝過程中,dst_q[0]以滿,則拷貝至dst[1],dst[1]也滿,則將數(shù)據(jù)拷貝至應(yīng)急隊列中;每次拷貝完成,則拷貝下一個源隊列in_q至下一個目的隊列dst_q中;
move_thread[1]負責(zé)將in_q[1]、in_q[5]、in_q[9]中的數(shù)據(jù)拷貝至dst_q[1]中;
move_thread[2]負責(zé)將in_q[2]、in_q[6]中的數(shù)據(jù)拷貝至dst_q[2]中;
move_thread[3]負責(zé)將in_q[3]、in_q[7]中的數(shù)據(jù)拷貝至dst_q[3]中。
(10)業(yè)務(wù)處理線程取出自己對應(yīng)任務(wù)隊列中的任務(wù)數(shù)據(jù)進行處理,并緩存處理結(jié)果;轉(zhuǎn)入步驟(11);
(11)如果任務(wù)隊列不為空,從任務(wù)隊列中取出任務(wù)進行處理,直至緩存的結(jié)果達到閾值數(shù)量(4000)或超過某個超時時間(30s),將緩存的結(jié)果一次性放入結(jié)果隊列,等待被發(fā)送,在將結(jié)果放入隊列時,給隊列加寫鎖,完成后解鎖;如果任務(wù)隊列為空,則表示沒有待處理的任務(wù),轉(zhuǎn)入步驟(3)進入等待狀態(tài);
(12)發(fā)送線程采用輪詢的方式從各個結(jié)果隊列中取出數(shù)據(jù)發(fā)送給遠端系統(tǒng),在讀結(jié)果隊列時加讀鎖和互斥鎖,完成后解鎖。