前景回顾

【mq】从零开始实现 mq-01-生产者、消费者启动

【mq】从零开始实现 mq-02-如何实现生产者调用消费者?

【mq】从零开始实现 mq-03-引入 broker 中间人

【mq】从零开始实现 mq-04-启动检测与实现优化

【mq】从零开始实现 mq-05-实现优雅停机

为什么需要优雅关闭?

我记得多年前,那个时候 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-实现优雅停机的更多相关文章

  1. dubbo-2.5.6优雅停机研究

    不优雅的停机: 当进程存在正在运行的线程时,如果直接执行kill -9 pid时,那么这个正在执行的线程被中断,就好像一个机器运行中突然遭遇断电的情况,所导致的结果是造成服务调用的消费端报错,也有可能 ...

  2. JAVA优雅停机的实现

    最近在项目中需要写一个数据转换引擎服务,每过5分钟同步一次数据.具体实现是启动engine server后会初始化一个ScheduledExecutorService和一个ThreadPoolExec ...

  3. 哦,这就是java的优雅停机?(实现及原理)

    优雅停机? 这个名词我是服的,如果抛开专业不谈,多好的名词啊! 其实优雅停机,就是在要关闭服务之前,不是立马全部关停,而是做好一些善后操作,比如:关闭线程.释放连接资源等. 再比如,就是不会让调用方的 ...

  4. Dubbo源码学习--优雅停机原理及在SpringBoot中遇到的问题

    Dubbo源码学习--优雅停机原理及在SpringBoot中遇到的问题 相关文章: Dubbo源码学习文章目录 前言 主要是前一阵子换了工作,第一个任务就是解决目前团队在 Dubbo 停机时产生的问题 ...

  5. spring cloud shutdown graceful 优雅停机

    spring cloud shutdown graceful 优雅停机 当一个服务启动后,会注册到eureka中,其他的服务也可以从eureka获取到新注册的服务.但当我们要停止一个服务的时候,如果直 ...

  6. dubbo之优雅停机

    优雅停机 Dubbo 是通过 JDK 的 ShutdownHook 来完成优雅停机的,所以如果用户使用 kill -9 PID 等强制关闭指令,是不会执行优雅停机的,只有通过 kill PID 时,才 ...

  7. Spring Boot 内嵌容器 Tomcat / Undertow / Jetty 优雅停机实现

    Spring Boot 内嵌容器 Tomcat / Undertow / Jetty 优雅停机实现 Anoyi 精讲JAVA 精讲JAVA 微信号 toooooooozi 功能介绍 讲解java深层次 ...

  8. ShutdownHook- Java 优雅停机解决方案

    想象一下,如果你现在刚好在 word 上写需求文档,电脑突然重启.等待开机完成,你可能会发现写了一个小时文档没有保存,就这么没了... 一个正在运行 Java 应用如果突然将其停止,影响不止数据丢失, ...

  9. spring cloud 优雅停机

    spring cloud 优雅停机 大部分部署项目如果要停掉项目一般都是用kill -9 来杀进程 但是由于Eureka采用心跳的机制来上下线服务,会导致服务消费者调用已经kill的服务提供者然后出错 ...

随机推荐

  1. jQuery--表单的过滤

    1.表单过滤器的介绍 :input 所有表单元素(<input>/<select>/<textarea>/<button>) :text 文本框< ...

  2. 访问修饰符 public,private,protected,以及不写(默认) 时的区别?

    修饰符 当前类 同 包 子 类 其他包 public √ √ √ √ protecte d √ √ √ × default √ √ × × private √ × × × 类的成员不写访问修饰时默认为 ...

  3. java-file类 hei

    File类 /* java.io.File类 文件和目录轮径的抽象形式 java把电脑种的文件和文件夹,封装为一个file类,我们可以使用file类对文件和文件夹进行曹祖 使用File类的方法 创建一 ...

  4. Spring Bean生命周期回调

    参阅官方文档:https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-factory ...

  5. cache + redis(一)

    一.缓存的概念 1.Buffer,Cache的区别: buffer: 一般用于写操作,写缓冲. cache: 一般用于读操作,读缓存. 它们都是解决速度不一致的问题,都设计到IO操作. 2.Cache ...

  6. LVS 工作图

    一.工作模式 1.NAT模式 -----主要是修改目标IP地址为RS的IP地址.即请求进入负载均衡器时做DNAT,响应出负载均衡器时做SNAT. 工作方式: 1)客户端请求网站,经过路由器到达负载均衡 ...

  7. Python中对象、类型、元类之间的关系

    Python里的对象.类型和元类的关系很微妙也很有意思. 1989年圣诞节期间,上帝很无聊,于是创造了一个世界. 对象 在这个世界的运转有几条定律. 1.一切都是对象 对象(object)是这个世界的 ...

  8. python-使用函数求特殊a串数列和

    给定两个均不超过9的正整数a和n,要求编写函数fn(a,n) 求a+aa+aaa++⋯+aa⋯aa(n个a)之和,fn须返回的是数列和 函数接口定义: 1 fn(a,n) 2 其中 a 和 n 都是用 ...

  9. CCF201609-2火车购票

    问题描述 请实现一个铁路购票系统的简单座位分配算法,来处理一节车厢的座位分配. 假设一节车厢有20排.每一排5个座位.为方便起见,我们用1到100来给所有的座位编号,第一排是1到5号,第二排是6到10 ...

  10. Spring MVC框架搭建及其详解

    现在主流的Web MVC框架除了Struts这个主力 外,其次就是Spring MVC了,因此这也是作为一名程序员需要掌握的主流框架,框架选择多了,应对多变的需求和业务时,可实行的方案自然就多了.不过 ...