12、Netty-源码-shutdownGracefully[进行中]

关闭应用分为两种方式,一种方式强杀用户进程,导致应用没有机会保证正在处理工作的完整性,具体表现在丢失数据、数据损坏、数据重复等问题。那么我们关闭应用就应该采用稳定靠谱的方式关闭应用,等待应用完全处理完毕当前的任务并无新任务进来在关闭应用,这种方式叫优雅关机。 下面我们从JVM优雅关机慢慢过渡到Netty的优雅关机。

一、导致JVM关闭的原因

分析JVM关闭前,我们需要了解什么情况下JVM会关闭。

JVM强制关闭:系统关机[系统会通知JVM关闭,如果JVM关闭超时会强制关闭]、kill -9、Runtime.halt()、断电、系统宕机

JVM正常关闭:JVM关闭前会调用已注册的ShutdownHook(),我们可以将扫尾的工作放在shutdown hooks中使应用程序安全的退出。基于平台通用性的考虑推荐System.exit(0)方式退出JVM。

二、JVM优雅关机

方法一、Runtime.getRuntime().addShutdownHook()方法钩子

/**
 * 注册关闭钩子
 * */ 
 Runtime.getRuntime().addShutdownHook(new Thread(){
            @Override
            public void run() {
                // 在关闭钩子中执行收尾工作
                // 注意事项:
                // 1.在这里执行的动作不能耗时太久
                // 2.不能在这里再执行注册,移除关闭钩子的操作
                // 3 不能在这里调用System.exit()
                System.out.println("do shutdown hook");
            }
 });

  方法二、实现SignalHandler信号量判断

Signal sg = new Signal("TERM"); // kill -15 pid
// 监听信号量
Signal.handle(sg, new SignalHandler() {
            @Override
            public void handle(Signal signal) {
                System.out.println("signal handle: " + signal.getName());
                System.exit(0);
            }
});

钩子实现与信号量实现区别  

  •  第一种钩子方法在进程被kill -15 pid时候main函数就已经结束了,仅会运行shutdownHook中run()方法的代码。此方法不管是程序异常关闭还是外界主动关闭都会进入钩子执行。
  •  第二种信号量方法函数会在进程被kill -15 pid 会收到TERM信号,对main函数的运行不会有任何影响。我们需要在里面实现优雅关机的代码处理完毕等待程序关闭。此方法只会受到外界的信号量执行,程序自身的OOM、Exception都不会执行,所以只有第一种实现比较靠谱。Github JVM优雅关机实现

三、Netty优雅关机

Netty作为很多框架及高并发应用通讯模块的基础框架,往往应用或者中间件退出的时候,作为其中一部分的Netty也需要优雅退出,Netty在优雅退出的时候需要做到下面几点。

  • NIO线程退出、资源释放
  • 等待发送队列中的待发送消息发送完成
  • 正在读、写的消息需要继续处理
  • 设置在NioEventLoop 线程调度器中的定时任务,需要执行或者清理

下面我们看下 Netty 优雅退出涉及的主要操作和资源对象

下面我们通过调试Netty优雅退出分析其实现原理

通过调试分析Netty shutdownGracefully只能分析到boss、work线程池的关闭过程、唤醒Select队列处理正在进行的任务,但是无法分析出Channel释放过程及多路复用器的注册关闭等。shutdownGracefully是一个看起来简单,但是比较复杂的方法,此处TODO待后续分析。

//boss、work工作组关闭流程
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();

//第一步:AbstractEventExecutorGroup.java 初始化优雅关闭参数
@Override
public Future<?> shutdownGracefully() {
        return shutdownGracefully(DEFAULT_SHUTDOWN_QUIET_PERIOD, DEFAULT_SHUTDOWN_TIMEOUT, TimeUnit.SECONDS);}


//第二步:MultithreadEventExecutorGroup.java quietPeriod=2、timeout=15、unit=SECONDS
//EventExecutor本质是一个线程,工作组声明几个线程就会关闭几次
@Override
public Future<?> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) {
        for (EventExecutor l: children) {
            l.shutdownGracefully(quietPeriod, timeout, unit);
        }
        return terminationFuture();
}


