2 回答

TA貢獻(xiàn)1844條經(jīng)驗 獲得超8個贊
客戶端向服務(wù)器 A 發(fā)起 HTTP 調(diào)用。
服務(wù)器 A 創(chuàng)建一個唯一密鑰并將其存儲在緩存服務(wù)器中 60 秒(超時期限)。
服務(wù)器 A 通過 HTTP 調(diào)用將請求轉(zhuǎn)發(fā)給 B。這是對 B 的一次火災(zāi)和忘記呼叫。
服務(wù)器 A 立即使用唯一密鑰向客戶端返回響應(yīng)。
客戶端開始使用 GET 狀態(tài) HTTP API 進(jìn)行輪詢(假設(shè)每 500 毫秒一次),以檢查服務(wù)器 A 是否已完成處理。
意味著當(dāng)服務(wù)器 B 完成任務(wù)并通過 HTTP API 回調(diào)到服務(wù)器 A 時。
服務(wù)器 A 將針對唯一鍵的響應(yīng)短時間(比如 60 秒)存儲在緩存中。
客戶端調(diào)用 A 進(jìn)行狀態(tài)檢查 API 將從緩存中獲取數(shù)據(jù)并將其返回給客戶端。
除了服務(wù)器A和服務(wù)器B之外沒有其他組件。有緩存服務(wù)器,但它是A內(nèi)部的。B不需要知道它。哪個服務(wù)維護(hù)什么,定義明確,維護(hù)方便。B 可以有內(nèi)部隊列來處理轉(zhuǎn)發(fā)的請求。但是 A 不需要知道 B 是如何實現(xiàn)的。每個服務(wù)都可以由不同的團(tuán)隊使用簡單的 HTTP 合同進(jìn)行維護(hù)。
另一個優(yōu)點(diǎn)是客戶端不持有與 A 的長時間運(yùn)行的 HTTP 連接。我提到了輪詢,認(rèn)為大多數(shù)原始客戶端就像一個舊瀏覽器。如果您的客戶端支持網(wǎng)絡(luò)套接字,您可以使用它從 A 發(fā)送回響應(yīng)。無論如何,這里的輪詢會執(zhí)行得很好,因為狀態(tài)檢查 API 只是一個緩存調(diào)用,沒有別的。
有人會問,A&B之間server to server通信的重試邏輯在哪里?但是我們不需要為此排隊。本質(zhì)上,客戶端在這里需要同步類型的調(diào)用。我們只是將其分解為多個調(diào)用。反正反應(yīng)要快。我們可以在 HTTP 客戶端有一個重試 3-5 次的重試機(jī)制,以防網(wǎng)絡(luò)出現(xiàn)故障。我們正在使用豆莢。我希望它位于 k8s 負(fù)載均衡器之后。如果第一個 pod 出現(xiàn)故障,該負(fù)載均衡器無論如何都會重試一個健康的 pod。所以你幾乎被覆蓋在那里。
我不知道您的確切要求,我是在快速思考后寫下這篇文章的。但總的來說,這對我來說很好。它會很健壯并且具有低延遲調(diào)用。也許這里和那里幾乎不需要調(diào)整。
根據(jù)評論更新:
客戶端輪詢的實現(xiàn)非?;A(chǔ)?;旧?,它是一個循環(huán),每 0.5 秒運(yùn)行一次并進(jìn)行 HTTP GET 調(diào)用以檢查狀態(tài)。我相信任何客戶都應(yīng)該能夠?qū)崿F(xiàn)這一點(diǎn)。但是可能會有一些限制,客戶團(tuán)隊不想進(jìn)行任何更改。我不想進(jìn)入那個。
假設(shè)一些客戶端沒有輪詢功能,一些客戶端不支持網(wǎng)絡(luò)套接字。然后唯一剩下的就是客戶端和服務(wù)器 A 之間的長期 HTTP 連接。假設(shè)我們采用這種方法。不討論在服務(wù)器中有一個長期存在的請求線程的明顯問題,還有另一個特定于這種情況的問題。服務(wù)器B回調(diào)時需要通知服務(wù)器A中的請求線程。
方法 1:我們使用其他答案中提到的某種隊列(我會將隊列保留在 A 內(nèi)部,但我們不要深入討論)。我們使用一些消息鍵,以便消費(fèi)發(fā)生在同一個 pod 中。我們的 API 和消費(fèi)者都在同一個 pod 中運(yùn)行。這是可以實現(xiàn)的。但是請求線程和消費(fèi)者線程是不同的。消費(fèi)者線程需要以某種方式通知請求線程結(jié)果可用。所以需要一些線程間通信,這只會讓事情變得復(fù)雜。這不是隊列的責(zé)任。所以我會放棄這種方法。
有人會問,能不能請求線程直接監(jiān)聽一些帶有特定key的消息。我不這么認(rèn)為。為了爭論的緣故,有一些隊列技術(shù)可以做到這一點(diǎn)。但是您實際上是為每個無法擴(kuò)展的鍵擁有短暫的消費(fèi)者和單獨(dú)的分區(qū)。監(jiān)聽所有消息并忽略臨時消費(fèi)者中的所有消息也不是一個可行的解決方案。
方法二:從方法一我們了解到,服務(wù)器B發(fā)送回調(diào)時需要通知請求線程。這會使整個流程變得非常復(fù)雜,并且您需要像 ZooKeeper 這樣的額外組件來進(jìn)行分布式鎖定或監(jiān)視某些更改。我們可以簡單地擴(kuò)展我們當(dāng)前的系統(tǒng)來進(jìn)行服務(wù)器端輪詢,而不是那樣做。由于我們已經(jīng)在進(jìn)行客戶端輪詢,因此響應(yīng)時間不應(yīng)有任何明顯差異。我們只是將其移至服務(wù)器端。此外,無論如何,無論我們構(gòu)建分布式通知還是服務(wù)器輪詢,我們都必須在服務(wù)器 A 中維護(hù)長時間運(yùn)行的請求線程。流程看起來像:
客戶端調(diào)用服務(wù)器 A。
服務(wù)器 A 請求線程對服務(wù)器 B 進(jìn)行 HTTP 調(diào)用。
服務(wù)器 B 開始后臺處理并立即將 HTTP 響應(yīng)返回給 A。
如果密鑰每 500 毫秒可用,則 A 中的請求線程會在緩存服務(wù)器中啟動循環(huán)檢查。
B 處理結(jié)果并向 A 進(jìn)行 HTTP 回調(diào)。
A 中的回調(diào)線程針對相同的鍵緩存結(jié)果。
原始請求線程從緩存中讀取值并將響應(yīng)返回給客戶端。
在進(jìn)行下一次緩存調(diào)用之前,原始請求線程將休眠 500 毫秒。所以其他線程將能夠利用時間。此外,通過密鑰獲取緩存非???,并且您不會在那里遇到任何可擴(kuò)展性問題。
但是,如果您保持來自客戶端的長時間運(yùn)行的 HTTP 連接,您將更快地耗盡請求線程。因此,您將需要更多 A pod 來處理相同的請求率。我仍然建議與客戶團(tuán)隊交談并在那里進(jìn)行必要的更改,以便使用短暫的連接(與您當(dāng)前的流程相同)。

