IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 服务版本发布问题(spring的bean生命周期控制) -> 正文阅读

[Java知识库]服务版本发布问题(spring的bean生命周期控制)


问题:

公司报警群,在项目上线时,会多出一些redis连接异常,在项目系统日志查到,多数错误信息,如下:

redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool

根据对应的报错类行中找出,其报错是mq消费者Consumer,在消费mq代码逻辑用到redis,大多报出上述redis线程池无连接错误。

分析:

出现问题,肯定是服务关闭时,redis线程池已经销毁了。查阅代码:


redis客户端的销毁方式

    @PreDestroy
    public void destroy() {
        if (this.scheduler != null) {
            this.scheduler.shutdown();
        }

    }

consumer客户端的销毁方式

    @Bean(
        destroyMethod = "shutdown"
    )
    @ConditionalOnMissingBean
    public MQConsumerArray messageQueueConsumer(MqConfigArray mqConfigArray) {

        。。。
        return mqConsumerArray;
    }

从上面代码看出,redis和consumer都是依赖spring管理bean的生命周期,进行管理的。首先想到,可不可以控制下,spring的bean销毁顺序来进行,或者先销毁consumer客户端,再处理其他bean的声明周期呢?

@DependsOn注解

之前了解到@DependsOn注解,可以控制bean之间的依赖,进而控制bean的创建顺序以及销毁顺序。

上手测试一下,很久没用这个注解了,上代码:

Person类

public class Person {
    private int age;
    private String name;
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    @PreDestroy
    public void destroy(){
        System.out.println("Person.destroy");
    }
    public void shutdown(){
        System.out.println("Person.shutdown");
    }
}

Student类

public class Student {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @PreDestroy
    public void destroy(){
        System.out.println("Student.destroy");
    }
    public void shutdown(){
        System.out.println("Student.shutdown");
    }
}

ConfigBean类

@Configuration
public class ConfigBean {
    @DependsOn("student")
    @Bean(destroyMethod = "shutdown")
    @ConditionalOnMissingBean
    public Person buildPerson(){
        System.out.println("Person.build");
        return new Person();
    }
    @Bean(name = "student",destroyMethod = "shutdown")
    @ConditionalOnMissingBean
    public Student buildStudent(){
        System.out.println("Student.build");
        return new Student();
    }
}

?启动项目:

关闭项目:

??

?注释掉ConfigBean的@DependsOn的使用后:

启动项目:

关闭项目:

?

?从上述实践可以看出,@DependsOn可以通过依赖关系控制促使bean之间生命周期的顺序。但是,redis和consumer客户端都是基础架构提供的,在spring实例化对应的bean时,没有对应的实例名称,所以@DependsOn用不上。

提前销毁Consumer客户端

如果控制不了redis和consumer之间的生命周期顺序,单一先销毁consumer也可以。
先看下,spring关闭方法的源码org.springframework.context.support.AbstractApplicationContext#registerShutdownHook
?

    public void registerShutdownHook() {
        if (this.shutdownHook == null) {
            this.shutdownHook = new Thread("SpringContextShutdownHook") {
                public void run() {
                    synchronized(AbstractApplicationContext.this.startupShutdownMonitor) {
                        AbstractApplicationContext.this.doClose();
                    }
                }
            };
            Runtime.getRuntime().addShutdownHook(this.shutdownHook);
        }

    }
    protected void doClose() {
        if (this.active.get() && this.closed.compareAndSet(false, true)) {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Closing " + this);
            }

            LiveBeansView.unregisterApplicationContext(this);
            //发布上下文关闭事件
            try {
                this.publishEvent((ApplicationEvent)(new ContextClosedEvent(this)));
            } catch (Throwable var3) {
                this.logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", var3);
            }

            if (this.lifecycleProcessor != null) {
                try {
                    this.lifecycleProcessor.onClose();
                } catch (Throwable var2) {
                    this.logger.warn("Exception thrown from LifecycleProcessor on context close", var2);
                }
            }
            //销毁bean
            this.destroyBeans();
            this.closeBeanFactory();
            this.onClose();
            if (this.earlyApplicationListeners != null) {
                this.applicationListeners.clear();
                this.applicationListeners.addAll(this.earlyApplicationListeners);
            }

            this.active.set(false);
        }

    }

从spring关闭的过程中可以看到,spring利用了JVM虚拟机关闭的钩子。

    public void addShutdownHook(Thread hook) {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new RuntimePermission("shutdownHooks"));
        }
        ApplicationShutdownHooks.add(hook);
    }

解决办法

上述分析可以知道,spring关闭事件发布,早于bean的销毁。可以利用关闭事件监听器提前处理需要的逻辑。鉴于这次只是解决consumer客户端没提前结束的问题。有两种选择:

1.在监听到spring关闭事件的同时,利用consumer提供的shutdown接口关闭consumer。

2.在监听到spring关闭事件之后,对mq的消息接收做拦截。

从解决方案上分析,都可以,考虑到处理问题的可控影响,选择第二种方案。代码如下:
?

/**
 * 拦截mq容器类
 */
@Component
public class MqInterceptContainer implements ApplicationListener<ContextClosedEvent> {
    /**
     * 系统活跃标识 0关闭;1活跃
     */
    public volatile static boolean activeCode = true;

    /**
     * 判断系统活跃 活跃返回true,关闭返回false
     * @return
     */
    public static boolean isActive(){
        return activeCode;
    }

    @Override
    public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
        activeCode = false;
    }
}

再需要拦截的地方用MqInterceptContainer.isActive()即可。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-04-06 16:04:04  更:2022-04-06 16:05:49 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 7:45:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码