背景本文记录了dubbo2.7应用级服务发现的问题,这里不再赘述。读者反映他们正在开发基于dubbo2.7应用级服务发现的dubbo网关。根据文章《dubbo应用级服务发现初体验》,他们写了一个democall,报noprovidererror。首先我觉得他们的想法很多,把dubbo应用级服务发现投入生产的公司并不多。其次,当时写文章的时候测试没有遇到什么问题,但是本着帮助读者解决问题的态度,我还是重新写了一个demo测试。问题定位我拿了一个平时用来测试的dubbodemo工程(不是dubbo源码里的demo),发现确实没有在zookeeper上注册,然后测试了不同的版本,发现都不能注册,在2.7.5~2.7。11版本不报错,2.7.12版本会报如下NPE错误2021-06-1613:17:31,086[Dubbo-framework-scheduler-thread-1]ERRORorg.apache.dubbo.config.bootstrap。DubboBootstrap(DubboBootstrap.java:1172)-[DUBBO]refreshmetadataandinstancefailed,dubboversion:2.7.12,currenthost:172.23.233.52java.lang.NullPointerExceptionatorg.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.calInstanceRevision(ServiceInstancejavadataUtils).2dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.lambda$refreshMetadataAndInstance$6(ServiceInstanceMetadataUtils.java:272)atjava.util.ArrayList.forEach(ArrayList.java:1259)atorg.apache.dubbo.registry.client.metadata.ServiceInstanceInstanceMetadataUtils。refreshMetadata和(ServiceInstanceMetadataUtils.java:271)atorg.apache.dubbo.config.bootstrap.DubboBootstrap.lambda$registerServiceInstance$20(DubboBootstrap.java:1170)atjava.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)在java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308)在java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180)atjava.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294)atjava.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)atjava.util.concurrent.ThreadPoolExecutor$Worker.run(readThread.java:624)atjava.lang.Thread.run(Thread.java:748)推测是服务注册有问题。跟着errorstackdebug,很快定位到问题直接导致NPE位于org.apache.dubbo。registry.client.AbstractServiceDiscovery#register<=2.7.11版本@Overridepublicfinalvoidregister(ServiceInstanceserviceInstance)throwsRuntimeException{this.serviceInstance=serviceInstance;doRegister(serviceInstance);}在2.7.12版本代码顺序调整为@Overridepublicfinalvoidregister(ServiceInstanceserviceInstance)throwsRuntimeException{doRegister(serviceInstance);this.serviceInstance=serviceInstance;}为什么调了代码顺序就报错了?追查后发现,NPE的来源是this.serviceInstance为null,原来的代码在执行doRegister之前为其赋值调整后的代码先执行doRegister,然后赋值。但是,执行diRegister时会抛出异常。不幸的是,这个异常被吃掉了。doRegister的实现如下@Overridepublicfinalvoidregister(ServiceInstanceserviceInstance)throwsRuntimeException{assertDestroyed(REGISTER_ACTION);assertInitialized(REGISTER_ACTION);executeWithEvents(of(newServiceInstancePreRegisteredEvent(serviceDiscovery,serviceInstance)),()->serviceDiscovery.register(serviceInstance),of(newServiceInstanceRegisteredEvent)(serviceDiscovery,serviceInstance)));}并且这个executeWithEvents将以事件的形式发送异常;try{action.execute();}catch(Throwablee){dispatchEvent(newServiceDiscoveryExceptionEvent(this,serviceDiscovery,e));}afterEvent.ifPresent(this::dispatchEvent);}但是这个事件抛出后没有处理out,表示异常被吃掉了。这就是为什么之前的dubbo版本没有抛出异常,也无法注册服务的原因。这个例外是什么?va.lang.NoClassDefFoundError:org/apache/curator/x/discovery/ServiceDiscovery实际上只是少引入了一个依赖项。这个问题可以通过添加如下
