背景

我在公司内负责自研的dubbo注册中心相关工作,群里经常接到业务方反馈dubbo接口注销报错。经排查,确定是同一个接口调用了两次注销接口导致,由于我们的注册中心注销接口不能重复调用,调用第二次会因为实例已经注销而报实例找不到的错误。

虽然这个报错仅会打印一条错误日志,不影响业务,但本着 follow through的精神,我决定还是一探究竟,更何况重复注销也增加了应用的结束时间,影响了发布回滚速度。

问题复现

拿到业务方的dubbo版本,基于开源2.7.3内部定制的一个版本,该版本修改主要涉及安全漏洞修复以及一些业务适配,写了个demo跑起来,然后kill,发现果然报错了。

为了确定不是内部修改导致的问题,用开源的2.7.3版本再次测试,发现还是报错。

同时为了确定这是一个bug,我将dubbo版本修改为2.7.7做测试,发现该版本不再报错。

说明了重复注销至少是开源dubbo 2.7.3的一个bug,在更高的2.7.7版本中已经被修复。

于是有了解决方案:升级dubbo,但如果这么简单就没有这篇文章了。

  1. 内部的dubbo已经做了修改,想升级得把改动merge到新版本,比较费劲
  2. 就算升级了内部的dubbo版本,也不可能这么快速推动业务方升级

所以应该首先找到bug是哪里导致的,其次看注册中心的扩展是否可以修复这个问题,如果不能修复,就只能在内部的dubbo版本中修复该问题。

问题排查

怀疑ShutdownHook

由于这几天研究过ShutdownHook(见文末《ShutdownHook原理》),第一时间怀疑ShutdownHook可能有问题。

dubbo 2.7.3代码有关ShutdownHook的实现在DubboShutdownHook类,顺着代码梳理出如下关系

看到dubbo本身和spring都注册了ShutdownHook,更加怀疑这里是不是ShutdownHook注册重复了。于是debug看看是否是注册重复了,这里给一个小经验,IntelliIDEA调试ShutdownHook执行时,要手动kill进程才会触发debug,点IDE上的关闭按钮不会触发



DubboShutdownHook.doDestroy打上断点,debug发现只会执行一次,这说明spring和dubbo的ShutdownHook只会注册一次,这是怎么实现的呢?经过很多次测试,发现了dubbo一个很牛逼的设计。

DubboShutdownHook中有registerunregister方法,分别是注册和注销ShutdownHook,在这两个方法上都打上断点,在程序启动时发现这样一个有趣的执行顺序:



总结一下是dubbo本身注册了ShutdownHook,但如果用到了spring框架,spring框架在初始化时注销了dubbo注册的ShutdownHook,这样就只保留了spring的ShutdownHook,真是秒啊!实现的代码只有这短短几行

public static void addApplicationContext(ApplicationContext context) {
CONTEXTS.add(context);
if (context instanceof ConfigurableApplicationContext) {
((ConfigurableApplicationContext) context).registerShutdownHook();
DubboShutdownHook.getDubboShutdownHook().unregister();
}
BeanFactoryUtils.addApplicationListener(context, SHUTDOWN_HOOK_LISTENER);
}

于是怀疑的ShutdownHook问题被证明没有任何问题了。

从注销堆栈继续排查

能稳定复现的问题一定很好排查,借助IDE的debug来看两次注销的调用堆栈,在注册中心扩展的unregister方法处加断点,可以看到如下两次来源不同的堆栈信息





代码中体现是



也就是说一次ShutdownHook执行,触发了两次注销。

接下来就比较好排查了,一步一步debug,这里解释下

  • AbstractRegistryFactory.destroyAll()是销毁所有注册中心,销毁时会调研注册中心的注销接口
  • destroyProtocols是销毁所有的protocol,注册中心的protocol在销毁时拿到registry,然后调用了registry的注销接口

那么dubbo 2.7.7是如何避免这个问题的呢?

在dubbo 2.7.7的代码中,注册中心的protocol在销毁时获取注册中心稍微增加了点代码



原来在注册中心被销毁后,destroyed变量被置为true,从而在registry protocol再次获取注册中心时,已经拿不到了原先的注册中心了,拿到的是一个空的注册中心,调用注销,自然没有什么效果。

追溯了下github,这次PR是

https://github.com/apache/dubbo/pull/5450

这个修复在2.7.5就已经修复了

总结

  • dubbo重复注销问题存在于2.7.0 ~ 2.7.4版本,2.7.5修复,zk注册中心不会报错,可能无法感知,但它确实存在,也会拖慢应用的关闭速度
  • 通过追查发现,其实该问题可以在注册中心的扩展中解决,让registry的destroy只能被调用一次
  • 遇到无论多小的问题,有空都去钻研下,你会收货一些新知识,比如这次dubbo中ShutdownHook如此巧妙的设计

关于作者:公众号"捉虫大师"作者,专注后端的中间件开发,关注我,给你推送最纯粹的技术干货

