日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
SpringBoot 優(yōu)雅停機(jī)的正確姿勢(shì)

?一、介紹

什么叫優(yōu)雅停機(jī)?

為新羅等地區(qū)用戶提供了全套網(wǎng)頁設(shè)計(jì)制作服務(wù),及新羅網(wǎng)站建設(shè)行業(yè)解決方案。主營業(yè)務(wù)為成都網(wǎng)站制作、成都網(wǎng)站設(shè)計(jì)、外貿(mào)營銷網(wǎng)站建設(shè)、新羅網(wǎng)站設(shè)計(jì),以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠的服務(wù)。我們深信只要達(dá)到每一位用戶的要求,就會(huì)得到認(rèn)可,從而選擇與我們長(zhǎng)期合作。這樣,我們也可以走得更遠(yuǎn)!

簡(jiǎn)單的說,就是向應(yīng)用進(jìn)程發(fā)出停止指令之后,能保證正在執(zhí)行的業(yè)務(wù)操作不受影響,直到操作運(yùn)行完畢之后再停止服務(wù)。應(yīng)用程序接收到停止指令之后,會(huì)進(jìn)行如下操作:

  • 1.停止接收新的訪問請(qǐng)求
  • 2.正在處理的請(qǐng)求,等待請(qǐng)求處理完畢;對(duì)于內(nèi)部正在執(zhí)行的其他任務(wù),比如定時(shí)任務(wù)、mq 消費(fèi)等等,也要等當(dāng)前正在執(zhí)行的任務(wù)執(zhí)行完畢,并且不再啟動(dòng)新的任務(wù)
  • 3.當(dāng)應(yīng)用準(zhǔn)備關(guān)閉的時(shí)候,按需向外發(fā)出信號(hào),告知其他應(yīng)用服務(wù)準(zhǔn)備接手,以保證服務(wù)高可用

如果暴力的關(guān)閉應(yīng)用程序,比如通過kill -9 命令強(qiáng)制直接關(guān)閉應(yīng)用程序進(jìn)程,可能會(huì)導(dǎo)致正在執(zhí)行的任務(wù)數(shù)據(jù)丟失或者錯(cuò)亂,也可能會(huì)導(dǎo)致任務(wù)所持有的全局資源等不到釋放,比如當(dāng)前任務(wù)持有 redis 的鎖,并且沒有設(shè)置過期時(shí)間,當(dāng)任務(wù)突然被終止并且沒有主動(dòng)釋放鎖,會(huì)導(dǎo)致其他進(jìn)程因無法獲取鎖而不能處理業(yè)務(wù)。

那么如何在不影響正在執(zhí)行的業(yè)務(wù)的情況下,將應(yīng)用程序安全的進(jìn)行關(guān)閉呢?

二、方案實(shí)踐

SpringBoot 官方文檔上,已經(jīng)告訴開發(fā)者只需要實(shí)現(xiàn)特定接口即可監(jiān)聽到項(xiàng)目啟動(dòng)成功與關(guān)閉時(shí)的事件,相關(guān)接口如下:

  • CommandLineRunner?接口:當(dāng)應(yīng)用啟動(dòng)成功后但在開始接受流量之前,會(huì)回調(diào)此接口的實(shí)現(xiàn)類,也可以實(shí)現(xiàn)ApplicationRunner接口,工作的方式與CommandLineRunner與之類似
  • DisposableBean?接口:當(dāng)應(yīng)用正要被銷毀前,會(huì)回調(diào)此接口的實(shí)現(xiàn)類,也可以使用@PreDestroy注解,被標(biāo)記的方法也會(huì)被調(diào)用

基于此流程,我們可以創(chuàng)建一個(gè)服務(wù)監(jiān)聽類,用于監(jiān)聽到項(xiàng)目啟動(dòng)成功與關(guān)閉時(shí)的回調(diào)服務(wù),示例代碼如下:

@Component
public class AppListener implements CommandLineRunner, DisposableBean {

@Override
public void run(String... args) throws Exception {
System.out.println("應(yīng)用啟動(dòng)成功,預(yù)加載相關(guān)數(shù)據(jù)");
}

@Override
public void destroy() throws Exception {
System.out.println("應(yīng)用正在關(guān)閉,清理相關(guān)數(shù)據(jù)");
}

}

每一個(gè)SpringApplication?在啟用的時(shí)候,都會(huì)向 JVM 注冊(cè)一個(gè)關(guān)閉鉤子shutdown hook?,以確保ApplicationContext在退出的時(shí)候,通過這個(gè)勾子通知 JVM,實(shí)現(xiàn)服務(wù)正常的關(guān)閉,以下介紹的所有關(guān)閉服務(wù)的方法,都是基于這一原理進(jìn)行實(shí)現(xiàn)的。

2.1、方法一:通過Actuator的Endpoint機(jī)制關(guān)閉服務(wù)

使用此方法,需要先添加spring-boot-starter-actuator監(jiān)控服務(wù)依賴包,


org.springframework.boot
spring-boot-starter-actuator

默認(rèn)配置下,shutdown?端點(diǎn)是關(guān)閉的,需要在application.properties里配置里面開啟:

management.endpoint.shutdown.enabled=true

雖然Actuator?的端點(diǎn),支持通過JMX或HTTP?進(jìn)行遠(yuǎn)程訪問。而shutdown?默認(rèn)配置下是不支持HTTP?進(jìn)行Web?訪問的,所以使用HTTP請(qǐng)求進(jìn)行關(guān)閉時(shí)的配置,也需要開啟:

management.endpoints.web.exposure.include=shutdown

最后將SpringBoot?服務(wù)啟動(dòng)之后,使用POST請(qǐng)求類型,調(diào)用以下接口,即可實(shí)現(xiàn)關(guān)閉服務(wù)!

