c10k
2023年由dan kegel ,那時的伺服器執行著linux2.2系統,32位系統,記憶體很少(2g),千兆網絡卡。
怎麼在這樣的系統中支援併發 1 萬的請求呢?
先來計算一下:
最大每個連線的記憶體:2gb / 10000 = 200kb
最大每個連線的頻寬: 1000mbit/10000 = 100bit
所以只要每個連線的記憶體不大於200kb,頻寬不大於100bit,物理資源三足夠的。
剩下的就是軟體問題:
在 c10k 以前,linux 中網路處理都用同步阻塞的方式,也就是每個請求都分配乙個程序或者執行緒。
執行緒數低於100,那肯定是沒有問題的,但是連線數目到達10000個的話,10000 個程序或執行緒的排程、上下文切換乃至它們占用的記憶體,都會成為瓶頸。
這就有兩個問題要解決:
1 乙個執行緒怎麼處理多個io,是否可以用非同步或者非阻塞
2 更節省資源的方式來服務客戶
i/o 模型優化
兩種 i/o 事件通知的方式:
水平觸發:只要檔案描述符可以非阻塞地執行 i/o ,就會觸發通知。也就是說,應用程式可以隨時檢查檔案描述符的狀態,然後再根據狀態,進行 i/o 操作。
邊緣觸發:只有在檔案描述符的狀態發生改變(也就是 i/o 請求達到)時,才傳送一次通知。這時候,應用程式需要盡可能多地執行 i/o,直到無法繼續讀寫,才可以停止。如果 i/o 沒執行完,或者因為某種原因沒來得及處理,那麼這次通知也就丟失了。
i/o 多路復用的實現方法:
1 使用非阻塞 i/o 和水平觸發通知,比如使用 select 或者 poll。
優點:是對應用程式比較友好,它的 api 非常簡單
缺點:效率比較低,select有檔案描述符的限制,需要兩層迴圈,時間複雜度變為o(n^2),polll處理耗時跟檔案描述符是o(n)。檔案描述符需要從使用者空間傳到核心空間,核心修改後,在返回使用者空間。
2 使用非阻塞 i/o 和邊緣觸發通知,比如 epoll。
使用紅黑樹和事件驅動的機制,提高效率。但是只有io發生的時候才會通知。需要應用程式做更多的io操作和處理異常事件。
3 使用非同步 i/o(asynchronous i/o,簡稱為 aio)
工作模型優化
1 主程序 + 多個 worker 子程序,這也是最常用的一種模型
1 主程序執行 bind() + listen() 後,建立多個子程序;
2 然後,在每個子程序中,都通過 accept() 或 epoll_wait() ,來處理相同的套接字。
會出現epoll驚群問題:當網路 i/o 事件發生時,多個程序被同時喚醒,但實際上只有乙個程序來響應這個事件,其他被喚醒的程序都會重新休眠。
nginx 的做法是在每個 worker 程序中,都增加乙個了全域性鎖(accept_mutex)。這些 worker 程序需要首先競爭到鎖,只有競爭到鎖的程序,才會加入到 epoll 中,這樣就確保只有乙個 worker 子程序被喚醒。
也可以用執行緒代替程序:主線程負責套接字初始化和子執行緒狀態的管理,而子執行緒則負責實際的請求處理。由於執行緒的排程和切換成本比較低,實際上你可以進一步把 epoll_wait() 都放到主線程中,保證每次事件都只喚醒主線程,而子執行緒只需要負責後續的請求處理。
2 監聽到相同埠的多程序模型。
每個程序都監聽到同乙個埠,由核心做負載均衡,喚醒程序,避免了驚群問題。需要在linux 3.9 以上開啟so_reuseport
c1000k
從 1 萬到 10 萬,其實還是基於 c10k 的這些理論,epoll 配合執行緒池,再加上 cpu、記憶體和網路介面的效能和容量提公升。大部分情況下,c100k 很自然就可以達到。
但是到100萬就沒有那麼簡單了
物理資源上:
1 假設每個請求需要 16kb 記憶體的話,那麼總共就需要大約 15 gb 記憶體。
2 從頻寬上來說,假設只有 20% 活躍連線,即使每個連線只需要 1kb/s 的吞吐量,總共也需要 1.6 gb/s 的吞吐量。千兆網絡卡顯然滿足不了這麼大的吞吐量,所以還需要配置萬兆網絡卡,或者基於多網絡卡 bonding 承載更大的吞吐量。
軟體資源上:
大量的連線也會占用大量的軟體資源,比如檔案描述符的數量、連線狀態的跟蹤(conntrack)、網路協議棧的快取大小(比如套接字讀寫快取、tcp 讀寫快取)等等。
c1000k 的解決方法,本質上還是構建在 epoll 的非阻塞 i/o 模型上。只不過,除了 i/o 模型之外,還需要從應用程式到 linux 核心、再到 cpu、記憶體和網路等各個層次的深度優化,特別是需要借助硬體,來解除安裝那些原來通過軟體處理的大量功能。
c10m
在 c1000k 問題中,各種軟體、硬體的優化很可能都已經做到頭了。無亂怎麼除錯引數,提公升硬體都到頭了。根本的問題是,linux網路協議棧做了太多太繁重的工作。
解決這個問題,那就是跳過linux網路協議棧,常見的解決方案是dpdk 和 xdp。
第一種機制,dpdk,是使用者態網路的標準。它跳過核心協議棧,直接由使用者態程序通過輪詢的方式,來處理網路接收。
在 pps 非常高的場景中,查詢時間比實際工作時間少了很多,絕大部分時間都在處理網路包;而跳過核心協議棧後,就省去了繁雜的硬中斷、軟中斷再到 linux 網路協議棧逐層處理的過程,應用程式可以針對應用的實際場景,有針對性地優化網路包的處理邏輯,而不需要關注所有的細節。
dpdk 還通過大頁、cpu 繫結、記憶體對齊、流水線併發等多種機制,優化網路包的處理效率。
第二種機制,xdp(express data path),則是 linux 核心提供的一種高效能網路資料路徑。它允許網路包,在進入核心協議棧之前,就進行處理,也可以帶來更高的效能。xdp 底層跟我們之前用到的 bcc-tools 一樣,都是基於 linux 核心的 ebpf 機制實現的。
xdp 的應用程式通常是專用的網路應用,常見的有 ids(入侵檢測系統)、ddos 防禦、 cilium 容器網路外掛程式等。
Linux 效能調優(1)
讀書筆記 linux 效能調優指南 首先建議對核心又足夠的了解,尤其記憶體管理,不然很多概念很難理解 1.理解linux 作業系統 2.監控和檢測工具 3.分析效能瓶頸 4.優化作業系統 linux效能監控工具 工具常用功能 top所有程序情況 vmstat 系統活動,硬體和系統資訊 uptime,...
hibernate效能調優1
a 完成同樣一件事,hibernate提供了可供選擇的一些方式,但具體使用什麼方式,可能用效能 都會有影響。顯示,一次返回十萬條記錄 list set bag map等 進行處理,很可能導致記憶體不夠的問題,而如果用基於游標 scrollableresults 或 iterator的結果集,則不存在...
zabbix效能調優(1)
概述 在我們生產環境使用zabbix監控會遇到卡頓效能緩慢等問題。這種情況下正確的調整zabbix系統,使之保持高效能就是非常重要的。效能緩慢的表現 01 nvps引數值 每秒寫入的新值數量 簡稱nvps 確認問題 雖然我們在前端看到zabbix的效能緩慢,但是我們還需要根據zabbix的後台資訊來...