排查dubbo接口重复注销问题,我发现了一个巧妙的设计的更多相关文章

  1. 从应用层到网络层排查 Dubbo 接口超时全记录

    大家好,我是坤哥 我们常说面试造火箭,很多人对此提出质疑,相信大家看了这篇文章会明白面试造火箭的道理,这篇排查问题的技巧涉及到索引,GC,容器,网络抓包,全链路追踪等基本技能,没有这些造火箭的本事,排 ...

  2. jmeter测试dubbo接口

    本文讲解jmeter测试dubbo接口的实现方式,文章以一个dubbo的接口为例子进行讲解,该dubbo接口实现的功能为: 一:首先我们看服务端代码 代码架构为: 1:新建一个maven工程,pom文 ...

  3. Jmeter实现dubbo接口压测案例

    当前项目中重构了消息服务,需要对消息服务接口做性能压测,评估消息服务的性能情况 通过和开发对接,目前消息服务是通过dubbo接口对内提供服务,所以才有了这边文章的记录 最初的压测这个dubbo接口有三 ...

  4. 关于dubbo接口性能测试

      最初的压测这个dubbo接口有三种思路: .第一种就是基于业务,比如注册业务,注册成功后,会发送短信消息到用户手机,通过业务调用消息服务,最容易实现,但是业务瓶颈最大导致测试结果不准 .第二种是通 ...

  5. 秒懂Dubbo接口(原理篇)

    引言 背景 单一应用架构 垂直应用架构 分布式服务架构 流动计算架构 为什么要用 Dubbo? 什么是分布式? 为什么要分布式? Dubbo 的架构 Dubbo 的架构图解 Dubbo 工作原理 Du ...

  6. dubbo接口demo开发

    接口需求 客户端输入uncleyong(当然,也可以输入其它字符串),服务端返回hello uncleyong 开发环境 jdk + idea + maven + zookeeper jdk安装 id ...

  7. java反射调用dubbo接口

    需求:项目增加幂等 场景:1.三个项目:a .b.c2.a项目加幂等3.b项目dubbo调用项目a的时候超时没有获取返回结果,增加重试机制(非立即重试,3min or 5min 后重试)4.c项目是一 ...

  8. dubbo 接口初入门

    最近公司开发新的一套系统,开发出来的方案会基于dubbo分布式服务框架开发的,那么什么是dubbo,身为测试的我,第一眼看到这个,我得去了解了解dubbo是啥玩意,为开展的测试工作做准备,提前先学 d ...

  9. 通过Jmeter对Dubbo接口进行接口及性能测试

    dubbo接口/性能测试 dubbo简介 zookeeper简介.安装及配置 dubbo服务端demo dubbo客户端调用 jmeter工程改造及接口调用 读取jmeter参数用于dubbo性能测试 ...

随机推荐

  1. vivo 全球商城:优惠券系统架构设计与实践

    一.业务背景 优惠券是电商常见的营销手段,具有灵活的特点,既可以作为促销活动的载体,也是重要的引流入口.优惠券系统是vivo商城营销模块中一个重要组成部分,早在15年vivo商城还是单体应用时,优惠券 ...

  2. 洛谷P2210题解

    题面 模拟退火练手好题. 对于这个题,一般有两种解法: 每次随机两个数交换. 每次直接打乱数组. 两个方法都可以过,我写了第一种,因为不想用stl. 代码

  3. 洛谷P2858题解

    这是一道裸的第二类区间DP(由已知区间向外扩展)题. 首先定义 \(f_{i,j}\) 为最后 \(j-i+1\) 个数取 \([i,j]\) 这个区间时,\([i,j]\) 这个区间可以产生的最大价 ...

  4. pwnable.kr之simple Login

    pwnable.kr之simple Login 懒了几天,一边看malloc.c的源码,一边看华庭的PDF.今天佛系做题,到pwnable.kr上打开了simple Login这道题,但是这道题个人觉 ...

  5. Windows协议 LDAP篇 - 域用户和计算机用户

    域用户 查询域用户 通过SAMR协议查询(net user就是通过这种协议查询的),samr也不是一种专门的协议,是一个RPC接口 在impacket里有个脚本samrdump.py,就是专门调用这个 ...

  6. small-spring 代码贡献者3个月,敢说精通Spring了,分享我的总结!

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.为什么手写Spring 这个与我们码农朝夕相处的 Spring,就像睡在你身边的媳妇,你知 ...

  7. xubuntu下制作自定义的ISO文件

    by han; 1.打开命令编辑器,建立文件 # sudo su # mkdir -p /opt/custom_conf/user_custom_conf/config 2.将自定义的设置复制保存到/ ...

  8. FSM自动售货机 verilog 实现及 code 细节讲解

    1.题目: 饮料1.5 元, 可投入硬币1 元 0.5 元,输出饮料 零钱 2. 画出状态机. 3.仿真结果:coin=1 --> 0.5 元 coin=2-->1元 4.关键代码分析: ...

  9. 配置VRRP的多备份组

    实验拓扑和端口IP见上一个博客 实验步骤: 1.继续创建虚拟组2 2. 2. 查看 3.验证: PC1 PC2 PC1通过R2,PC2通过R3访问外网 二.验证VRRP的抢占特性 可以看到,即使R2的 ...

  10. 线程队列 concurrent 协程 greenlet gevent

    死锁问题 所谓死锁:是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进 ...