//第三步:SingleThreadEventExecutor.java 关闭worke、boos线程组、唤醒Selecter线程继续处理正常进行的任务
@Override
    public Future<?> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) {
        if (quietPeriod < 0) {
            throw new IllegalArgumentException("quietPeriod: " + quietPeriod + " (expected >= 0)");
        }
        if (timeout < quietPeriod) {
            throw new IllegalArgumentException(
                    "timeout: " + timeout + " (expected >= quietPeriod (" + quietPeriod + "))");
        }
        if (unit == null) {
            throw new NullPointerException("unit");
        }

        //判断此线程词是否关闭,注意AtomicIntegerFieldUpdater<SingleThreadEventExecutor> STATE_UPDATER的精妙设计,为啥这样设计?
        if (isShuttingDown()) {
            return terminationFuture();
        }

        boolean inEventLoop = inEventLoop();
        boolean wakeup;
        int oldState;
        //自选方式标记线程池状态
        for (;;) {
            if (isShuttingDown()) {
                return terminationFuture();
            }
            int newState;
            wakeup = true;
            oldState = STATE_UPDATER.get(this);
            if (inEventLoop) {
                //修改boos线程池状态设置为不在接收状态
                newState = ST_SHUTTING_DOWN;
            } else {
                switch (oldState) {
                    case ST_NOT_STARTED:
                    case ST_STARTED:
                        newState = ST_SHUTTING_DOWN;
                        break;
                    default:
                        newState = oldState;
                        wakeup = false;
                }
            }
            if (STATE_UPDATER.compareAndSet(this, oldState, newState)) {
                break;
            }
        }
        //关闭等待时间单位转化
        gracefulShutdownQuietPeriod = unit.toNanos(quietPeriod);
        gracefulShutdownTimeout = unit.toNanos(timeout);

        if (oldState == ST_NOT_STARTED) {
            //关闭线程池
            scheduleExecution();
        }

        if (wakeup) {
            //唤醒柱塞在Selector.wakeup的线程
            wakeup(inEventLoop);
        }

        return terminationFuture();
    }

四、jvm和spring钩子区别

spring钩子就是spring销毁坚挺函数,其特点是依赖spring环境,更多用于spring环境的项目中间件.JVM理论可以用于任何java环境.不过如果项目是spring构建的推荐使用spring钩子.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Netty-socketio是一个用于构建实时通信应用程序的开源框架。它基于Netty框架,通过使用WebSocket协议来实现异步、高性能的网络通信。Netty-socketio具有以下特点: 1. 强大的异步处理能力:Netty-socketio采用事件驱动的方式处理客户端请求,能够高效地处理大量并发连接。 2. 完善的消息传递机制:Netty-socketio提供了灵活的消息传递方式,支持广播、点对点、房间等不同的消息发送方式,满足不同场景下的通信需求。 3. 多协议支持:Netty-socketio不仅支持WebSocket协议,还支持其他常用的协议,如TCP、HTTP等,便于与现有的系统集成。 4. 可扩展性强:Netty-socketio提供了丰富的拓展接口,用户可以根据自己的需求定制和扩展框架的功能。 5. 易于使用:Netty-socketio提供了简洁的API和丰富的文档,可以快速上手使用,并提供了相应的示例代码,方便开发者学习和理解。 对于客服应用来说,Netty-socketio作为一个实时通信框架,可以用于构建在线客服聊天系统。通过使用Netty-socketio,我们可以实现客户与客服人员之间的实时消息传递,支持文字、图片、文件等多种类型的消息。客户可以通过网页或移动端应用与客服人员进行沟通,实时解决问题,提升用户体验。 Netty-socketio提供了强大的异步处理能力和全双工通信机制,能够处理大量并发连接,并保持连接的稳定性和可靠性。同时,它的多协议支持和可扩展性强的特点,使得我们可以根据自己的业务需求进行定制和拓展,满足不同客服场景下的通信需求。 总之,Netty-socketio作为一个强大的实时通信框架,为客服应用提供了一种高效、稳定的解决方案,帮助企业构建更好的客服系统,并提升客户的满意度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值