Dubbo是阿里的一个开源框架,后来捐给了Apache,所以现在叫ApacheDubbo,但是在日常生活中,很多人更喜欢叫它Dubbo简而言之。ApacheDubbo是一个为大规模微服务实践提供高性能RPC通信、流量管理、可观察性等解决方案的微服务框架,涵盖Java、Golang等语言SDK实现。Dubbo版本已经进入3.0+时代。国内很多公司都在使用Dubbo3.0+版本,比如:阿里云、饿了么、平安健康、烽火通信、小米等,大部分公司应该还是停留在2.7+版本。由此可见(我不想再废话了),学习和掌握Dubbo还是很重要的。如果只是用Dubbo,其实不难,但是要想弄清楚它背后的实现原理,真的没那么简单。下面说说模板方法模式在Dubbo中的使用。模板方法模式模板方法模式代码通用模板:publicclassabstractparentclass{method(){first();第二();做业务();第三();abstractdoBusi();}publicclassConcreteimplementationclass1extendsparentclass{doBusi(){//我想变得更富有}}publicclassConcreteimplementationclass2extendsparentclass{doBusi(){//我要变Getmorecultured}}publicclass具体实现class3extendsparentclass{doBusi(){//我要变得更帅}}//...更多的子类只要看任何框架的任何源码就可以实现这种类型,它使用的是模板方法模式。我们可以使用上面的方法去Dubbo,只要有类似的地方,就是Dubbo中使用了模板方法模式。场景1我们的提供商通常会为同一服务部署多个节点以实现高可用性。客户端拿到服务列表后,需要从服务列表中选择一个服务发起调用。Dubbo中有四种负载均衡算法。ConsistentHash,具有相同参数的请求总是发送给相同的提供者。当某个provider宕机时,原本发送给该provider的请求会基于虚拟节点扩散到其他provider,不会引起剧烈变化。最小活跃调用数,相同活跃数的随机数,活跃数是指调用前后的计数差值。使慢的提供者接收到更少的请求,因为较慢的提供者调用前后的计数差异会更大。Random,按权重设置随机概率。一个section的碰撞概率高,但是调用量越大,分布越均匀,使用概率后权重也越均匀,有利于动态调整provider权重。Roundrobin,按照约定后的权重设置轮转比例。有一个慢provider积累请求的问题,比如:第二台机器很慢,但是没有挂掉,当请求转到第二台机器的时候卡在那里。久而久之,所有的请求都卡在了第二台机器上。以上是Dubbo中负载均衡部分的类关系图。从图中可以看出,AbstractLoadBalance是各个具体负载均衡算法的父类。我们来看看AbstractLoadBalance是如何实现的:{返回空值;}if(invokers.size()==1){返回调用者。得到(0);}returndoSelect(invokers,url,invocation);}protectedabstractInvokerdoSelect(List>a,URLurl,bc);我们再用IDEA,很容易就知道这个方法的具体实现类:这不就是Dubbo中所谓的模板方法模式的使用场景之一吗?再来看另外一个场景:场景2说到Dubbo,我们通常会想到注册中心。Dubbo支持的注册中心非常多,比如:Zookeeper、Naocos、Redis等。我们来看一下类关系图:从类关系图中我们可以看出Dubbo支持的注册中心还是蛮多的。接下来我们看一下注册函数在父类FailbackRegistry中的实现)。publicabstractclassFailbackRegistryextendsAbstractRegistry{publicvoidregister(URLurl){//销毁,跳过if(destroyed.get()){return;}//添加到`registered`变量super.register(url);//移除`failedRegistered``failedUnregistered`变量failedRegistered.remove(url);failedUnregistered.remove(url);try{//向注册中心发送注册请求doRegister(url);}catch(Exceptione){Throwablet=e;//如果开启了启动检测,则直接抛出Exception。//如果开启了启动检测,则直接抛出Exception。布尔检查=getUrl().getParameter(Constants.CHECK_KEY,true)&&url.getParameter(Constants.CHECK_KEY,true)&&!Constants.CONSUMER_PROTOCOL.equals(url.getProtocol());//非消费者。在`ReferenceConfig#createProxy(...)`方法中,消费者将//调用`Invoker#avatable()`方法进行检查。booleanskipFailback=tinstanceofSkipFailbackWrapperException;如果(检查||skipFailback){如果(skipFailback){t=t.getCause();}thrownewIllegalStateException("无法将"+url+"注册到注册表"+getUrl().getAddress()+",cause:"+t.getMessage(),t);}else{logger.error("注册失败"+url+",等待重试,原因:"+t.getMessage(),t);}//将失败的注册请求记录到`failedRegistered`,定期重试//将失败的注册请求记录到失败列表,定期重试failedRegistered.add(url);}}//钩子方法protectedabstractvoiddoRegister(URLurl);}这段代码有点多,估计有的同学看得头疼,我们把上面的代码精简一下:publicabstractclassFailbackRegistryextendsAbstractRegistry{publicvoidregister(URLurl){//Destroyed,跳过if(destroyed.get()){return;}//添加到`registered`变量super.register(url);//删除`failedRegistered``failedUnregistered`变量failedRegistered.remove(url);失败注销。删除(网址);try{//向注册中心发送注册请求doRegister(url);}catch(Exceptione){failedRegistered.add(url);}}//HookmethodprotectedabstractvoiddoRegister(URLurl);}这样看,不就是我们前面说的模板方法模式代码的通用模板吗?我们在查看源码的时候,只要看到上面的通用代码模板是类似的,我们就可以认为这就是模板方法模式在Dubbo中的应用。这时候需要注意的是,真正的实现基本上是在这个方法里面加上doXxx()。至于其他的,我建议你查看源代码。如果你能找到它,那就证明你真的明白我的意思。如果只是用Dubbo,其实不难,但是要想弄清楚它背后的实现原理,真的没那么简单。