2017-01-06 52 views
1

我們正在運行它本身包含幾個微服務的Web應用程序跟蹤請求過程,併爲每個請求,我們需要最後調用第三方服務,這是耗時,通常需要幾秒鐘的時間。我們需要使用請求traceId來跟蹤每個請求的所有服務中的所有處理日誌。如何使用NIO或事件驅動的框架時,像Netty的

在當前的實現中,我們使用的是基於線程的併發模型,一個線程被分配到從開始的每個服務的最終處理請求,並等待遠程服務的響應時受阻。這是很自然的把traceIdThreadLocal的這樣我們就可以拿回來,每當/地方,我們需要它。

但基於線程的併發模型不能很好地擴展,我們傾向於更改爲NIO /事件驅動模型,並試圖Netty的一個非常大的性能提升。但是每個請求處理的不同階段可能會由Netty的不同線程處理,這使得日誌的跟蹤非常棘手。

我們目前的考慮因素包括:

  • traceId作爲方法的參數,它已經在請求無論如何,但它如果一個深嵌套的方法需要它非常不便利。
  • Set traceId into ThreadLocal在每次回調開始時。但是我個人認爲這種方法很容易出錯,並且可能會引入難以找到的競爭條件錯誤。

那麼,在NIO /事件驅動模型中解決這種跟蹤問題的複雜/優雅的方法是什麼?

+0

您可以使用通道ID而不是線程嗎? – Nicholas

回答

1

這是致命的所有的Java EE框架存在的弱點努力適應一個異步的世界(以及爲什麼現有從未真正會) - 幾十年來在ThreadLocals儲存狀態。

基本上,您需要將您想傳遞給渠道的狀態或要求處理的狀態聯繫起來,以便它可用於接下來的任何代碼 - 而且您不能假設這將發生在同一個線程上。

兩種方式來解決這個問題:

  1. Channel.attr() - 如果國家可連接到連接,這是一次只能用於一個東西,然後創建一個靜態AttributeKey和通它到Channel.attr() - 你會得到一個屬性,它最初是空的 - 在你的第一個處理程序中,將它分配給某個東西,之後的所有東西都可以將它拉出來(確保在你知道你時清除它如果連接要被重新使用而不被關閉,就像HTTP保持連接一樣)。
  2. 將它附加到您解碼的某個對象 - 爲解碼器的HTTP請求子類(如果HTTP是您正在做的)並使用ID創建您自己的子類。

使用ThreadLocals這個東西似乎只是自然的,因爲我們這個行業花了十年或二十年繞道進入製作程序模型I/O在什麼都沒有做什麼的電腦正在做的方式 - 雖然這賣了很多的硬件(異步更像是我在1983年左右寫的中斷處理程序):-)

+0

感謝@Tim Boudreau,最後我用第二種方法將traceId綁定到了請求/響應對象,因爲Netty沒有適合我們的所有用例,我們的一些應用程序由Java 8的CompletableFuture框架實現。所以我認爲將traceId與相關對象綁定是解決此問題最自然的方法。 – shizhz

2

我的2美分:如果你在NIO /事件驅動模型,那麼你可能必須將「請求ID」從調用者傳遞給被調用者,然後返回調用者(異步/偶數驅動方法)。這跟線程或通道Id沒有任何關係(一個通道可以重複用於各種查詢,這樣你就不會一次又一次地支付「連接」)。

然後在發送方,你可以使用地圖或左右(甚至是一個物化通過任何持久性工具)恢復的背景下,做你需要做什麼。

+0

感謝@Frederic,所以如果我在被調用者的多重嵌套方法中需要「請求標識符」,我會將其一直傳遞給該方法,或爲被調用者準備上下文? – shizhz

+0

我相信你可能需要傳遞id(或者更多,如果需要的話,例如上下文或者通過對上下文的外部持久化和param中的id來獲取上下文的id)。然後,無論深度如何,每個人都可以知道哪個查詢/操作/上下文參與了哪個查詢。 –

相關問題