dubbo架构设计与源码解析(二) 服务注册-凯发app官方网站

凯发app官方网站-凯发k8官网下载客户端中心 | | 凯发app官方网站-凯发k8官网下载客户端中心
  • 博客访问: 85511
  • 博文数量: 165
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1655
  • 用 户 组: 普通用户
  • 注册时间: 2022-09-26 14:37
文章分类

全部博文(165)

文章存档

2024年(2)

2023年(95)

2022年(68)

我的朋友
相关博文
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·

分类: 架构设计与优化

2022-12-21 10:00:46

一、dubbo 简介

dubbo 是一款典型的高扩展、高性能、高可用的 rpc 微服务框架,用于解决微服务架构下的服务治理与通信问题。其核心模块包含【rpc 通信】【服务治理】,其中服务治理又分为服务注册与发现、服务容错、负载均衡、流量调度等。今天将重点介绍 dubbo 的服务注册与发现

二、spi 机制

在介绍服务注册发现之前,先简单介绍一下贯穿整个 dubbo 源码,也是 dubbo 实现自适应扩展的核心 --spi 机制,下图为 dubbo spi 实现的简单类图。

?

??

 

? 1、dubbo spi 原理:通过读取相应的配置文件找到具体实现类,然后通过以下两种方式实例化对象:(1)通过自适应动态字节码编译技术,生成相应的动态代理类,(2)利用反射机制实现实例化。相较于 java spi,dubbo spi 实现了内部的 ioc 和 aop
? 2、dubbo spi 优点:(1)高扩展:用户可以根据实际业务需求扩展相应的实现模块,包含字节码编译技术、rpc 协议、通信方式、注册方式等,(2)解耦:通过封装 spi 调用机制,架构上实现了上层应用与底层逻辑之间的解耦,为高扩展提供了支撑条件
? 3、dubbo spi 常用样例(以 getextension 和 getadaptiveextension 为例)
配置文件内容
test1=com.dubbo.demo.service.testserviceimpl test2=com.dubbo.demo.service.testserviceimpl2 一、通过getextension方法生成实例 extensionloader extensionloader =extensionloader.getextensionloader(testservice.class);testservice t1 = extensionloader.getextension("test1");testservice t2 = extensionloader.getextension("test2"); 二、通过getadaptiveextension生成实例(方法中需要@adaptive注解,参数会对url校验) testservice testservice =extensionloader.getextensionloader(testservice.class).getadaptiveextension();url url =newurl("test","localhost",8080,newstring[]{"test.service","test1"}); testservice.sayhello("bbb", url);

