java 优雅停机

什么是优雅停机

让应用退出前,通知 JVM 先不再接收新的消息,但正在处理的任务要正常完成,完成特定资源清理,之后才退出应用,这就是优雅关机(graceful shutdown)的概念

Java 语言中,我们可以通过 Runtime.getRuntime().addShutdownHook(Thread hook):这个方法的意思就是在jvm中增加一个关闭的钩子,当jvm关闭的时候,会执行系统中已经设置的所有通过方法addShutdownHook添加的钩子,当系统执行完这些钩子后,jvm才会关闭。所以这些钩子可以在jvm关闭的时候进行内存清理、对象销毁等操作。

sringboot 实现

1 tomcat配置

//tomcat链接配置1
import org.apache.catalina.connector.Connector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.cloud.zookeeper.serviceregistry.ZookeeperRegistration;
import org.springframework.cloud.zookeeper.serviceregistry.ZookeeperServiceRegistry;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * springboot优雅关闭
 */
public class GracefulShutdownTomcatConnectorCustomizer implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {
    private final Logger log = LoggerFactory.getLogger(GracefulShutdownTomcatConnectorCustomizer.class);
    private volatile Connector connector;

   
    private int timeout=30;

   
    private int wait=30;

    private boolean isShutdown;

    @Override
    public void customize(Connector connector) {
        this.connector = connector;
    }

    /**
     * 服务下线,没有使用zookeeper的可以忽略
     * @param contextClosedEvent
     */
   /* private void offline(ContextClosedEvent contextClosedEvent){
        try{
            ZookeeperServiceRegistry registry = contextClosedEvent.getApplicationContext().getBean(ZookeeperServiceRegistry.class);
            ZookeeperRegistration registration = contextClosedEvent.getApplicationContext().getBean(ZookeeperRegistration.class);
            registry.deregister(registration);
            log.info("[GracefulShutdown] service zookeeper deregister success. id={},name={},address={},port={}.",
                    registration.getServiceInstance().getId(),registration.getServiceInstance().getName(),
                    registration.getServiceInstance().getAddress(),registration.getServiceInstance().getPort());
        }catch(Exception e){
            log.error("[GracefulShutdown] service zookeeper deregister error.",e);
        }
    }*/

    /**
     * tomcat暂停接收
     */
    private void pause(){
        try{
            Thread.sleep(wait*1000);
            log.info("[GracefulShutdown] service pause. wait={}.",wait);
            this.connector.pause();
        }catch(Exception e){
            log.error("[GracefulShutdown] connector pause error.",e);
        }
    }

    @Override
    public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
        if(isShutdown){
            return;
        }
        this.offline(contextClosedEvent);

        if (this.connector == null) {
            isShutdown = true;
            return;
        }

        this.pause();
        Executor executor = this.connector.getProtocolHandler().getExecutor();
        if (executor instanceof ThreadPoolExecutor) {
            try {
                log.info("[GracefulShutdown] tomcat begin shutdown. timeout={}.",timeout);
                ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
                threadPoolExecutor.shutdown();
                if (!threadPoolExecutor.awaitTermination(timeout, TimeUnit.SECONDS)) {
                    log.info("[GracefulShutdown] tomcat is not termination. timeout={}.",timeout);
                }
                log.info("[GracefulShutdown] tomcat is shutdown success. timeout={}.",timeout);
            } catch (InterruptedException ex) {
                log.error("[GracefulShutdown] application Event error.",ex);
                Thread.currentThread().interrupt();
            }
        }
        isShutdown = true;
    }
}

//tomcat容器配置2
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;

public class GracefulShutdownTomcatContainerCustomizer
        implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

    private final GracefulShutdownTomcatConnectorCustomizer connectorCustomizer;

    public GracefulShutdownTomcatContainerCustomizer(GracefulShutdownTomcatConnectorCustomizer connectorCustomizer) {
        this.connectorCustomizer = connectorCustomizer;
    }

    @Override
    public void customize(TomcatServletWebServerFactory factory) {
        factory.addConnectorCustomizers(connectorCustomizer);
    }
}


2 停机配置

import shutdown.tomcat.GracefulShutdownTomcatConnectorCustomizer;
import shutdown.tomcat.GracefulShutdownTomcatContainerCustomizer;

import javax.servlet.Servlet;

import org.apache.catalina.startup.Tomcat;
import org.apache.coyote.UpgradeProtocol;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@ConditionalOnWebApplication
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@Configuration
public class GracefulShutdownAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public GracefulShutdownTomcatContainerCustomizer gracefulShutdownTomcatContainerCustomizer(
        GracefulShutdownTomcatConnectorCustomizer connectorCustomizer) {
        return new GracefulShutdownTomcatContainerCustomizer(connectorCustomizer);
    }

    @Bean
    @ConditionalOnMissingBean
    public GracefulShutdownTomcatConnectorCustomizer gracefulShutdownTomcatConnectorCustomizer() {
        return new GracefulShutdownTomcatConnectorCustomizer();
    }

}

定义钩子函数

import javax.annotation.PostConstruct;

@Component
public class ShutDownConfig {

    public static boolean healthStatus = true;

    @PostConstruct
    public void init() {
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                try {
                    // 修改checkHeal状态码
                    healthStatus = false;
                    // 这里设置等待20秒,让clb检测状态后拒绝请求
                    Thread.sleep(20000);
                    close();//关闭资源
                    Thread.sleep(20000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

    }

//把所有关闭资源的close放在这,才能做到程序全部停止前,已经进入的请求可以正常执行
    public void close() {
        //调用使用的资源关闭close()接口

    }
}

cli接口

@RestController
public class HealthController {
    @Autowired
    private IUserDao userDao;



/*
@Mapper
public interface IUserDao {
@Select("SELECT now()")
    String health();
}
*/

    @GetMapping("/checkHealth")
    public Result<String> check() {
        try {
            userDao.health();
            if (!serviceStatus) {
                throw new ServiceException("application is unhealthy");
            }
            return Result.ofSuccess("ok");
        } catch (Exception e) {
            throw new ServiceException("application is unhealthy", e);
        }
    }


}

测试

启动代码,jps查找当前应用的pid, 使用kill -15 pid,然后在postman 调用接口

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值