Dubbo + Nacos这么玩就失去高可用的能力了( 二 )

(2) dubbo微服务调用异常:
2023-09-06 08:09:38|ERROR|runtimeExceptionHandler|135|http-nio-8080-exec-5|"发生系统异常"|"org.Apache.dubbo.rpc.RpcException: No provider available from registry 10.20.1.13:8848,10.20.1.14:8848,10.20.1.15:8848 for service ClueAuntMatchApi:1.0 on consumer 10.21.230.14 use dubbo version 2.7.8, please check status of providers(disabled, not registered or in blacklist).at org.apache.dubbo.registry.integration.RegistryDirectory.doList(RegistryDirectory.java:599)at org.apache.dubbo.rpc.cluster.directory.AbstractDirectory.list(AbstractDirectory.java:74)at org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker.list(AbstractClusterInvoker.java:292)at org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker.invoke(AbstractClusterInvoker.java:257)at org.apache.dubbo.rpc.cluster.interceptor.ClusterInterceptor.intercept(ClusterInterceptor.java:47)三、根据异常进行猜测熟悉Dubbo的朋友肯定知道这个错误please check status of providers(disabled, not registered or in blacklist). , 基本上是代表:Provider下线了 或者 Consumer没找到Provider 。
根据以往使用dubbo + zookeeper的经验 , 客户端应该会拉取注册中心的Provider的信息 , 然后本地缓存一份 , 即使注册中心挂了 , 应该也能调用到别的服务 。不至于出现完全找不到服务提供者的信息 。
当思考不出来时 , 只能靠异常去猜测原因了 。根据以上2个异常开始猜测 。
1.猜测1由于nacos-server-1挂了 , 导致nacos-client与server的心跳异常 , 导致本地缓存的provider的元数据被清掉了 。有了猜测 , 赶紧查看nacos-client的源代码 , 找到nacos-client 与 nacos-server 心跳的那一段:

Dubbo + Nacos这么玩就失去高可用的能力了

文章插图

Dubbo + Nacos这么玩就失去高可用的能力了

文章插图
继续往下跟 , 可以看到这段核心代码:
public String reqApi(String api, Map<String, String> params, Map<String, String> body, List<String> servers,String method) throws NacosException {params.put(CommonParams.NAMESPACE_ID, getNamespaceId());if (CollectionUtils.isEmpty(servers) && StringUtils.isEmpty(nacosDomain)) {throw new NacosException(NacosException.INVALID_PARAM, "no server available");}NacosException exception = new NacosException();if (servers != null && !servers.isEmpty()) {Random random = new Random(System.currentTimeMillis());int index = random.nextInt(servers.size());for (int i = 0; i < servers.size(); i++) {String server = servers.get(index);try {return callServer(api, params, body, server, method);} catch (NacosException e) {exception = e;if (NAMING_LOGGER.isDebugEnabled()) {NAMING_LOGGER.debug("request {} failed.", server, e);}}index = (index + 1) % servers.size();}}if (StringUtils.isNotBlank(nacosDomain)) {for (int i = 0; i < UtilAndComs.REQUEST_DOMAIN_RETRY_COUNT; i++) {try {return callServer(api, params, body, nacosDomain, method);} catch (NacosException e) {exception = e;if (NAMING_LOGGER.isDebugEnabled()) {NAMING_LOGGER.debug("request {} failed.", nacosDomain, e);}}}}NAMING_LOGGER.error("request: {} failed, servers: {}, code: {}, msg: {}", api, servers, exception.getErrCode(),exception.getErrMsg());throw new NacosException(exception.getErrCode(),"failed to req API:" + api + " after all servers(" + servers + ") tried: " + exception.getMessage());}注意上面的这段代码:
for (int i = 0; i < servers.size(); i++) {String server = servers.get(index);try {return callServer(api, params, body, server, method);} catch (NacosException e) {exception = e;if (NAMING_LOGGER.isDebugEnabled()) {NAMING_LOGGER.debug("request {} failed.", server, e);}}index = (index + 1) % servers.size();}通过以上这一段代码可以知道 , nacos-client与nacos-server集群里的随机一台通信 , 感兴趣的朋友可以继续阅读源代码 , 跟到最后会发现 , 只要有一次心跳是正常的 , 那就认为心跳正常 。因为我只停了一台nacos-server , 但是与其他两台server依旧可以保持心跳 , 所以整个心跳过程虽然报错 , 但是仍然是正常的 , 所以这个猜测放弃了 , 继续猜测 。
2.猜测2既然dubbo与zookeeper是建立长连接进行socket通信 , 那dubbo与nacos-server可能也是建立了长连接进行socket通信 , 某个nacos-server挂了之后 , 可能因为nacos-server没有zookeeper的选主机制 , 所以不会自动切换到别的可用的nacos-server去调用 。
或者是nacos-server集群选主问题 , 选主后没有及时通知到consumer , 或者consumer与nacos本身通信机制有问题 。总之就是因为某种机制 , 导致没有自动切换到可用的nacos-server上 , 导致获取不到provider元数据 , 自然就无法发起调用 。


推荐阅读