调用 getadaptiveextension 方法{banned}最佳终会生成相应的代理类,{banned}最佳终生成的代理类会根据 url 参数里面的 protocol 决定(以内部 protocol 为例

?

???

三、服务注册

1、服务注册流程

?

??

 

2、服务注册类图详解

?

??

 

3、服务注册步骤

(1)步骤一:初始化配置(类图:抽象 config 与初始化配置)

首先需要实例化 serviceconfig 实例,声明 “注册接口、接口实例、注册中心配置”,其中 “servicebean” 是实现 spring 与 dubbo 整合的桥梁。然后会由 dubbobootstrap 调用 initialize 方法实现 configmanager 和 environment 的初始化,其中就包括将 serviceconfig 中的配置转换成内部封装的协议(applicationmodel、providermodel 等

privatestaticvoidstartwithexport()throwsinterruptedexception{//初始化配置serviceconfig service =newserviceconfig<>(); service.setinterface(demoservice.class); service.setref(newdemoserviceimpl()); service.setapplication(newapplicationconfig("dubbo-demo-api-provider")); service.setregistry(newregistryconfig("zookeeper://127.0.0.1:2181"));//服务注册入口 service.export();}
publicsynchronizedvoidexport(){if(bootstrap ==null){ bootstrap =dubbobootstrap.getinstance();// compatible with api call.if(null!=this.getregistry()){ bootstrap.registries(this.getregistries());}//初始化配置() bootstrap.initialize();}......if(shoulddelay()){ delay_export_executor.schedule(this::doexport,getdelay(),timeunit.milliseconds);}else{//服务注册doexport();}exported();}

(2)步骤二:组装 url

根据初始化配置组转注册接口服务的 url。其中 url 也是 dubbo 内部通过 @adaptive 注解实现 spi 的核心,通过修改 url 的头部协议(如:register、dubbo、injvm 等),在调用

private static final protocol protocol = extensionloader.getextensionloader(protocol.class).getadaptiveextension();
protocol.export(wrapperinvoker)

该方法的时候,会根据不同的协议切换不通的实现类,实现了 dubbo 技术架构与业务逻辑的解耦。

privatevoiddoexporturls(){//组装后的url格式样例//registry://127.0.0.1:2181/org.apache.dubbo.registry.registryservice?application=dubbo-demo-api-provider&dubbo=2.0.2&pid=26212?istry=zookeeper×tamp=1663049763199list registryurls =configvalidationutils.loadregistries(this,true);int protocolconfignum = protocols.size();for(protocolconfig protocolconfig : protocols){//组装pathkey : org.apache.dubbo.demo.demoservicestring pathkey = url.buildkey(getcontextpath(protocolconfig).map(p -> p "/" path).orelse(path), group, version);//保存接口服务 repository.registerservice(pathkey, interfaceclass);//服务注册doexporturlsfor1protocol(protocolconfig, registryurls, protocolconfignum);}}

(3)步骤三:invoker 封装(类图:ref -> invoker)

通过内置的动态字节码编译(默认 javassist)生成 invoker 代理类,然后通过反射机制生成 wrapper 实例。其中 invoker 是 dubbo 的核心模型,invoker 是 dubbo 中的实体域,也就是真实存在的。其他模型都向它靠拢或转换成它

privatevoiddoexporturlsfor1protocol(protocolconfig protocolconfig,list registryurls,int protocolconfignum){......//组装新的url//dubbo://2.0.0.1:20880/org.apache.dubbo.demo.demoservice?anyhost=true&application=dubbo-demo-api-provider&bind.ip=2.0.0.1&bind.port=20880&default=true&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.demoservice&methods=sayhello,sayhelloasync&pid=46528&release=&service.name=servicebean:/org.apache.dubbo.demo.demoservice&side=provider×tamp=1663051456562url url =newurl(name, host, port,getcontextpath(protocolconfig).map(p -> p "/" path).orelse(path), map);......//invoker封装invoker invoker = proxy_factory.getinvoker(ref,(class) interfaceclass, registryurl.addparameterandencoded(export_key, url.tofullstring()));//wrapperdelegateprovidermetadatainvoker wrapperinvoker =newdelegateprovidermetadatainvoker(invoker,this);//服务注册(此时url头部协议变成了register,实际会调用registryprotocol)exporter exporter = protocol.export(wrapperinvoker); exporters.add(exporter);} # proxy_factory publicinvokergetinvoker(t proxy,class type,url url){// 动态代理类生成,反射生成实例finalwrapper wrapper =wrapper.getwrapper(proxy.getclass().getname().indexof('$')<0? proxy.getclass(): type);returnnewabstractproxyinvoker(proxy, type, url){@overrideprotectedobjectdoinvoke(t proxy,string methodname,class[] parametertypes,object[] arguments)throwsthrowable{return wrapper.invokemethod(proxy, methodname, parametertypes, arguments);}};}

(4)步骤四:exporter 封装(类图:invoker-> exporter)

此时会依次调用 registryprotocol 、dubboprotocol 将 invoker 封装成 exporter,并将封装后的 exporter 存储到本地 map 中(类似于 spring bean)。然后会调用底层通信服务(默认 netty)进行端口监听,此时会通过责任链模式封装 exchanger 与 transporter,用于处理网络传输消息的编码 / 解码。

# registryprotocol: export publicexporterexport(finalinvoker origininvoker)throwsrpcexception{......//此时url头部协议已变成dubbo//dubbo://2.0.0.1:20880/org.apache.dubbo.demo.demoservice?anyhost=true&application=dubbo-demo-api-provider&bind.ip=2.0.0.1&bind.port=20880&default=true&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.demoservice&methods=sayhello,sayhelloasync&pid=56036&release=&service.name=servicebean:/org.apache.dubbo.demo.demoservice&side=provider×tamp=1663052353098 providerurl =overrideurlwithconfig(providerurl, overridesubscribelistener);// export invokerfinalexporterchangeablewrapper exporter =dolocalexport(origininvoker, providerurl);// 此时registry实例默认是zookeeperregistryfinalregistry registry =getregistry(origininvoker);finalurl registeredproviderurl =geturltoregistry(providerurl, registryurl);// decide if we need to delay publishboolean register = providerurl.getparameter(register_key,true);if(register){//底层调用zk,创建node节点 registry.register(registeredproviderurl);}....} # registryprotocol: dolocalexport privateexporterchangeablewrapperdolocalexport(finalinvoker origininvoker,url providerurl){string key =getcachekey(origininvoker);return(exporterchangeablewrapper) bounds.computeifabsent(key, s ->{invoker> invokerdelegate =newinvokerdelegate<>(origininvoker, providerurl);//此时会调用dubboprotocol进行exporter封装returnnewexporterchangeablewrapper<>((exporter) protocol.export(invokerdelegate), origininvoker);});}
# dubboprotocol: export publicexporterexport(invoker invoker)throwsrpcexception{......// export service.string key =servicekey(url);//exporter封装dubboexporter exporter =newdubboexporter(invoker, key, exportermap); exportermap.put(key, exporter);......//开启服务监听openserver(url);optimizeserialization(url);return exporter;}

(5)步骤五:注册服务节点

封装 exporter 并开启服务端口监听后,会调用注册中心(默认 zookeeper)注册服务节点信息

# registryprotocol: export public<t>exporter<t>export(finalinvoker<t> origininvoker)throwsrpcexception{......//此时url头部协议已变成dubbo//dubbo://2.0.0.1:20880/org.apache.dubbo.demo.demoservice?anyhost=true&application=dubbo-demo-api-provider&bind.ip=2.0.0.1&bind.port=20880&default=true&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.demoservice&methods=sayhello,sayhelloasync&pid=56036&release=&service.name=servicebean:/org.apache.dubbo.demo.demoservice&side=provider×tamp=1663052353098 providerurl =overrideurlwithconfig(providerurl, overridesubscribelistener);// export invokerfinalexporterchangeablewrapper<t> exporter =dolocalexport(origininvoker, providerurl);// 此时registry实例默认是zookeeperregistryfinalregistry registry =getregistry(origininvoker);finalurl registeredproviderurl =geturltoregistry(providerurl, registryurl);// decide if we need to delay publishboolean register = providerurl.getparameter(register_key,true);if(register){//底层调用zk,创建node节点 registry.register(registeredproviderurl);}....}

四、总结

至此,dubbo 服务注册的整体流程已大致结束,文中如有不当或者错误观点,欢迎大家评论区指出。感兴趣的同学,可以关注后续 “dubbo 架构设计与源码解析” 系列的文章。

阅读(255) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
")); function link(t){ var href= $(t).attr('href'); href ="?url=" encodeuricomponent(location.href); $(t).attr('href',href); //setcookie("returnouturl", location.href, 60, "/"); }
网站地图