【mq】从零开始实现 mq-05-实现优雅停机
前景回顾
【mq】从零开始实现 mq-02-如何实现生产者调用消费者?
【mq】从零开始实现 mq-03-引入 broker 中间人
为什么需要优雅关闭?
我记得多年前,那个时候 rpc 框架主流用的还是 dubbo,每次都是半夜还是上线,上线上完基本都是凌晨 2-3 点。
为什么要半夜上线呢?
因为这个时候一般业务流量最低。
还有就是上线发布,每次都要人工等待一段几分钟。
因为 rpc 调用入口已经关闭了,但是本身可能还没有处理完。
那么有没有方法可以让服务的关闭更加优雅,而不是人工等待呢?
实现思路
人工等待几分钟的方式一般可以解决问题,但是大部分情况是无用功,还比较浪费时间。
比较自然的一种方式是引入钩子函数。
当应用准备关闭时,首先判断是否存在处理中的请求,不存在则直接关闭;存在,则等待请求完成再关闭。
实现
生产者和消费者是类似的,我们以生产者为例。
启动实现的调整
@Override
public synchronized void run() {
this.paramCheck();
// 启动服务端
log.info("MQ 生产者开始启动客户端 GROUP: {} brokerAddress: {}",
groupName, brokerAddress);
try {
//0. 配置信息
ProducerBrokerConfig config = ProducerBrokerConfig.newInstance()
.groupName(groupName)
.brokerAddress(brokerAddress)
.check(check)
.respTimeoutMills(respTimeoutMills)
.invokeService(invokeService)
.statusManager(statusManager);
//1. 初始化
this.producerBrokerService.initChannelFutureList(config);
//2. 连接到服务端
this.producerBrokerService.registerToBroker();
//3. 标识为可用
statusManager.status(true);
//4. 添加钩子函数
final DefaultShutdownHook rpcShutdownHook = new DefaultShutdownHook();
rpcShutdownHook.setStatusManager(statusManager);
rpcShutdownHook.setInvokeService(invokeService);
rpcShutdownHook.setWaitMillsForRemainRequest(waitMillsForRemainRequest);
rpcShutdownHook.setDestroyable(this.producerBrokerService);
ShutdownHooks.rpcShutdownHook(rpcShutdownHook);
log.info("MQ 生产者启动完成");
} catch (Exception e) {
log.error("MQ 生产者启动遇到异常", e);
throw new MqException(ProducerRespCode.RPC_INIT_FAILED);
}
}
状态管理类
这里我们引入 statusManager 管理整体的状态。
默认的如下:
public class StatusManager implements IStatusManager {
private boolean status;
@Override
public boolean status() {
return this.status;
}
@Override
public IStatusManager status(boolean status) {
this.status = status;
return this;
}
}
就是对一个是否可用的状态进行维护,然后在 channel 获取等地方便于判断当前服务的状态。
钩子函数
DefaultShutdownHook 实现如下:
public class DefaultShutdownHook extends AbstractShutdownHook {
/**
* 调用管理类
* @since 0.0.5
*/
private IInvokeService invokeService;
/**
* 销毁管理类
* @since 0.0.5
*/
private Destroyable destroyable;
/**
* 状态管理类
* @since 0.0.5
*/
private IStatusManager statusManager;
/**
* 为剩余的请求等待时间
* @since 0.0.5
*/
private long waitMillsForRemainRequest = 60 * 1000;
//get & set
/**
* (1)设置 status 状态为等待关闭
* (2)查看是否 {@link IInvokeService#remainsRequest()} 是否包含请求
* (3)超时检测-可以不添加,如果难以关闭成功,直接强制关闭即可。
* (4)关闭所有线程池资源信息
* (5)设置状态为成功关闭
*/
@Override
protected void doHook() {
statusManager.status(false);
// 设置状态为等待关闭
logger.info("[Shutdown] set status to wait for shutdown.");
// 循环等待当前执行的请求执行完成
long startMills = System.currentTimeMillis();
while (invokeService.remainsRequest()) {
long currentMills = System.currentTimeMillis();
long costMills = currentMills - startMills;
if(costMills >= waitMillsForRemainRequest) {
logger.warn("[Shutdown] still remains request, but timeout, break.");
break;
}
logger.debug("[Shutdown] still remains request, wait for a while.");
DateUtil.sleep(10);
}
// 销毁
destroyable.destroyAll();
// 设置状态为关闭成功
statusManager.status(false);
logger.info("[Shutdown] set status to shutdown success.");
}
}
(1)进行关闭前,首先判断通过 invokeService.remainsRequest()
判断是否有未处理完的消息,有则进行等待。
(2)当然,我们还需要考虑网络消息丢失的场景,不可能一直等待。
所以引入了超时中断,最大等待时间也是可以自行定义的。
if(costMills >= waitMillsForRemainRequest) {
logger.warn("[Shutdown] still remains request, but timeout, break.");
break;
}
(3)关闭之后
将 status 设置为 false,标识当前服务不可用。
小结
随着 rpc 技术的成熟,优雅关闭已经成为一个很基本的功能点。
一个小小的改动,可以节约生产发布时间,早点下班陪陪家人。
希望本文对你有所帮助,如果喜欢,欢迎点赞收藏转发一波。
我是老马,期待与你的下次重逢。
开源地址
The message queue in java.(java 简易版本 mq 实现) https://github.com/houbb/mq
拓展阅读
rpc-从零开始实现 rpc https://github.com/houbb/rpc
【mq】从零开始实现 mq-05-实现优雅停机的更多相关文章
- dubbo-2.5.6优雅停机研究
不优雅的停机: 当进程存在正在运行的线程时,如果直接执行kill -9 pid时,那么这个正在执行的线程被中断,就好像一个机器运行中突然遭遇断电的情况,所导致的结果是造成服务调用的消费端报错,也有可能 ...
- JAVA优雅停机的实现
最近在项目中需要写一个数据转换引擎服务,每过5分钟同步一次数据.具体实现是启动engine server后会初始化一个ScheduledExecutorService和一个ThreadPoolExec ...
- 哦,这就是java的优雅停机?(实现及原理)
优雅停机? 这个名词我是服的,如果抛开专业不谈,多好的名词啊! 其实优雅停机,就是在要关闭服务之前,不是立马全部关停,而是做好一些善后操作,比如:关闭线程.释放连接资源等. 再比如,就是不会让调用方的 ...
- Dubbo源码学习--优雅停机原理及在SpringBoot中遇到的问题
Dubbo源码学习--优雅停机原理及在SpringBoot中遇到的问题 相关文章: Dubbo源码学习文章目录 前言 主要是前一阵子换了工作,第一个任务就是解决目前团队在 Dubbo 停机时产生的问题 ...
- spring cloud shutdown graceful 优雅停机
spring cloud shutdown graceful 优雅停机 当一个服务启动后,会注册到eureka中,其他的服务也可以从eureka获取到新注册的服务.但当我们要停止一个服务的时候,如果直 ...
- dubbo之优雅停机
优雅停机 Dubbo 是通过 JDK 的 ShutdownHook 来完成优雅停机的,所以如果用户使用 kill -9 PID 等强制关闭指令,是不会执行优雅停机的,只有通过 kill PID 时,才 ...
- Spring Boot 内嵌容器 Tomcat / Undertow / Jetty 优雅停机实现
Spring Boot 内嵌容器 Tomcat / Undertow / Jetty 优雅停机实现 Anoyi 精讲JAVA 精讲JAVA 微信号 toooooooozi 功能介绍 讲解java深层次 ...
- ShutdownHook- Java 优雅停机解决方案
想象一下,如果你现在刚好在 word 上写需求文档,电脑突然重启.等待开机完成,你可能会发现写了一个小时文档没有保存,就这么没了... 一个正在运行 Java 应用如果突然将其停止,影响不止数据丢失, ...
- spring cloud 优雅停机
spring cloud 优雅停机 大部分部署项目如果要停掉项目一般都是用kill -9 来杀进程 但是由于Eureka采用心跳的机制来上下线服务,会导致服务消费者调用已经kill的服务提供者然后出错 ...
随机推荐
- 哪一个List实现了最快插入?
LinkedList和ArrayList是另个不同变量列表的实现.ArrayList的优势在于动态的增长数组,非常适合初始时总长度未知的情况下使用.LinkedList的优势在于在中间位置插入和删除操 ...
- MyBatis 实现一对一有几种方式?具体怎么操作的?
有联合查询和嵌套查询,联合查询是几个表联合查询,只查询一次, 通过在 resultMap 里面配置 association 节点配置一对一的类就可以完成: 嵌套查询是先查一个表,根据这个表里面的结果的 ...
- 详解AOP——用配置文件的方式实现AOP
AOP概念 1.AOP:面向切面(方面)编程,扩展功能不修改源代码实现 AOP原理 AOP采用横向抽取机制,取代了传统纵向继承体系重复性代码 传统的纵向抽取机制: 横向抽取机制: AOP操作术语 1. ...
- 当心,你搞的Scrum可能是小瀑布
摘要:有的团队刚接触Scrum,一个问题令他们很困扰:迭代初期开发人员的工作较多,测试人员闲着:迭代末期开发人员闲着,测试人员的工作比较多,怎么解决资源等待的问题呢? 本文分享自华为云社区<当心 ...
- web前端教程《每日一题》(1-99)完结
第1期(2016年4月6日): (1)js中关闭当前窗口的方法是:window.close(); 第2期(2016年4月7日): (1)js中使字符串中的字符变为小写的方法是:toLowerCase方 ...
- 微信小程序开发快速入手
1.在page中的修改数据的setData函数,需要传递的是一个对象. that.setData({ src: res.tempFilePath }) 2.在 onload 事件中,可以获取wx.na ...
- Java实现链表反转(借助栈实现)
public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } // 添加新的 ...
- 通过CSS给图像设置圆角边框
<html> <style> .smaller-image{ border-radius: 50%; width: 100px; } </style> <bo ...
- 微信小程序下拉加载和上拉刷新两种实现方法
方法一:onPullDownRefresh和onReachBottom方法实现小程序下拉加载和上拉刷新 首先要在json文件里设置window属性 设置js里onPullDownRefresh和onR ...
- Spring Boot-@ImportResource注解
@ImportResource:导入Spring的配置文件,让配置文件里面的内容生效 第一步:创建一个spring配置文件bean.xml <?xml version="1.0&quo ...