您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關(guān)Envoy如何將連接映射到線程,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。
我得到的關(guān)于Envoy的最常見技術(shù)問題之一是要求對其使用的線程模型進行低級描述。 小編將介紹Envoy如何將連接映射到線程,以及內(nèi)部使用的線程本地存儲(TLS)系統(tǒng)的描述,以使代碼極其平行且性能高。
Envoy使用三種不同類型的線程,如圖1所示。
Main:此線程擁有服務器啟動和關(guān)閉,所有xDS API處理(包括DNS,運行狀況檢查和常規(guī)集群管理),運行時,統(tǒng)計刷新,管理和一般進程管理(信號,熱啟動等)。 在此線程上發(fā)生的所有事情都是異步的并且是“非阻塞的”。通常,主線程協(xié)調(diào)所有不需要大量CPU來完成的關(guān)鍵過程功能。 這允許將大多數(shù)管理代碼編寫為單線程編寫。
Worker:默認情況下,Envoy為系統(tǒng)中的每個硬件線程生成一個工作線程。 (這可以通過--concurrency 選項控制)。 每個工作線程運行一個“非阻塞”事件循環(huán),負責監(jiān)聽每個偵聽器(當前沒有偵聽器分片),接受新連接,為連接實例化過濾器堆棧,以及處理所有IO的生命周期。 連接。 同樣,這允許將大多數(shù)連接處理代碼寫成好像是單線程的。
文件刷新器:Envoy寫入的每個文件(主要是訪問日志)當前都有一個獨立的阻塞刷新線程。 這是因為即使使用O_NONBLOCK寫入文件系統(tǒng)緩存文件有時也會阻塞(嘆息)。 當工作線程需要寫入文件時,數(shù)據(jù)實際上被移入內(nèi)存緩沖區(qū),最終通過文件刷新線程刷新。 這是代碼的一個區(qū)域,技術(shù)上所有工作人員都可以阻止同一個鎖嘗試填充內(nèi)存緩沖區(qū)。 還有一些其他的將在下面進一步討論。
連接處理
如上所述,所有工作線程都會在沒有任何分片的情況下監(jiān)聽所有偵聽器。 因此,內(nèi)核用于智能地將接受的套接字分派給工作線程。 現(xiàn)代內(nèi)核一般都很擅長這個; 他們使用諸如IO優(yōu)先級提升之類的功能來嘗試填充線程的工作,然后開始使用同時監(jiān)聽同一套接字的其他線程,以及不使用單個自旋鎖來處理每個接受。
一旦工人接受了連接,它就永遠不會離開那個工人。 所有進一步的連接處理都在工作線程內(nèi)完全處理,包括任何轉(zhuǎn)發(fā)行為。 這有一些重要的含義:
Envoy中的所有連接池都是每個工作線程。 因此,盡管HTTP / 2連接池一次只與每個上游主機建立一個連接,但如果有四個工作站,則每個上游主機在穩(wěn)定狀態(tài)下將有四個HTTP / 2連接。
Envoy以這種方式工作的原因是因為通過將所有代碼保存在單個工作線程中,幾乎所有代碼都可以在沒有鎖的情況下編寫,就像它是單線程一樣。 這種設(shè)計使得大多數(shù)代碼更易于編寫,并且可以非常好地擴展到幾乎無限數(shù)量的工作人員。
然而,一個主要的問題是,從內(nèi)存和連接池效率的角度來看,調(diào)整 -- 并發(fā)選項實際上非常重要。 擁有比所需更多的工作人員將浪費內(nèi)存,創(chuàng)建更多空閑連接,并導致更低的連接池命中率。 在Lyft,我們的邊車Envoys以非常低的并發(fā)性運行,因此性能大致與他們旁邊的服務相匹配。 我們只以最大并發(fā)性運行我們的邊緣Envoys。
什么是非阻塞
到目前為止,在討論主線程和工作線程如何操作時,已經(jīng)多次使用術(shù)語“非阻塞”。 所有代碼都是在假設(shè)沒有任何阻塞的情況下編寫的。 然而,這并不完全正確(完全是真的嗎?)。 特使確實采用了一些過程寬鎖:
如前所述,如果正在寫入訪問日志,則所有工作程序在填充內(nèi)存訪問日志緩沖區(qū)之前都會獲取相同的鎖。 鎖定保持時間應該非常低,但是這種鎖可以在高并發(fā)性和高吞吐量下競爭。
Envoy采用了一個非常復雜的系統(tǒng)來處理線程本地的統(tǒng)計數(shù)據(jù)。 這將是一個單獨的帖子的主題。 但是,我將簡要提一下,作為線程本地統(tǒng)計處理的一部分,有時需要獲取對中央“stat store”的鎖定。這種鎖定不應該高度爭用。
主線程需要定期與所有工作線程協(xié)調(diào)。 這是通過從主線程“發(fā)布”到工作線程(有時從工作線程返回到主線程)來完成的。 發(fā)布需要鎖定,以便將發(fā)布的消息放入隊列中以便以后發(fā)送。 這些鎖永遠不應該高度爭用,但它們?nèi)匀豢梢栽诩夹g(shù)上阻止。
當Envoy將自己記錄到標準錯誤時,它會獲取進程范圍的鎖定。 一般來說,Envoy本地記錄被認為是表現(xiàn)糟糕的,所以沒有多少考慮改善這一點。
還有一些其他隨機鎖,但它們都不在性能關(guān)鍵路徑中,永遠不應該爭用。
線程本地存儲
由于Envoy將主線程職責與工作線程職責分開,因此需要在主線程上完成復雜處理,然后以高度并發(fā)的方式使每個工作線程可用。 下面介紹了Envoy的高級線程本地存儲(TLS)系統(tǒng)。
如已經(jīng)描述的那樣,主線程基本上處理Envoy過程中的所有管理/控制平面功能。 (控制平面在這里有點過載但是當在特使過程中考慮并與工人做的轉(zhuǎn)發(fā)進行比較時,似乎是合適的)。 主線程進程執(zhí)行某些工作是一種常見模式,然后需要使用該工作的結(jié)果更新每個工作線程,并且工作線程不需要在每次訪問時獲取鎖定。
Envoy的TLS系統(tǒng)的工作原理如下:
在主線程上運行的代碼可以分配進程范圍的TLS槽。 雖然是抽象的,但實際上,這是一個允許O(1)訪問的向量索引。
主線程可以將任意數(shù)據(jù)設(shè)置到其槽中。 完成此操作后,數(shù)據(jù)將作為正常事件循環(huán)事件發(fā)布到每個工作程序中。
工作線程可以從其TLS槽讀取,并將檢索那里可用的任何線程本地數(shù)據(jù)。
雖然非常簡單,但這是一個非常強大的范例,與RCU鎖定概念非常相似。 (實質(zhì)上,工作線程在工作時從不會看到TLS插槽中的數(shù)據(jù)發(fā)生任何變化。更改只發(fā)生在工作事件之間的靜止期間)。 特使以兩種不同的方式使用它:
通過在沒有任何鎖定的情況下訪問每個工作人員存儲不同的數(shù)據(jù)
通過將共享指針存儲到每個worker的只讀全局數(shù)據(jù)。 因此,每個工作者都具有對在工作時不能遞減的數(shù)據(jù)的引用計數(shù)。 只有當所有工作人員都已停頓并加載新的共享數(shù)據(jù)時,舊數(shù)據(jù)才會被銷毀。 這與RCU相同。
集群線程更新
我將描述TLS如何用于集群管理。 群集管理包括xDS API處理和/或DNS以及運行狀況檢查。
圖3顯示了涉及以下組件和步驟的總體流程:
集群管理器是Envoy內(nèi)部的組件,用于管理所有已知的上游集群,CDS API,SDS / EDS API,DNS和活動(帶外)運行狀況檢查。 它負責創(chuàng)建每個上游集群的最終一致視圖,其中包括已發(fā)現(xiàn)的主機以及運行狀況。
運行狀況檢查程序執(zhí)行活動運行狀況檢查,并將運行狀況更改報告回集群管理器。
執(zhí)行CDS / SDS / EDS / DNS以確定群集成員資格。 狀態(tài)更改將報告回集群管理器。
每個工作線程都在不斷運行事件循環(huán)。
當集群管理器確定集群的狀態(tài)已更改時,它會創(chuàng)建集群狀態(tài)的新只讀快照,并將其發(fā)布到每個工作線程。
在下一個靜止期間,工作線程將更新分配的TLS插槽中的快照。
在需要確定要負載均衡的主機的IO事件期間,負載均衡器將在TLS插槽中查詢主機信息。 沒有獲得鎖定來執(zhí)行此操作。 (另請注意,TLS還可以在更新時觸發(fā)事件,以便負載平衡器和其他組件可以重新計算高速緩存,數(shù)據(jù)結(jié)構(gòu)等。這超出了本文的范圍,但在代碼中的各個位置使用)。
通過使用先前描述的過程,Envoy能夠處理每個請求而不需要任何鎖定(除了之前描述的那些)。 除了TLS代碼本身的復雜性之外,大多數(shù)代碼都不需要理解線程如何工作,并且可以編寫為單線程。 這使得大多數(shù)代碼更容易編寫,并產(chǎn)生出色的性能。
其他使用TLS的子系統(tǒng)
TLS和RCU在Envoy中廣泛使用。 其他一些例子包括:
運行時(功能標志)覆蓋查找:在主線程上計算當前功能標志覆蓋映射。 然后使用RCU語義為每個工作程序提供只讀快照。
路由表交換:對于RDS提供的路由表,路由表在主線程上實例化。 然后使用RCU語義為每個工作程序提供只讀快照。 這使得路由表交換有效地原子化。
HTTP日期標頭緩存:事實證明,在每個請求上計算HTTP日期標頭(當每個核心執(zhí)行~25K + RPS時)非常昂貴。 Envoy大約每半秒計算一次日期標題,并通過TLS和RCU將其提供給每個工作人員。
還有其他情況,但前面的例子應該提供TLS所用事物的良好品味。
已知的性能陷阱
雖然Envoy整體表現(xiàn)相當不錯,但是當它以非常高的并發(fā)性和吞吐量使用時,有一些已知領(lǐng)域需要注意:
正如本文中已經(jīng)描述的那樣,當前所有工作者在寫入訪問日志的內(nèi)存緩沖區(qū)時都會獲得鎖定。 在高并發(fā)性和高吞吐量的情況下,當寫入最終文件時,將需要以按順序交付為代價對每個工作人員批量訪問日志進行批處理。 或者,訪問日志可以成為每個工作線程。
盡管統(tǒng)計信息已經(jīng)過非常優(yōu)化,但在非常高的并發(fā)性和吞吐量下,個別統(tǒng)計信息可能存在原子爭用。 對此的解決方案是每個工人計數(shù)器,定期沖洗到中央計數(shù)器。 這將在后續(xù)文章中討論。
如果Envoy部署在幾乎沒有需要大量資源來處理的連接的場景中,現(xiàn)有架構(gòu)將無法正常運行。 這是因為無法保證連接在工作人員之間均勻分布。 這可以通過實現(xiàn)工作者連接平衡來解決,其中工作人員能夠?qū)⑦B接轉(zhuǎn)發(fā)給另一個工作人員進行處理。
Envoy的線程模型旨在支持編程的簡單性和大規(guī)模并行性,但如果調(diào)整不當可能會浪費內(nèi)存和連接使用。 該模型允許它在非常高的工人數(shù)量和吞吐量下表現(xiàn)良好。
正如我在Twitter上簡要提到的那樣,該設(shè)計也適合在DPDK之類的完整用戶模式網(wǎng)絡堆棧上運行,這可能導致商用服務器在執(zhí)行完整的L7處理時每秒處理數(shù)百萬個請求。 看看未來幾年建成什么將是非常有趣的。
看完上述內(nèi)容,你們對Envoy如何將連接映射到線程有進一步的了解嗎?如果還想了解更多知識或者相關(guān)內(nèi)容,請關(guān)注億速云行業(yè)資訊頻道,感謝大家的支持。
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。