26. 后台程序如何优雅的退出
一. 前言
项目初期我们可以使用kill -9 pid的方法来杀死后台服务进程,然后重新部署。
但是随着时间发展,这种简单粗暴的方法会有一些问题:
如何在退出时清理一些资源?
如果某个请求执行操作到一半,直接被退出了,就会造成脏数据。
如何给客户端正确的反馈?
二. Java虚拟机退出钩子
虚拟机允许在退出的时候执行钩子程序,那么我们可以在这个方法里面作一些释放资源的操作,如下:
System.out.println("addhook...");ThreadshutdownThread=newThread(newRunnable(){@Overridepublicvoidrun(){System.out.println("hookrunning.."+System.currentTimeMillis());try{Thread.sleep(10000);}catch(InterruptedExceptione){//TODOAuto-generatedcatchblocke.printStackTrace();}System.out.println("hooksleepdone..."+System.currentTimeMillis());}});Runtime.getRuntime().addShutdownHook(shutdownThread);
三. tomcat的unloadDelay属性
这个属性的意思是tomcat留给请求执行的时间,默认只有2秒,也就是执行./shundown.sh之后,请求处理只有2秒的时间。如果没有及时返回给客户端,那么就会返回客户端503错误。
apache-tomcat-9.0.0.M22-src/java/org/apache/catalina/core/StandardWrapper.java
/***Waittimeforservletunloadinms.*/protectedlongunloadDelay=2000;publicsynchronizedvoidunload()throwsServletException{//Nothingtodoifwehaveneverloadedtheinstanceif(!singleThreadModel&&(instance==null)){log.info("instancerun...");return;}unloading=true;log.info("countAllocated.get():"+countAllocated.get());//Loafawhileifthecurrentinstanceisallocated//(possiblymorethanonceifnon-STM)if(countAllocated.get()>0){intnRetries=0;longdelay=unloadDelay/20;System.out.println("unloadDelay:"+unloadDelay);log.info("unloadDelay2222:"+unloadDelay);while((nRetries<21)&&(countAllocated.get()>0)){if((nRetries%10)==0){log.info(sm.getString("standardWrapper.waiting",countAllocated.toString(),getName()));}try{Thread.sleep(delay);}catch(InterruptedExceptione){//Ignore}nRetries++;}}}
这段代码它会把unloadDelay分成20份,然后循环20次检查请求是否已经结束。
unloadDelay默认是2000ms,也就是2秒钟.
这样写的好处是即使配置的unloadDelay时间很长,但是如果请求很快结束,那么它也不会等待配置的unloadDelay时间,它可以提前退出服务。
如果请求在unloadDelay时间内处理完请求,那么客户端就可以接受到正常的结果。
四. unloadDelay配置
在tomcat的conf目录下的context.xml中可以配置,如下:
<ContextunloadDelay="20000">...</Context>
五. springboot发送http请求关闭服务
在pom.xml中配置
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency>
在application.properties中配置
#启用shutdownendpoints.shutdown.enabled=true#禁用密码验证endpoints.shutdown.sensitive=false
发送http关闭命令
curl-XPOSThost:port/shutdown
得到的返回消息如下:
{"message":"Shuttingdown,bye..."}
发送http消息后,它会拒绝新的请求,2秒内让已有请求执行完毕,清理资源。
六. 权限
如果没有权限,那么任何人发送curl -X POST host:port/shundown都可以关闭服务。
所以,需要添加权限
1.pom.xml中添加申明
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency>
spring-boot-starter-actuator的作用参考这篇帖子:
Springboot startr作用详解
2.appliaction.properties文件中添加用户名和密码
endpoints.shutdown.sensitive=truesecurity.user.name=adminsecurity.user.password=adminmanagement.security.role=SUPERUSER
3.发起的http指令中携带用户名和密码
curl-uadmin:admin-XPOSThttp://host:port/shutdown
4.不过这样会导致正常的请求也需要用户名和密码,所以application.properties还得再加一句:
security.basic.enabled=false
5.IP地址验证
如果想另外加上IP地址验证,那么全部配置如下:
endpoints.shutdown.enabled=trueendpoints.shutdown.sensitive=truesecurity.basic.enabled=false#不影响正常请求security.user.name=adminsecurity.user.password=adminmanagement.security.role=SUPERUSERmanagement.port=3081#管理的端口,需要和server.port不同(正常的请求端口)management.address=127.0.0.1#只允许本机发送httpshutdown命令
发送的指令如下:
curl -uadmin:admin -X POST http://localhost:3081/shutdown
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。