2 回答

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

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