大部分項目部署中,為了方便,可能都直接使用kill -9 服務的pid來停掉服務。
但是由於Eureka采用心跳的機制來上下線服務,會導致服務消費者調用此已經kill的服務提供者然后出錯。
可以采用以下方式來解決:
核心是先調用方法主動通知Eureka注冊中心服務下線,然后再停掉服務。
本文會介紹幾種eureka 注冊中心服務下線的方式
最不可取的就是直接使用kill命令停掉服務。
默認情況下,如果Eureka Server在90秒沒有收到Eureka客戶的續約,它會將實例從其注冊表中刪除。但這種做法的不好之處在於, 客戶端已經停止了運行,但仍然在注冊中心的列表中。 雖然通過一定的負載均衡策略或使用熔斷器可以讓服務正常進行,但有沒有方法讓注冊中心馬上知道服務已經下線呢?
1、直接關閉服務
kill -9 沒有善后
kill -15 有善后 會發送down狀態到eureka server
kill java進程【不建議】
這種方式簡單粗暴,直接造成的影響就是部分模塊調用時出錯,如果有多台服務器的話,一台一台地重啟還是可以的,前提是調用端得有自己的重試策略,比如使用Feign作為客戶端調用接口的話可以配置ribbon的重試策略,而且被調用方得做好冪等策略,防止重試調用時出現重復數據的問題。
2、向eureka 注冊中心發送delete 請求,只是取消注冊服務, 當發送心跳時還是會注冊到eureka
格式為 eureka地址/eureka/apps/服務名稱/實例名稱
請求方式為delete
下面是取消注冊一個服務的例子。
下圖是用postman 發送delete請求
3.通過eureka變更服務狀態的方式實現服務上下線,不會再發送心跳注冊到eureka server
4. 客戶端主動通知注冊中心下線,下線后不會再注冊到eureka了
如果你的eureka客戶端是是一個spring boot應用,可以通過調用以下代碼通知注冊中心下線。
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import com.netflix.discovery.DiscoveryManager; import com.shm.common.model.RespVO; import com.shm.common.util.RespUtil; @RestController public class OfflineController { @RequestMapping(value = "/offline", method = RequestMethod.GET) public RespVO<Object> offLine(){ DiscoveryManager.getInstance().shutdownComponent(); return RespUtil.success(); } }
5、設置服務的狀態,可通過狀態變更來實現再eureka的上下線
pom中加入
actuator的包
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
以上的方式都能實現服務的下線,但是有的時候只想要下線某個服務,卻不需要發布,等待事情處理完成后上線此服務,則以上方式就做不到了。這時可以通過設置微服務的狀態來完成此功能。項目中整合了actuator的話就非常簡單了,在項目啟動的時候可以看到控制台的輸出:
/actuator/service-registry 可以已Get的方式獲取當前服務的狀態,以Post的方式修改當前服務狀態,如將服務設置為Down狀態,這樣其他微服務接收到此狀態后將不調用此服務。將order服務狀態設置為Down:
看下執行后的效果:
等到流量都沒有進來后,需要發布的話直接發布接口,不需要發布可以直接上線當前服務:
這樣一個服務的上線和下線就優雅的完成了,如果項目中沒有使用Actuator框架,可以模仿Actuator框架的實現方式,詳見類:ServiceRegistryEndpoint
如果要再上線:
總結
以上幾種方式都可以實現微服的下線,3和5的方式最為優雅,可以主動下線和上線,在沒有新流量進來后可以隨時發布,這樣在也不用等到半夜12點發布了。