前言
最近自己嘗試著搭建springcloud專案,果不其然,剛開始就踩坑了,還是那種一臉懵逼的坑。搭建後程式能正常執行註冊到eureka註冊中心,但註冊好之後便會立即登出掉。剛開始認為是執行緒拋異常掛掉了,便導致服務登出了。然後各種debug排查,最後發現是正常退出。至於具體原因,我下面做個詳細解釋。
eureka註冊中心
eureka服務端
初次執行結果
這裡註冊中心啟動是完全沒問題的,問題就出在client啟動,啟動日誌如下:
看上面的日誌可以觀察到,服務最開始是有正常註冊到eureka註冊中心的,但是緊接著會發現。Unregistering application EUREKA-CLIENT with eureka with status DOWN,後面接著 Shutting down DiscoveryClient ... 最後 Unregistering ...。
解決方案之一
看日誌並未發現很明顯的異常,然後debug除錯一波,無任何錯誤發現。便隨手google了一下。果然還是有小夥伴曾經也有遇到過同樣的問題的。網上的解決辦法如下: 在client端pom中加上依賴:
當然,這也只是其中的一個解決辦法。其實出現上述情況的原因是JVM隨著主執行緒的結束而退出了。容器close時會觸發DiscoveryClient的shutdown方法,便會登出已註冊的節點。但是加上web依賴後,就拿tomcat來舉例說明,隨著springboot專案的啟動,會建立一個WebServer:我們們重點關注下initialize方法中的:
看原始碼上的註釋,便會發現這裡開啟了一個使用者執行緒(非守護執行緒),這樣主執行緒退出後JVM也不會退出。也就不會觸發登出節點的動作。其他解決方案
其實解決這個問題的辦法還是很簡單的,只要你能保證由一個正常的使用者執行緒存在就行了,有可能我們們自己的提供服務的應用不需要用到tomcat這樣的web容器,而是像其他rpc服務一樣呼叫時,我們們便可以自己建立一個使用者執行緒即可(預設構造出來的執行緒都是使用者執行緒,除非你呼叫setDaemon方法將其daemon屬性設定成true才會變成守護執行緒)。然後我們們自己內部提供一個狀態值可以讓其正常退出即可。 下面寫個簡單的例子:
總結
通過這次的踩坑,還是彌補了自己對JVM理解的一些盲區。比如當程式中只要還有一個使用者執行緒存在時,程式便不會立即結束。只有當程式中只存在守護執行緒或者不存在任何執行緒的情況下,程式才會馬上結束。我們通常瞭解到的類似gc的操作執行緒,便是守護執行緒,這樣我們們自己的業務執行緒在異常情況下掛掉,程式也會隨之結束。