织梦网站模板源码下载,郓城网站建设价格,工业机器人技术,微商营销软件商城前言
随着瓜子业务的不断发展#xff0c;系统规模在逐渐扩大#xff0c;目前在瓜子的私有云上已经运行着数百个 Dubbo 应用#xff0c;上千个 Dubbo 实例。瓜子各部门业务迅速发展#xff0c;版本没有来得及统一#xff0c;各个部门都有自己的用法。随着第二机房的建设系统规模在逐渐扩大目前在瓜子的私有云上已经运行着数百个 Dubbo 应用上千个 Dubbo 实例。瓜子各部门业务迅速发展版本没有来得及统一各个部门都有自己的用法。随着第二机房的建设Dubbo 版本统一的需求变得越发迫切。几个月前公司发生了一次与 Dubbo 相关的生产事故成为了公司 基于社区 Dubbo 2.7.3 版本升级的诱因。
接下来我会从这次线上事故开始讲讲我们这段时间所做的 Dubbo 版本升级的历程以及我们规划的 Dubbo 后续多机房的方案。
一、Ephermal节点未及时删除导致provider不能恢复注册的问题修复
事故背景
在生产环境瓜子内部各业务线共用一套zookeeper集群作为dubbo的注册中心。2019年9月份机房的一台交换机发生故障导致zookeeper集群出现了几分钟的网络波动。在zookeeper集群恢复后正常情况下dubbo的provider应该会很快重新注册到zookeeper上但有一小部分的provider很长一段时间没有重新注册到zookeeper上直到手动重启应用后才恢复注册。
排查过程
首先我们统计了出现这种现象的dubbo服务的版本分布情况发现在大多数的dubbo版本中都存在这种问题且发生问题的服务比例相对较低在github中我们也未找到相关问题的issues。因此推断这是一个尚未修复的且在网络波动情况的场景下偶现的问题。
接着我们便将出现问题的应用日志、zookeeper日志与dubbo代码逻辑进行相互印证。在应用日志中应用重连zookeeper成功后provider立刻进行了重新注册之后便没有任何日志打印。而在zookeeper日志中注册节点被删除后并没有重新创建注册节点。对应到dubbo的代码中只有在FailbackRegistry.register(url)的doRegister(url)执行成功或线程被挂起的情况下才能与日志中的情况相吻合。 public void register(URL url) {super.register(url);failedRegistered.remove(url);failedUnregistered.remove(url);try {// Sending a registration request to the server sidedoRegister(url);} catch (Exception e) {Throwable t e;// If the startup detection is opened, the Exception is thrown directly.boolean check getUrl().getParameter(Constants.CHECK_KEY, true) url.getParameter(Constants.CHECK_KEY, true) !Constants.CONSUMER_PROTOCOL.equals(url.getProtocol());boolean skipFailback t instanceof SkipFailbackWrapperException;if (check || skipFailback) {if (skipFailback) {t t.getCause();}throw new IllegalStateException(Failed to register url to registry getUrl().getAddress() , cause: t.getMessage(), t);} else {logger.error(Failed to register url , waiting for retry, cause: t.getMessage(), t);}// Record a failed registration request to a failed list, retry regularlyfailedRegistered.add(url);}}
在继续排查问题前我们先普及下这些概念dubbo默认使用curator作为zookeeper的客户端curator与zookeeper是通过session维持连接的。当curator重连zookeeper时若session未过期则继续使用原session进行连接若session已过期则创建新session重新连接。而ephemeral节点与session是绑定的关系在session过期后会删除此session下的ephemeral节点。
继续对doRegister(url)的代码进行进一步排查我们发现在CuratorZookeeperClient.createEphemeral(path)方法中有这么一段逻辑在createEphemeral(path)捕获了NodeExistsException创建ephemeral节点时若此节点已存在则认为ephemeral节点创建成功。这段逻辑初看起来并没有什么问题且在以下两种常见的场景下表现正常
Session未过期创建Ephemeral节点时原节点仍存在,不需要重新创建Session已过期创建Ephemeral节点时原节点已被zookeeper删除创建成功public void createEphemeral(String path) {try {client.create().withMode(CreateMode.EPHEMERAL).forPath(path);} catch (NodeExistsException e) {} catch (Exception e) {throw new IllegalStateException(e.getMessage(), e);}}
但是实际上还有一种极端场景zookeeper的Session过期与删除Ephemeral节点不是原子性的也就是说客户端在得到Session过期的消息时Session对应的Ephemeral节点可能还未被zookeeper删除。此时dubbo去创建Ephemeral节点,发现原节点仍存在故不重新创建。待Ephemeral节点被zookeeper删除后便会出现dubbo认为重新注册成功但实际未成功的情况也就是我们在生产环境遇到的问题。
此时问题的根源已被定位。定位问题之后经我们与 Dubbo 社区交流发现考拉的同学也遇到过同样的问题更确定了这个原因。
问题的复现与修复
定位到问题之后我们便开始尝试本地复现。由于zookeeper的Session过期但Ephemeral节点未被删除的场景直接模拟比较困难我们通过修改zookeeper源码在Session过期与删除Ephemeral节点的逻辑中增加了一段休眠时间间接模拟出这种极端场景并在本地复现了此问题。
在排查问题的过程中我们发现kafka的旧版本在使用zookeeper时也遇到过类似的问题并参考kafka关于此问题的修复方案确定了dubbo的修复方案。在创建Ephemeral节点捕获到NodeExistsException时进行判断若Ephemeral节点的SessionId与当前客户端的SessionId不同则删除并重建Ephemeral节点。在内部修复并验证通过后我们向社区提交了issues及pr。
kafka类似问题issueshttps://issues.apache.org/jira/browse/KAFKA-1387
dubbo注册恢复问题issueshttps://github.com/apache/dubbo/issues/5125
二、瓜子的dubbo升级历程
上文中的问题修复方案已经确定但我们显然不可能在每一个dubbo版本上都进行修复。在咨询了社区dubbo的推荐版本后我们决定在dubbo2.7.3版本的基础上开发内部版本修复来这个问题。并借这个机会开始推动公司dubbo版本的统一升级工作。
为什么要统一dubbo版本
统一dubbo版本后我们可以在此版本上内部紧急修复一些dubbo问题如上文的dubbo注册故障恢复失效问题。瓜子目前正在进行第二机房的建设部分dubbo服务也在逐渐往第二机房迁移。统一dubbo版本也是为dubbo的多机房做铺垫。有利于我们后续对dubbo服务的统一管控。dubbo社区目前的发展方向与我们公司现阶段对dubbo的一些诉求相吻合如支持gRPC、云原生等。
为什么选择dubbo2.7.3
我们了解到在我们之前携程已经与dubbo社区合作进行了深度合作携程内部已全量升级为 2.7.3 的社区版本并在协助社区修复了 2.7.3 版本的一些兼容性问题。感谢携程的同学帮我们踩坑dubbo2.7.3版本在当时虽然是最新的版本但已经发布了2个月的时间从社区issues反馈来看dubbo2.7.3相对dubbo2.7之前的几个版本在兼容性方面要好很多。我们也咨询了dubbo社区的同学推荐升级版本为2.7.3。
内部版本定位
基于社区dubbo2.7.3版本开发的dubbo内部版本属于过渡性质的版本目的是为了修复线上provider不能恢复注册的问题以及一些社区dubbo2.7.3的兼容性问题。瓜子的dubbo最终还是要跟随社区的版本而不是开发自已的内部功能。因此我们在dubbo内部版本中修复的所有问题均与社区保持了同步以保证后续可以兼容升级到社区dubbo的更高版本。
兼容性验证与升级过程
我们在向dubbo社区的同学咨询了版本升级方面的相关经验后于9月下旬开始了dubbo版本的升级工作。
初步兼容性验证 首先,我们梳理了一些需要验证的兼容性case针对公司内部使用较多的dubbo版本与dubbo2.7.3一一进行了兼容性验证。经验证除dubboX外dubbo2.7.3与其他dubbo版本均兼容。dubboX由于对dubbo协议进行了更改与dubbo2.7.3不兼容。生产环境兼容性验证 在初步验证兼容性通过后我们与业务线合作挑选了一些重要程度较低的项目在生产环境对dubbo2.7.3与其他版本的兼容性进行了进一步验证。并在内部版本修复了一些兼容性问题。推动公司dubbo版本升级 在10月初完成了dubbo兼容性验证后我们开始在各个业务线推动dubbo的升级工作。截止到12月初,已经有30%的dubbo服务的完成了版本升级。按照排期预计于2020年3月底前完成公司dubbo版本的统一升级。
兼容性问题汇总
在推动升级dubbo2.7.3版本的过程整体上比较顺利,当然也遇到了一些兼容性问题 创建zookeeper节点时提示没有权限 dubbo配置文件中已经配置了zookeeper的用户名密码但在创建zookeeper节点时却抛出KeeperErrorCode NoAuth的异常这种情况分别对应两个兼容性问题 issues:https://github.com/apache/dubbo/issues/5076 dubbo在未配置配置中心时默认使用注册中心作为配置中心。通过注册中心的配置信息初始化配置中心配置时由于遗漏了用户名密码导致此问题。issues:https://github.com/apache/dubbo/issues/4991 dubbo在建立与zookeeper的连接时会根据zookeeper的address复用之前已建立的连接。当多个注册中心使用同一个address但权限不同时就会出现NoAuth的问题。参考社区的pr我们在内部版本进行了修复。 curator版本兼容性问题 dubbo2.7.3与低版本的curator不兼容因此我们默认将curator版本升级至4.2.0dependencygroupIdorg.apache.curator/groupIdartifactIdcurator-framework/artifactIdversion4.2.0/version
/dependency
dependencygroupIdorg.apache.curator/groupIdartifactIdcurator-recipes/artifactIdversion4.2.0/version
/dependency 分布式调度框架elastic-job-lite强依赖低版本的curator,与dubbo2.7.3使用的curator版本不兼容,这给dubbo版本升级工作带来了一定阻塞。考虑到elastic-job-lite已经很久没有人进行维护目前一些业务线计划将elastic-job-lite替换为其他的调度框架。openFeign与dubbo兼容性问题 issues: https://github.com/apache/dubbo/issues/3990
dubbo的ServiceBean监听spring的ContextRefreshedEvent,进行服务暴露。openFeign提前触发了ContextRefreshedEvent此时ServiceBean还未完成初始化于是就导致了应用启动异常。 参考社区的pr我们在内部版本修复了此问题。
RpcException兼容性问题 dubbo低版本consumer不能识别dubbo2.7版本provider抛出的org.apache.dubbo.rpc.RpcException。因此在consumer全部升级到2.7之前不建议将provider的com.alibaba.dubbo.rpc.RpcException改为org.apache.dubbo.rpc.RpcExceptionqos端口占用 dubbo2.7.3默认开启qos功能导致一些混部在物理机的dubbo服务升级时出现qos端口占用问题。关闭qos功能后恢复。自定义扩展兼容性问题 业务线对于dubbo的自定义扩展比较少因此在自定义扩展的兼容性方面暂时还没有遇到比较难处理的问题基本上都是变更package导致的问题由业务线自行修复。skywalking agent兼容性问题 我们项目中一般使用skywalking进行链路追踪由于skywalking agent6.0的plugin不支持dubbo2.7因此统一升级skywalking agent到6.1。
三、dubbo多机房方案
瓜子目前正在进行第二机房的建设工作dubbo多机房是第二机房建设中比较重要的一个话题。在dubbo版本统一的前提下我们就能够更顺利的开展dubbo多机房相关的调研与开发工作。
初步方案
我们咨询了dubbo社区的建议并结合瓜子云平台的现状初步确定了dubbo多机房的方案。
在每个机房内部署一套独立的zookeeper集群。集群间信息不同步。这样就没有了zookeeper集群跨机房延迟与数据不同步的问题。dubbo服务注册时仅注册到本机房的zookeeper集群订阅时同时订阅两个机房的zookeeper集群。实现同机房优先调用的路由逻辑。以减少跨机房调用导致的不必要网络延迟。
同机房优先调用
dubbo同机房优先调用的实现比较简单相关逻辑如下
瓜子云平台默认将机房的标志信息注入容器的环境变量中。provider暴露服务时读取环境变量中的机房标志信息追加到待暴露服务的url中。consumer调用provider时读取环境变量中的机房标志信息根据路由策略优先调用具有相同标志信息的provider。
针对以上逻辑我们简单实现了dubbo通过环境变量进行路由的功能并向社区提交了pr。
dubbo通过环境变量路由pr: https://github.com/apache/dubbo/pull/5348
原文链接 本文为阿里云原创内容未经允许不得转载。