前言
最近自己嘗試着搭建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的操作線程,便是守護線程,這樣咱們自己的業務線程在異常情況下掛掉,進程也會隨之結束。
