jvm shutdownHook + spring 自定义事件实现业务处理

jvm 的shutdownHook 可以实现对于jvm 退出的一些处理,比如资源清理,异常事件通知,spring 自定义事件(或者使用内部的)可以实现
bean 的一些事件驱动处理,两个结合起来可以方便我们进行一些业务处理

一些业务场景

  • 资源清理
  • 服务停止业务状态一致性补偿
  • 服务注册场景中的取消注册
  • 服务停止业务处理

简单使用

  • 项目结构
├── HELP.md
├── README.md
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
    ├── main
    ├── java
    └── com
    └── dalong
    └── shutdowndemo
    ├── Api.java
    ├── BizA.java
    ├── BizB.java
    ├── BizC.java
    ├── EventHook.java
    ├── MyEvent.java
    └── ShutdowndemoApplication.java
    └── resources
    ├── application.properties
    ├── static
    └── templates
  • 代码简单说明
    ShutdowndemoApplication为spring boot 的入口,Api业务接口(rest api 入口),BizA,BizB,BizC 是业务服务,
    EventHook 一个spring 系统的事件处理,MyEvent 自定义事件
  • ShutdowndemoApplication.java
@SpringBootApplication
public class ShutdowndemoApplication {
    private  static ApplicationContext applicationContext;
    public static void main(String[] args) {
        applicationContext= SpringApplication.run(ShutdowndemoApplication.class, args);
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            System.out.println("Shutting down");
            MyEvent myEvent = new MyEvent("1.0.0");
            myEvent.setName("dalong");
            myEvent.setVersion("1.0.0");
            applicationContext.publishEvent(myEvent);
        } ));
    }
}

Api.java 主要是调用业务服务,同时实现了spring 系统事件,可以进行一些处理

@RestController
public class Api implements ApplicationListener<ContextClosedEvent> {
 
    @Autowired
    private BizA bizA;
 
    @Autowired
    private BizB bizB;
 
 
    @Autowired
    private BizC bizC;
 
    @GetMapping(value = {"/demo"})
    public String demo() {
        String a = bizA.acitonA();
        String b = bizB.actionB();
        String c = bizC.actionC();
        return a + b + c;
    }
 
    private void doClean() {
        try {
            System.out.println("Api doClean start");
            Thread.sleep(3000);
            System.out.println("Api doClean end");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
 
    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
        System.out.println("Api onApplicationEvent Shutting down");
        doClean();
    }
}

BizA.java (其他的b,c 都比较类似)

@Service
public class BizA implements ApplicationListener<ContextClosedEvent> {
    public String doBiz() {
        try {
            System.out.println("doBizA biz start");
            Thread.sleep(6000);
            System.out.println("doBizA biz end");
            return "doBizA biz end";
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
 
    public String acitonA() {
        return "actionA";
    }
 
    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
        System.out.println("BizA onApplicationEvent Shutting down");
        doBiz();
    }
}

EventHook.java

@Component
public class EventHook implements ApplicationListener<ContextClosedEvent> {
    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
        try {
            System.out.println("onApplicationEvent Shutting down");
            Thread.sleep(1000);
            System.out.println("onApplicationEvent biz end");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
 
    @Override
    public boolean supportsAsyncExecution() {
        return ApplicationListener.super.supportsAsyncExecution();
    }
}

MyEvent.java

public class MyEvent extends ApplicationEvent {
    private String name;
    private String version;
 
    public MyEvent(Object source) {
        super(source);
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getVersion() {
        return version;
    }
 
    public void setVersion(String version) {
        this.version = version;
    }
}

运行效果

如果实际我们执行kill <jvm pid>,效果如下

说明

以上只是一个简单的演示,实际上如果我们想更好的基于jvm shutdownHook + spring 进行一些业务系统的处理,比如业务务感知的业务滚动升级处理,实际上有不少东西要处理,比如服务的prestop 阶段的的处理,同时业务请求流量的处理,业务一致性的保证,但是基于spring 以及jvm 提供的一些
完整测试代码我已经push 到github 了可以参考
能力可以简化我们的实际实现

参考资料

https://docs.oracle.com/javase/8/docs/technotes/guides/lang/hook-design.html
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/ApplicationListener.html
https://www.baeldung.com/spring-events
https://www.baeldung.com/spring-boot-enable-disable-endpoints-at-runtime

posted on 2024-02-14 08:00  荣锋亮  阅读(4)  评论(0编辑  收藏  举报

导航