Fastsocket學習筆記之小結篇

jopen 10年前發布 | 45K 次閱讀 網絡工具包 Fastsocket

原文  http://www.blogjava.net/yongboy/archive/2015/02/05/422760.html

前言

前面啰啰嗦嗦的幾篇文字,各個方面介紹了Fastsocket,盲人摸象一般,能力有限,還得繼續深入學習不是。這不,到了該小結的時候了。

Fastsocket架構

Fastsocket學習筆記之小結篇

Fastsocket架構圖可以很清晰說明其大致結構,內核態和用戶態通過ioctl函數傳輸。記得netmap在重寫網卡驅動里面通過ioctl函數直接透傳到用戶態中,其更為高效,但沒有完整的TCP/IP網絡堆棧支持嘛。

原先的TCP調用圖

Fastsocket學習筆記之小結篇

  • kernel 3.9之前的tcp socket實現
  • bind系統調用會將socket和port進行綁定,并加入全局tcp_hashinfo的bhash鏈表中
  • 所有bind調用都會查詢這個bhash鏈表,如果port被占用,內核會導致bind失敗
  • listen則是根據用戶設置的隊列大小預先為tcp連接分配內存空間
  • 一個應用在同一個port上只能listen一次,那么也就只有一個隊列來保存已經建立的連接
  • nginx在listen之后會fork處多個worker,每個worker會繼承listen的socket,每個worker會創建一個epoll fd,并將listen fd和accept的新連接的fd加入epoll fd
  • 但是一旦新的連接到來,多個nginx worker只能排隊accept連接進行處理
  • 對于大量的短連接,accept顯然成為了一個瓶頸
  • </ul>

    起因:內核已經成為瓶頸

    • TCP處理&多核

      • 一個完整的TCP連接,中斷發生在一個CPU核上,但應用數據處理可能會在另外一個核上
      • 不同CPU核心處理,帶來了鎖競爭和CPU Cache Miss(波動不平衡)
      • 多個進程監聽一個TCP套接字,共享一個listen queue隊列
      • 用于連接管理全局哈希表格,存在資源競爭
      • epoll IO模型多進程對accept等待,驚群現象 Fastsocket學習筆記之小結篇
      • </ul> </li>

      • Linux VFS的同步損耗嚴重

        • Socket被VFS管理
        • VFS對文件節點Inode和目錄Dentry有同步需求
        • SOCKET只需要在內存中存在即可,非嚴格意義上文件系統,不需要Inode和Dentry
        • 代碼層面略過不必須的常規鎖,但又保持了足夠的兼容性
        • </ul> </li> </ul>

          Fastsocket所作改進

          1. TCP單個連接完整處理做到了CPU本地化,避免了資源競爭
          2. 保持完整BSD socket API
          3. </ol>

            CPU之間不共享數據,并行化各自獨立處理TCP連接,也是其高效的主要原因。其架構圖可以看出其改進: Fastsocket學習筆記之小結篇

            Fastsocket的TCP調用圖

            Fastsocket學習筆記之小結篇

            • 多個進程可以同時listen在同一個port上
            • 動態鏈接庫libfsocket.so攔截socket、bind、listen等系統調用并進入這個鏈接庫進行處理
            • 對于listen系統調用,fastsocket會記錄下這個fd,當應用通過epoll將這個fd加入到epoll fdset中時,libfsocket.so會通過ioctl為該進程clone listen fd關聯的socket、sock、file的系統資源
            • 內核模塊將clone的socket再次調用bind和listen
            • bind系統調用檢測到另外一個進程綁定到已經被綁定的port時,會進行相關檢查
            • 通過檢查sock將會被記錄到port相關聯的一個鏈表中,通過該鏈表可以知道所有bind同一個port的sock
            • 而sock是關聯到fd的,進程則持有fd,那么所有的資源就已經關聯到一起
            • 新的進程再次調用listen系統調用的時候,fastsocket內核會再次為其關聯的sock分配accept隊列
            • 結果是多個進程也就擁有了多個accept隊列,可避免cpu cache miss
            • fastsocket提供將每個listen和accept的進程綁定到用戶指定的CPU核
            • 如果用戶未指定,fastsocket將會為該進程默認綁定一個空閑的CPU核
            • </ul>

              Fastsocket短連接性能

              在新浪測試中,在24核的安裝有Centos 6.5的服務器上,借助于Fastsocket,Nginx和HAProxy每秒處理連接數指標(connection/second)性能很驚人,分別 增加290%和620%。這也證明了,Fastsocket帶來了TCP連接快速處理的能力。 除此之外,借助于硬件特性:

              • 借助于Intel超級線程,可以獲得另外20%的性能增長
              • HAProxy代理服務器借助于網卡Flow-Director特性支持,吞吐量可增加15%
              • </ul>

                Fastsocket V1.0正式版從2014年3月份開始已經在新浪生產環境中使用,用作代理服務器,因此大家可以考慮是否可以采用。針對1.0版本,以下環境較為收益:

                • 服務器至少不少于8個CPU核心
                • 短連接被大量使用
                • CPU周期大部分消耗在網絡軟中斷和套接字系統調用上
                • 應用程序使用基于epoll的非阻塞IO
                • 應用程序使用多個進程單獨接受連接
                • </ul>

                  多線程嘛,就得需要參考示范應用所提供實踐建議了。

                  Nginx測試服務器配置

                  • nginx工作進程數量設置成CPU核數個
                  • http keep-alive特性被禁用
                  • 測試端http_load從nginx獲取64字節靜態文件,并發量為500*CPU核數
                  • 啟用內存緩存靜態文件訪問,用于排除磁盤影響
                  • 務必禁用accept_mutex(多核訪問accept產生鎖競爭,另fastsocket內核模塊為其去除了鎖競爭)
                  • </ul>

                    從下表測試圖片中,可以看到:

                    1. Fastsocket在24核服務器達到了475K Connection/Second,獲得了21倍的提升
                    2. Centos 6.5在CPU核數增長到12核時并沒有呈現線性增長勢頭,反而在24核時下降到159k CPS
                    3. Linux kernel 3.13在24核時獲得了近乎兩倍于Centos 6.5的吞吐量,283K CPS,但在12核后呈現出擴展性瓶頸
                    4. </ol>

                      HAProxy重要配置

                      • 工作進程數量等同于CPU核數個
                      • 需要啟用RFD(Receive Flow Deliver)
                      • http keep-alive需要禁用
                      • 測試端http_load并發量為500*CPU核數
                      • 后端服務器響應外圍64個字節的消息
                      • </ul>

                        測試結果中:

                        • fastsocket呈現出了驚人的擴展性能
                        • 24核,Linux kernel 3.13成績為139K CPS
                        • 24核,Centos 6.5借助Fastsocket,獲得了370K CPS的吞吐量
                        • </ul>

                          Fastsocket學習筆記之小結篇

                          實際部署環境的成績

                          Fastsocket學習筆記之小結篇

                          8核服務器線上環境運行了24小時的成績,圖a展示了部署fastsocket之前CPU利用率,圖b為部署了fastsocekt之后的CPU利用率。 Fastsocket帶來的收益:

                          • 每個CPU核心負載均衡
                          • 平均CPU利用率降低10%
                          • HAProxy處理能力增長85%
                          • </ul>

                            Fastsocket學習筆記之小結篇

                            其實吧,這一塊期待新浪公布更多的數據。

                            長連接的支持正在開發中

                            長連接支持,還是需要等一等的。但是要支持什么類型長連接?百萬級別應用服務器類型,還是redis,可能是后者。雖然目前正做,但目前沒有時間表,但目前所做特性總結如下:

                            1. 網絡堆棧的定制

                              • SKB-Pool ,每一CPU核對應一個預分配skb pool,替換內核緩沖區kernel slab

                                • Percore skb pool
                                • 合并skb頭部和數據
                                • 本地Pool和重復循環使用的Pool(Flow-Director)
                                • </ul> </li>

                                • Fast-Epoll

                                  • 多進程之間TCP連接共享變得稀少
                                  • 在file結構體中保存Epoll entry,用以節省調用epoll_ctl時紅黑樹查詢的開銷
                                  • </ul> </li> </ul> </li>

                                  • 跨層的設計

                                    • Direct-TCP ,數據包隸屬于已建立套接字會直接跳過路由過程

                                      • 記錄TCP套接字的輸入路由信息(Record input route information in TCP socket)
                                      • 直接查找網絡套接字在進入網絡堆棧之前(Lookup socket directly before network stack)
                                      • 從套接字讀取輸入路由信息(Read input route information from socket)
                                      • 標記數據包被路有過(Mark the packet as routed)
                                      • </ul> </li>

                                      • Receive-CPU-Selection 類似于RFS,但更輕巧、精準與快速

                                        • 把當前CPU核id編碼到套接字中(Application marks current CPU id in the socket)
                                        • 直接查詢套接字在進入網絡堆棧之前(Lookup socket directly before network stack)
                                        • 讀取套接字中包含的CPU核,然后發送給它(Read CPU id from socket and deliver accordingly)
                                        • </ul> </li>

                                        • RPS-Framework 數據包在進入網絡堆棧之前,讓開發者在內核模塊之外定制數據包投遞規則,擴充RPS功能
                                        • </ul> </li> </ol>

                                          Redis測試結果

                                          測試環境:

                                          • CPU: Intel E5 2640 v2 (6 core) * 2
                                          • NIC: Intel X520
                                          • </ul>

                                            Redis配置選項:

                                            • TCP持久連接
                                            • 8個Redis實例,綁定不同端口
                                            • 使用到8個CPU核心,并且綁定CPU核
                                            • </ul>

                                              測試結果:

sesese色