http://127.0.0.1:8080/actuator/shutdown

2.2、方法二:使用ApplicationContext的close方法關(guān)閉服務(wù)

如果你不想添加spring-boot-starter-actuator?監(jiān)控服務(wù)依賴包來關(guān)停服務(wù),也可以使用ApplicationContext的close?方法來關(guān)停服務(wù),他會(huì)自動(dòng)銷毀bean對(duì)象并關(guān)停服務(wù)。

只需要在應(yīng)用啟用的時(shí)候,獲取ApplicationContext?對(duì)象,然后在相關(guān)的位置調(diào)用close方法,就可以關(guān)閉服務(wù)。

示例代碼如下:

@SpringBootApplication
public class Application {

public static void main(String[] args){
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);

try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//啟動(dòng)10秒以后,自動(dòng)關(guān)閉
context.close();
}
}

當(dāng)然我們也可以自己寫一個(gè)Controller?,獲取對(duì)應(yīng)的ApplicationContext?對(duì)象,通過api?操作調(diào)用close方法關(guān)停服務(wù),示例代碼如下:

@RestController
public class ShutdownController implements ApplicationContextAware {

private ApplicationContext context;

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}

/**
* 關(guān)閉服務(wù)
*/
@GetMapping("/shutdown")
public void shutdownContext(){
((ConfigurableApplicationContext) context).close();
}
}

2.3、方法三:監(jiān)聽服務(wù)pid,通過kill方式關(guān)閉服務(wù)

通過api方式來關(guān)停服務(wù),在很多人看來并不安全,因?yàn)橐坏┙涌谛孤┝?,意味著用戶可以隨便請(qǐng)求這個(gè)接口來關(guān)閉服務(wù),其影響不言而喻,因此很多人建議在服務(wù)端,通過其他的方式來關(guān)閉服務(wù),比如通過進(jìn)程命令方式來關(guān)停。

在springboot?啟動(dòng)的時(shí)候?qū)?yīng)用進(jìn)程 ID 寫入一個(gè)app.pid文件,生成的路徑可以指定,然后通過腳本命令方式來關(guān)閉服務(wù)。

啟動(dòng)示例代碼如下:

@SpringBootApplication
public class Application {

public static void main(String[] args){
SpringApplication application = new SpringApplication(Application.class);
application.addListeners(new ApplicationPidFileWriter("/home/app/project1/app.pid"));
application.run();
}
}

通過如下命令方式,可以安全的關(guān)閉服務(wù)。

cat /home/app/project1/app.pid | xargs kill

這種方式,也是目前在linux操作系統(tǒng)中,使用較為普遍的一種解決方案,區(qū)別在于實(shí)現(xiàn)的方式可能不同,有的不用寫文件,通過其他方式來獲取應(yīng)用進(jìn)程 ID。

如果使用?kill -9 的方式關(guān)閉服務(wù),服務(wù)的監(jiān)聽器不會(huì)收到任何消息,類似于直接強(qiáng)殺應(yīng)用進(jìn)程,此方法不可??!

2.4、方法四:使用SpringApplication的exit方法關(guān)閉服務(wù)

通過調(diào)用一個(gè)SpringApplication.exit()?方法也可以安全的退出程序,同時(shí)會(huì)返回一個(gè)退出碼,這個(gè)退出碼可以傳遞給所有的context?,最后通過調(diào)用System.exit()?可以將這個(gè)錯(cuò)誤碼也傳給JVM。

示例代碼如下:

@SpringBootApplication
public class Application {

public static void main(String[] args){
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);

try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
//5秒后,關(guān)閉服務(wù)
exitApplication(context);
}

public static void exitApplication(ConfigurableApplicationContext context){
//獲取退出碼
int exitCode = SpringApplication.exit(context, (ExitCodeGenerator) () -> 0);
//退出碼傳遞給jvm,安全退出程序
System.exit(exitCode);
}

}

三、其他監(jiān)聽介紹

3.1、ApplicationListener

如果有些服務(wù),比如定時(shí)任務(wù),我們想在SpringBoot?關(guān)閉數(shù)據(jù)源連接池之前,將其關(guān)閉,可以通過實(shí)現(xiàn)ApplicationListener?接口,監(jiān)聽bean?對(duì)象的變化情況,在bean對(duì)象銷毀之前,執(zhí)行相關(guān)的關(guān)閉任務(wù)。

示例代碼如下:

@Component
public class JobTaskListener implements ApplicationListener {

@Override
public void onApplicationEvent(ApplicationEvent applicationEvent){
// 在spring bean容器銷毀之前執(zhí)行的事件,防止數(shù)據(jù)庫連接池在任務(wù)終止前銷毀
if (applicationEvent instanceof ContextClosedEvent) {
System.out.println("關(guān)閉相關(guān)的定時(shí)任務(wù)");
}
}
}

3.2、PreDestroy

上文中,我們提到了實(shí)現(xiàn)DisposableBean?接口,可以監(jiān)聽?wèi)?yīng)用關(guān)閉前的回調(diào)處理,其實(shí)在自定義的方法上加@PreDestroy注解,也可以實(shí)現(xiàn)相同的效果。

示例代碼如下:

@Component
public class AppDestroyConfig {

@PreDestroy
public void PreDestroy(){
System.out.println("應(yīng)用程序正在關(guān)閉。。。");
}
}

四、小結(jié)

本位主要圍繞如何安全的關(guān)閉SpringBoot服務(wù),進(jìn)行了一些方案操作的介紹,如果有疏漏的地方,歡迎網(wǎng)友批評(píng)指出!


網(wǎng)頁名稱:SpringBoot 優(yōu)雅停機(jī)的正確姿勢(shì)
標(biāo)題URL:http://www.5511xx.com/article/dhocesc.html