本週公司接了一個小項目,是給北京國舜科技股份有限公司作一個 HTTP 相關的小功能產品。大概實現功能是將交換機的源數據經過解析,分析出 HTTP 包配對的 request 和 response 頭,並把每對的 request 和 response 頭相關字段內容,經過TCP方式發送給對方的指定的服務器。java
須要一臺流量設備來抓取分析交換機中的數據,流量設備經過推送的方式將解析後的數據發送到咱們平臺,平臺基於數據進行分析,關於流量設備有下邊幾個需求:linux
一、流量設備ios
a.須要同時支持雙向(流出,流入)流量分析 b.需能支持千兆網峯值流量
二、雙方對接方案web
採用 TCP 長鏈接對接,也就是經過 socket 以 TCP 長鏈接方式向對方服務器發送指定格式的數據內容,對方以相應的格式進行解析便可。編程
三、發送數據包格式數組
1: typedefstruct{ 2: unsignedintmagic;// =0x4458434A,即「DXCJ」,用於同步時從新找到一個消息起始點 3: unsignedintlength;// 負載數據的長度 4: char*data;// 實際數據內容 5: } 6: // 即以前定義的按照需求文檔中2張表的順序\t分隔的請求+響應數據,長度爲length,以後即下一條消息的magic字段
注意:對於magic和length字段須要考慮網絡字節序問題,咱們設備都是小端字節序,對方要求是大端字節序,網絡字節序也是大端字節序,最後轉換爲網絡字節序傳輸便可。咱們這邊是用C語言發送數據,對方接收端程序是java語言寫的,不過這都無所謂了,底層協議都是標準的,能夠相互通訊。緩存
四、負載均衡服務器
對方接收數據端會有多個主機進行數據的接收,所以存在多主機負載均衡的問題。能夠經過設置配置文件,內容大概以這樣的形式: host:port,多個主機就存在多個列表。這個配置文件由雙方來協商,能夠由對方來配置,也存在對方後續會不定時的添加主機或修改主機的狀況。在發送數據時,使用輪詢調度(Round-Robin Scheduling)將流量分發到各個節點上。例如,如有 3 個節點,發送 4 個消息,按以下順序: 給節點1發一條消息,再給節點 2 發一條消息,再給節點 3 發一條消息,再從節點1開始繼續循環,實現簡單的負載均衡。cookie
同時須要支持失敗重試,好比 3 個節點有一個節點宕了,那麼我這邊會盡快啓動,啓動以前流量由另兩個節點處理。待失敗節點啓動後,再次 3 臺機器同時工做;經過長鏈接端口來斷定節點失敗。網絡
五、數據格式
需求的字段有不少,有一部分字段須要額外從新解析,解析完畢各個字段以後須要以指定的格式進行發送;一個請求各字段間依照上述2個表序號順序使用\t拼接爲一整行做爲一條消息發送。若是某個字段爲空,不作忽略。例如,假如3個字段:Name\tAge\tAddress 若是Age爲空,分隔符保留。則輸出:Name\t\tAddress便可,如:liming\t\tchangpingqu。
本功能實際上並不複雜,徹底能夠在 ineedle 系統基礎之上作一些簡單修改,造成數據消息,而後發送給對方便可。關於 ineedle 後續的額外功能能夠摘除,以提升整個 ineedle 系統的效率。
總共有4部分須要修改或添加
一、HTTP的請求頭和相應頭字段
由於以前ineedle解析的相關字段是根據後續分析需求而定的,比較少,並不全面;而此次對方需求中的字段有一大部分在ineedle中是沒有的,須要再額外再次進行解析。須要在ineedle系統中的相應解碼代碼中做出相應的解碼工做,解碼方式還按照原來的實現方式,只是添加些而已,而後掛到相應mbuf上,供後續sess使用。固然首要工做仍是要對這58個字段(後續又增長7個字段)進行查找和分析,找出這些字段的經典取值,以及相關做用,用於後續代碼中buf長度的判斷,最終暫時定爲(1024*8字節)大小。
二、定時刷新主機列表
這一部分能夠在ineedle系統中原來flt_main進程中的某個定時線程中作,初次能夠設置爲10秒鐘定時週期。主要定時刷新主機:端口列表是否有修改,或者判斷是否有宕機設備從新啓動。在刷新主機列表以前要判斷一下配置文件host_list的時間戳是否變化,若是沒有變化說明這段時間內用戶沒有更新主機列表,咱們在程序中就不會再更新主機列表。還有一點就是用來從新復活以前宕機的設備,原理是這樣的,若是某個設備宕機,就加入黑名單列表,就是在激活主機列表中將該主機alive標誌位置1,表示宕機,後續再也不往該設備上發送消息,直到用戶重啓該設備並定時器刷新復活。所以若是存在一個或多個設備宕機以後,用戶將設備啓動以後,須要作一個操做就是修改host_list文件的時間戳,簡單touch一下就行,若是不touch的話,再沒有修改host_list文件內容狀況下,咱們程序就不會從新刷新主機列表,也就不會再復活宕機而又重啓的設備,這個條件到時候要提醒對方必定要作這個操做,不然單單重啓宕機設備多是無效的。這個地方原來是這樣設計的,可是對方設備不只可能會存在宕機狀況,好比也多是程序掛掉等,也是不能通訊的,這種狀況發生的概率仍是比較大的,不能使用上述方式來搞,比較麻煩,每次都須要用戶來ssh登錄並操做,這樣可能自己就是一個缺陷,如今決定暫時放棄這個作法,也就是不去判斷配置文件的時間戳,每次直接去讀取配置主機列表,這樣應該不會有太多性能消耗,不會有太大影響。
三、HTTP報文的request和reponse配置向隊列寫消息
這部分不用修改太多東西,主要是將分析到的request頭和response頭的相關字段,組合成指定格式的消息併發送給咱們的緩存隊列裏邊。存在一點問題,就是原來的ineedle每次解析完request和response頭相關信息後都是掛在mbuf結構體上,好比上次解析的request頭信息掛載mbuf上,下次到response包解析時,會把上次的mbuf清空,來填寫此次response報文解析的數據,所以這時得不到完整的req和res的組合信息。所以須要將req和res信息掛到sess上,由於req和res在共同的sess上,所以能夠讀到上次的信息,並在第二次讀到正確的res包時,進行req和res數據的組合,組合完畢將數據拷貝到緩衝數組。須要注意的是一個sess中可能有多個req和res,注意後邊的req和res要把前邊的數據覆蓋掉,保證數據清零,不要讓上次數據影響到此次的數據。這裏實現的一下細節,須要在mbuf中添加一些須要額外解析的字段,並且在sess表結構中添加http_req_res_buf來存儲一對請求響應的數據內容。組合完畢,在發送到數據隊列中。
四、啓動單獨線程發送消息
另外單獨啓動一個線程來讀取緩衝隊列的消息,並按照主機列表的順序經過socket TCP方式依次發送到相應主機上,若是遇到主機宕機狀況,將其加入黑名單,並後續不發送數據給它。理論上是這樣,要考慮接收主機的負載均衡。經過定時不斷刷新,若是以前宕機程序又從新啓動,須要從新嘗試鏈接該主機並鏈接tcp並繼續向該主機發送消息。從緩存隊列讀取消息併發送消息過程,徹底相似一個生產者與消費者的模型,考慮到數據讀寫的互斥操做,讀寫線程之間要用線程的條件變量和線程鎖來實現互斥的功能。
這裏就詳細介紹一下,這幾部分的詳細設計實現細節。
一、解碼http請求和響應字段數據
這一部分沒有什麼特別須要注意的地方,嚴格參考以前的解碼方式,對比原來方案,實現代碼編寫,而後將生成解碼的值掛到mbuf結構體上。這時就須要在mbuf結構體上添加須要的字段,待後續sess彙總時要用到,其實這時候tcp鏈接已經徹底創建,sess表已經存在,徹底能夠將解析的字段值直接掛到sess表上,可是這樣作有點混亂,沒有嚴格按照以前的代碼方案,因此最好的方式仍是按照前邊的方式比較好。
二、定時刷新主機列表
定時10秒間隔不停刷新主機列表。
三、彙總req和res並向緩衝隊列寫消息
四、發送線程不斷髮送消息
。。。。。。暫時能夠參考代碼
0一、問題1 ---- MSG_NOSIGNAL
在tcp編程過程當中,測試代碼時,發現若是服務器端被關閉,則client端系統會自動向內核發送一個信號,直接把client程序給殺死。這是linux默認的行爲,這個地方咱們不須要這樣,須要在send函數後邊標誌位設置一下MSG_NOSIGNAL標記。這樣設置完畢就能夠了。
0一、問題1
當時設置的若是接收方宕機重啓後須要手動修改時間戳,這個問題須要修復一下。這個地方只要在刷新線程中將判斷時間戳函數地方註釋掉便可。
0二、問題2
socket狀態一直關閉不正常狀態,出現FIN_WAIT1?????比較多,不知道是什麼緣由,後來也沒有發現過。後續有時間查找具體緣由。
0三、問題3
雙方接收數據對接不成功,對方接收消息時,常常出現錯誤,好比接收magic和length都會有錯誤,當時也不肯定是什麼問題,或者說是雙方誰的程序有問題。後來又通過後續的連續測試,雙方檢查程序,結果也都沒有肯定哪方程序有問題?最後在C程序端發送socket上設置TCP_NODELAY標記後,對方接收數據暫時正確和正常。但這樣作只能告訴內核快速發送,不要有延時,這樣作的話,對jar端產生的影響就是接收緩存中的msg比較簡單,從而減少出現錯誤的可能性,如今這只是推斷,還不能肯定具體緣由是什麼?
0四、問題4
ineedle程序在對方接收端所有掛掉的時候會陷入睡眠,致使看門狗沒有喂狗,導致ineedle重啓。這種狀況是很不友好的,須要除去這種狀況,就在這個地方設置了一個下標誌位,每次判斷一下,若是是所有宕機的狀況,這時就不要像往常那樣進入睡眠,直接跳轉到下條數據處理流程代碼便可,直接丟棄數據,這種狀況是不可避免的。
最後在國舜作測試,作 ineedle 開機啓動,遇到了一些問題,將啓動程序命令寫入到/etc/init.d/rc.local文件中,單單寫入 /var/dz_resource/ineedle/release/ineedle 這樣是運行不了,須要在這個命令以前 source /etc/profile 文件,多是有什麼環境變量須要設置,暫時沒有找到具體什麼緣由,後續再研究。
ineedle設備HA特性尚不支持。後續待開發。
小設備發包極限:50M/S
銀灰色小設備測試ineedle性能:
最大速率達到50M/S的速率時開始丟包,算是極限速率吧。
發包數據與生成消息比例:
50M/S ----> 1.6M/S 20M/S ----> 0.65M/S
以前是用一個設備上邊跑linux_pcap程序來給待測設備進行發包,這樣對於低配置的發包設備來講,發包速率很低,最高才50M/s左右;對於較高配置(如DELL R430)速率也纔到90M/s,這樣達不到測試程序速率的效果。爲此買了一臺千兆交換機,準備用三臺設備同時往交換機的三個網口來打流量,這樣交換機鏡像口就能彙總三個設備流量到一個口上,再經過一根網線將此網口數據打到待測試設備網口上便可。三臺設備每臺50M/S,三臺總共150M/s,此時交換機鏡像口接收數據速率有且120M/S,這樣的速率基本上達到千兆網速,算是打到了千兆網卡的極限了。此工具之後能夠測試後續性能比較好的設備了。可是發包工具當時作了簡單的修改,把修改ip、cookie、等信息給去掉了,pcap拿到數據包以後直接從端口發送出去,這樣效率應該會高一些,後續有時間能夠繼續優化一下發包工具。
其中拿到新交換機以後,看說明書進行簡單配置,其實說明書並未有卵用,簡單的不能再簡單,就說明了哪些端口的指示燈表示什麼,什麼顏色表明網口的工做狀態。而後經過交換機的web界面進行了簡單配置,設置了幾個端口爲鏡像口,和監測口。在設備與交換機相連的過程當中,出現了協商速率不一致問題,明明都設置的是千兆全雙工,設備顯示的協商速率確實百兆的,後來仔細琢磨和嘗試,換了灰色的網線以後,很輕易的自動識別並協商爲1000M的網速,估計是和網線有關,估計那藍色和紅色的網線不支持千兆速率的吧。
從國舜拿到的設備回來以後開始安裝操做系統,連上顯示器,死活都不會顯示,並且不會出現bios界面,剛在gs仍是好好的,妹的,把內存條插拔了幾回,從新安裝上以後,不一會就聽到了滴滴2聲,顯示器唰唰的打印出了bios的啓動信息,原來是路上設備顛簸,內存鬆動,這種狀況不止一次出現,以前就出現過一次。這種bios都不顯示信息的,必定不是系統問題,確定是基本設備的硬件沒有檢測到,或者是檢測不到位,這樣不知足系統啓動的條件,就啓動不了,並且沒有打印信息,連bios都啓動不了,太過度了,至少也要嘀嘀嘀報警一陣子啊,變態,這也算是知識的積累,總結經驗。