使用了注冊中心的服務(wù)集群净嘀,各個業(yè)務(wù)節(jié)點通過RestTemplate、openFeign進行內(nèi)部調(diào)用時,或者gateway做服務(wù)轉(zhuǎn)發(fā)時吮播,一般會使用從注冊中心獲取的服務(wù)節(jié)點列表進行負載路由,而當(dāng)一個服務(wù)節(jié)點被暴力關(guān)閉時眼俊,從注冊中心檢測到節(jié)點處在不健康狀態(tài)意狠,到注冊中心將該節(jié)點從維護列表中移除,再到其他業(yè)務(wù)節(jié)點將失效節(jié)點從自身維護的節(jié)點列表緩存數(shù)據(jù)中移除(定時拉取或廣播通知方式)疮胖,中間一般會經(jīng)歷數(shù)秒時長环戈,這時如果有轉(zhuǎn)向失效節(jié)點的新請求闷板,就會不得不等滿一個timeout周期,然后再反饋回調(diào)用端去做下一步是重試還是標(biāo)記熔斷的處理院塞≌谕恚總之,有異常發(fā)生拦止,不優(yōu)雅县遣。
(假裝有流程圖)
現(xiàn)成的有不少優(yōu)雅關(guān)閉服務(wù)的方案,比如使用actuator的shutdown端點汹族、提供入口調(diào)用SpringApplication的exit()靜態(tài)方法等艺玲,不過重要的是在做這些之前要能先主動的將節(jié)點從注冊中心中下線,然后等待一個合適的時間段之后再關(guān)閉服務(wù)鞠抑,這樣能多給正在處理中的請求執(zhí)行完成的機會饭聚。
下面是使用了nacos的服務(wù)下線示例代碼:
/** 使用了nacos注冊中心的服務(wù)關(guān)閉端點配置 */
@ConditionalOnClass(NacosAutoServiceRegistration.class)
@RestController
@RequestMapping("actuator")
@RequiredArgsConstructor
@Slf4j
public class NacosStopEndpoint {
private final NacosAutoServiceRegistration nacosAutoServiceRegistration;
private final ApplicationContext context;
/** 注銷服務(wù)后關(guān)閉應(yīng)用前等待的時間(毫秒) */
@Value("${stopService.waitTime:10000}")
private int waitTime;
/**
* 關(guān)閉服務(wù) <br>
* 只接收localhost發(fā)起的請求
*
* @param request
* @return
*/
@PostMapping("stopService")
public ResponseEntity<Boolean> stopNacosService(
HttpServletRequest request) {
if (!request.getServerName().equalsIgnoreCase("localhost"))
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(false);
new Thread(
() -> {
log.info("Ready to stop service");
nacosAutoServiceRegistration.stop();
log.info("Nacos instance has been de-registered");
log.info("Waiting {} milliseconds...", waitTime);
try {
Thread.sleep(waitTime);
} catch (InterruptedException e) {
log.info("interrupted!", e);
}
log.info("Closing application...");
SpringApplication.exit(context);
((ConfigurableApplicationContext) context).close();
})
.start();
return ResponseEntity.ok(true);
}
}
這段代碼提供了一個 /actuator/stopService
的POST端點,調(diào)用它即可優(yōu)雅將服務(wù)下線搁拙,先把自身從nacos注冊中心中移除秒梳,等待10秒后關(guān)閉spring應(yīng)用。
接下來箕速,還需要找一個合適的時機來調(diào)用這個接口酪碘。如果使用了k8s,可以在容器生命周期中的prestop階段配置一個執(zhí)行腳本:
lifecycle:
preStop:
exec:
command:
- /bin/sh
- '-c'
- 'curl -X POST ''http://localhost:8080/actuator/stopService'';\'
- sleep 30;
其中的sleep時間可以配置的比下線節(jié)點之后的等待時間稍長一些即可盐茎。