TA貢獻(xiàn)1830條經(jīng)驗 獲得超3個贊
問題描述
因此,假設(shè)您調(diào)用的服務(wù)器應(yīng)用程序server_app有 3 個 pod:
? ? ?+---------------------+
? ? ?|? server_app_service |
? ? ?+---------------------+
? ? ?|? server_app_pod_a? ?|
? ? ?|? server_app_pod_b? ?|
? ? ?|? server_app_pod_c? ?|
? ? ?+---------------------+
您的服務(wù)收到一個名為 的請求"request A"
,并決定將其傳遞給server_app_pod_a
。現(xiàn)在您server_app_pod_a
將請求轉(zhuǎn)發(fā)到某個網(wǎng)關(guān),并等待某種通知,以繼續(xù)處理客戶端的響應(yīng)。正如您所知,無法保證當(dāng)網(wǎng)關(guān)執(zhí)行 時request B
,服務(wù)會server_app_pod_a
再次將其傳遞給。即使這樣做,您的應(yīng)用程序的狀態(tài)管理也將成為一項艱巨的任務(wù)。
訊息
您可能已經(jīng)注意到,我在上一段中將“通知”一詞加粗了,那是因為如果您認(rèn)真考慮一下,它看起來request "B"
更像是帶有某些消息的通知,而不是對某些資源的請求。所以我的第一選擇是像 kafka 這樣的消息隊列(如你所知,有很多這樣的消息隊列)。這個想法是,如果你可以定義一個算法來計算你的請求的唯一鍵,你可以期待在你完全相同的 pod 中產(chǎn)生通知。這樣,狀態(tài)管理會簡單很多,而且在同一個 pod 中獲得通知的機(jī)會也會高很多(當(dāng)然這取決于很多因素,比如消息隊列的狀態(tài))??纯茨愕膯栴}:
我想以最好的方式做到這一點(diǎn),同時牢記縮放比例。
當(dāng)然,您可以使用像 kafka 這樣的消息隊列來實現(xiàn)消息隊列和您的應(yīng)用程序的擴(kuò)展和更少的數(shù)據(jù)丟失。
所有請求都會超時,使初始請求在 60 秒后超時。
這取決于您如何管理代碼庫中的超時,使用上下文是個好主意。
我還想知道如何用其他編程語言實現(xiàn)它。
使用消息隊列是一個普遍的想法,它幾乎適用于任何編程語言,但根據(jù)語言的編程范式、特定于語言的庫和工具,可能會有一些其他方法來解決這個問題。例如Scala
,如果你使用一些特定的工具akka
(它提供了 actor 模型編程范例),你可以使用所謂的東西akka-cluster-sharding
, 來處理這個問題。這個想法很簡單,我們知道必須有某種監(jiān)管者,它知道自己訂閱者的確切位置和狀態(tài)。因此,當(dāng)它收到一些消息時,它只知道將請求轉(zhuǎn)發(fā)到何處以及將請求轉(zhuǎn)發(fā)給哪個參與者(我們正在談?wù)搮⑴c者模型編程)。換句話說,它可以用于在集群上生成的參與者之間共享狀態(tài),無論是否在同一臺機(jī)器上。但作為個人偏好,我不會選擇特定語言的交流,而是會堅持籠統(tǒng)的想法,因為它可能會在未來引起問題。
包起來
足夠長的解釋:)。只是為了弄清楚我在說什么,讓我們繼續(xù)完全相同的場景,但通信模型有所不同:
客戶端向服務(wù)發(fā)送請求“A”?
server_app
。該服務(wù)選擇其中一個 pod(
server_app_pod_b
例如)來處理請求。然后 pod 嘗試為請求定義一些鍵,并將它與請求一起傳遞給網(wǎng)關(guān),并等待帶有鍵的消息在隊列中發(fā)布。
網(wǎng)關(guān)做它應(yīng)該做的事,并將帶有密鑰的消息發(fā)送到消息隊列。
完全相同的 pod
serer_app_pod_b
接收到帶有 key 的消息,獲取消息的數(shù)據(jù),并繼續(xù)處理客戶端的請求。
可能還有其他方法可以解決這個問題,但這就是我想要的。希望它有所幫助!
- 2 回答
- 0 關(guān)注
- 149 瀏覽
添加回答
舉報