总体介绍
分层
左边是“服务消费者”使用的接口和类,
右边是“服务提供者”使用的接口和类,
在中间的是双方都用到的接口和类。
总体分成Business层,RPC层,Remoting层
其中,
1)Business层是平时写的业务逻辑的接口和实现类;
2)RPC层是这次的重点:里面又分:
A)config配置层:使用这一层提供的@注解,xml配置等方法来 暴露写好的接口实现类成服务 和 生成远程服务代理类,
B)proxy服务代理层:服务接口透明代理,可以像调本地服务一样调远程服务
C)registry注册中心层:完成服务地址的注册与发现
D)cluster路由层:负载均衡容错处理
E)monitor监控层:RPC调用次数和调用时间监控,发送数据到monitor监控中心
F)protocol远程调用层:封将RPC调用
3)Remoting层:实现dubbo协议,底层使用hessian或其他协议,就不会用到这一层
A)exchange信息交换层:封装请求响应模式,同步转异步,封装Request-Response语义
B)transport网络传输层:对Mina,Netty,Grizzly的抽象,socket TCP,UDP编程
C)serialize数据序列化层:
分模块
1)common公共逻辑模块:包括Util类和通用模型,包括serialize层
2)remoting远程通讯模块:Dubbo协议的实现,包括tansport层和exchange层
3)rpc远程调用模块:抽象各种协议,以及动态代理,包括proxy和protocol层
4)cluster集群模块:处理负载均衡, 容错,路由等
5)registry注册中心模块
6)monitor监控模块
7)config配置模块:对应config层,是Dubbo对外的API,用户通过Config使用Dubbo,隐藏Dubbo所有细节。
8)container容器模块:一个Standlone的容器,以简单的Main加载Spring启动、启动开源服务器,因为服务通常不需要Tomcat/JBoss等Web容器的特性,没必要用Web容器去加载服务。
模块-config配置
3配置方式来开发分布式应用
1)提供xml
在spring bean使用<dubbo:service> <dubbo:reference>来暴露服务和获得远程代理。
<dubbo:registry> <dubbo:protocol>配置注册中心和提供服务的协议等等
2)注解
注解方式则是@Service 和@Reference 。[@Service 不行,Service service = bean.getClass().getAnnotation(Service.class);]有的时候bean是JdkDynamicAopProxy,不能识别到@Service 。
3)properties文件配置方式,用于全局配置:
dubbo.service.xxx;
dubbo.reference.xxx
dubbo.application.xxxx;
dubbo.protocol.xxx;
dubbo.registry.xxxx;
dubbo.monitor.xxx
注解配置说明 ServiceConfig,ReferenceConfig
<dubbo:annotation> 生成AnnotationBean类实例,AnnotationBean实现了BeanPostProcessor接口。
Spring创建每个bean过程中:
1)AnnotationBean的postProcessAfterInitialzation会检验bean是否有@service 注解,有就创建组装一个ServiceBean,并注入这个bean对象。最后再用ServiceConfig的export()来注册和暴露服务。
2)AnnotationBean的postProcessBeforeInitialzation会检测bean是否有@Reference注解的field或setter方法。有就创建一个组装一个ReferenceBean。并使用referenceConfig的get()获得对应field接口类的远程代理对象,并注入到这个bean的field。
其他dubbo定义的标签<dubbo:protocol> <dubbo:registry> 是对应namespace注册的DubboBeanDefinitionParser生成对应的ProtocolConfig,registryConfig等Config类,然后在组装ServiceBean和ReferenceBean时通过applicationContext注入。[大概是这样]
Dubbo的配置覆盖策略[abstractConfig.appendProperties]
JVM启动-D参数优先,这样可以使用户在部署和启动时进行参数重写,比如在启动时需改变协议的端口。
@和XML次之,如果在XML中有配置,则dubbo.properties中的相应配置项无效。
Properties最后,相当于缺省值,只有XML没有配置时,dubbo.properties的相应配置项才会生效,通常用于共享公共配置,比如应用名。
模块-rpc远程调用
解析服务
在ServiceConfig.export()或ReferenceConfig.get()初始化时,将Bean对象转换暴露服务或请求服务的URL,所有Bean属性转成URL的参数。
然后将URL传给Protocol扩展点,基于扩展点的Adaptive机制,根据URL的协议头,进行不同协议的服务暴露或引用。
url参数列只有group,interface,version是服务的匹配条件,三者决定是不是同一个服务,其它配置项均为调优和治理参数
暴露服务
有注册中心配置:<dubbo:registry address="zookeeper://10.20.153.10:2181" />
ServiceConfig解析出的URL的格式为:
registry://registry-host/com.alibaba.dubbo.registry.RegistryService?export=URL.encode("dubbo://service-host/com.foo.FooService?version=1.0.0")
1)基于扩展点的Adaptive机制,通过URL的"registry://"协议头识别,就会调用RegistryProtocol的export()方法,将export参数中的提供者URL,先注册到注册中心,再重新传给Protocol扩展点进行暴露:
dubbo://service-host/com.foo.FooService?version=1.0.0
2)基于扩展点的Adaptive机制,通过提供者URL的"dubbo://"协议头识别,就会调用DubboProtocol的export()方法,打开服务端口。
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
Exporter<?> exporter = protocol.export(invoker);
具体服务àInvoker àExporter Exporter是负载打开服务端口等待请求。
把服务实现类使用JavassisProxyFactory包装成一个invoker。它的getInvoker里使用Wrapper代码生成具体的类,包装服务实现类,通过方法名,参数类型,参数值直接调用对应的服务实现类方法。
接着使用扩展机制得到protocol串,protocolFilterWrapper先包装代理生成的一条每个节点都带filter的invoker链
到protocolListenrWrapper会把最终生成的Exporter包装成一个带监听服务暴露的监听器的Exporter。
到RegistryProtocol,它会先根据服务url的参数暴露服务,使用到HessianProtocol,最后再使用Registry注册服务。
远程请求服务时,就会通过Exporter调用Invoker的invoker,这时就会经过一层层filter,最后调用服务实现类的方法。
HessianSkelen和HessianProxyFactory
引用服务
注册中心配置:<dubbo:registry address="zookeeper://10.20.153.10:2181" />
ReferenceConfig解析出的URL的格式为:
registry://registry-host/com.alibaba.dubbo.registry.RegistryService?refer=URL.encode("consumer://consumer-host/com.foo.FooService?version=1.0.0")
基于扩展点的Adaptive机制,通过URL的"registry://"协议头识别,就会调用RegistryProtocol的refer()方法,基于refer参数中的条件,查询提供者URL,如:
dubbo://service-host/com.foo.FooService?version=1.0.0
基于扩展点的Adaptive机制,通过提供者URL的"dubbo://"协议头识别,就会调用DubboProtocol的refer()方法,得到提供者引用。
然后RegistryProtocol将多个提供者引用,通过Cluster扩展点,伪装成单个提供者引用返回。
invoker = refprotocol.refer(interfaceClass, urls.get(0));
(T) proxyFactory.getProxy(invoker);
拦截服务
基于ProtocolFilterWrapper类,将所有Filter组装成链,在链的最后一节调用真实的引用。
基于ProtocolListenerWrapper类,将所有InvokerListener和ExporterListener组装集合,在暴露和引用前后,进行回调。
包括监控在内,所有附加功能,全部通过Filter拦截实现。
- 用户自定义filter默认在内置filter之后。
- 特殊值default,表示缺省扩展点插入的位置。
- 比如:filter="xxx,default,yyy",表示xxx在缺省filter之前,yyy在缺省filter之后。
- 特殊符号-,表示剔除。
- 比如:filter="-foo1",剔除添加缺省扩展点foo1。
- 比如:filter="-default",剔除添加所有缺省扩展点。
- provider和service同时配置的filter时,累加所有filter,而不是覆盖。
- 比如:<dubbo:provider filter="xxx,yyy"/>和<dubbo:service filter="aaa,bbb" />,则xxx,yyy,aaa,bbb均会生效。
- 如果要覆盖,需配置:<dubbo:service filter="-xxx,-yyy,aaa,bbb" />
几个预置拦截器介绍
accessLogFilter |
Active provider before |
记录每次服务调用的方法、形参、实参 |
ActiveLimitFilter |
Active consumer around |
限制同时请求一个服务方法的请求数,超过最大值就等待直到超时。默认0,没有限制 |
CacheFilter |
Active consumer,provider, before |
获得缓存里的数据 |
ClassLoaderFilter |
Active provider before Order=-30000 |
设置线程上下文的类加载器 |
CompatibleFilter |
|
类型转换,泛型? |
ConsumerContextFilter |
Active consumer before Order=-10000 |
RpcContext设置调用的invoker和invocation |
ContextFilter |
Active provider before Order=-10000 |
RpcContext设置调用的invoker和invocation |
deprecatedFilter |
Active consumer before |
调用过时服务方法检测,url有deprecate参数 |
Echo |
Active consumer provider Order=-110000 |
url有echo参数时,返回参数值 |
exceptionFilter |
Active provider before |
异常处理 |
executerLimitFilter |
Active provider before |
限制同时请求一个服务方法的请求数,超过则抛错 |
FutureFilter |
Active consumer before |
异步或同步 |
GenericFilter |
Active consumer berfore |
|
GenericImplFilter |
Active consumer berfore Order=2000 |
|
MonitorFilter |
Active consumer,provider around |
记录调用日志 |
TimeoutFilter |
Active provider |
服务调用超时 |
其他
1)proxy服务接口透明代理,生成服务的客户端Stub和服务器端Skeleton,以ServiceProxy为中心,扩展接口为ProxyFactory
2)Protocol远程调用层,封将RPC调用,以Invocation, Result为中心,扩展接口为Protocol, Invoker, Exporter
3)只要有Protocol + Invoker + Exporter就可以完成非透明的RPC调用,然后在Invoker的主过程上Filter拦截点。Proxy层封装了所有接口的透明化代理,而在其它层都以Invoker为中心,只有到了暴露给用户使用时,才用Proxy将Invoker转成接口,或将接口实现转成Invoker,也就是去掉Proxy层RPC是可以Run的,只是不那么透明,不那么看起来像调本地服务一样调远程服务。
4)参考其他人共享文档http://humn-chou.iteye.com/blog/1866272
模块-registry注册中心
服务注册和订阅过程
registry,注册中心层,封装服务地址的注册与发现,以服务URL为中心,扩展接口为RegistryFactory, Registry, RegistryService
System.getProperty("user.home") + "/.dubbo/dubbo-registry-"+ConfigUtils.getProperty("dubbo.application.name")+"-" + url.getHost() + ".cache");
模块-cluster集群
负载容错
cluster,路由层,封装多个提供者的路由及负载均衡,并桥接注册中心,以Invoker为中心,扩展接口为Cluster, Directory, Router, LoadBalance
- 这里的Invoker是Provider的一个可调用Service的抽象,Invoker封装了Provider地址及Service接口信息。
- Directory代表多个Invoker,可以把它看成List<Invoker>,但与List不同的是,它的值可能是动态变化的,比如注册中心推送变更。
- Cluster将Directory中的多个Invoker伪装成一个Invoker,对上层透明,伪装过程包含了容错逻辑,调用失败后,重试另一个。
- Router负责从多个Invoker中按路由规则选出子集,比如读写分离,应用隔离等。
- LoadBalance负责从多个Invoker中选出具体的一个用于本次调用,选的过程包含了负载均衡算法,调用失败后,需要重选。
模块-monitor监控
monitor,监控层,RPC调用次数和调用时间监控,以Statistics为中心,扩展接口为MonitorFactory, Monitor, MonitorService
配置
dubbo.monitor.address=127.0.0.1:8080
是filter链上的一个MonitorFilter
DubboMonitor是缓存请求,每隔一段时间再后台一起发送。
extension扩展点介绍
dubbo采用Microkernel + Plugin模式,提供很多SPI来扩展框架
如何操作增加新模块:参考官方spi手册
在扩展类的jar包内,放置扩展点配置文件:META-INF/dubbo/接口全限定名,内容为:配置名=扩展实现类全限定名,多个实现类用换行符分隔。
扩展点使用单一实例加载,扩展实现必须是线程安全性的
1)自动Wrap扩展点的Wrapper类
ExtensionLoader会把加载扩展点时(通过扩展点配置文件中内容),如果该实现有拷贝构造函数,则判定为扩展点Wrapper类。
通过Wrapper类可以把所有扩展点公共逻辑移至Wrapper中。新加的Wrapper在所有的扩展点上添加了逻辑,有些类似AOP(Wraper代理了扩展点)
2)扩展点自动装配
加载扩展点时,自动注入依赖的扩展点
加载扩展点时,扩展点实现类的成员如果为其它扩展点类型,ExtensionLoader在会自动注入依赖的扩展点。
ExtensionLoader通过扫描扩展点实现类的所有set方法来判定其成员。
即ExtensionLoader会执行扩展点的拼装操作。
3)扩展点自适应
扩展点的Adaptive实例
ExtensionLoader注入的依赖扩展点是一个Adaptive实例,直到扩展点方法执行时才决定调用是一个扩展点实现。
Dubbo使用URL对象(包含了Key-Value)传递配置信息。
扩展点方法调用会有URL参数(或是参数有URL成员)
这样依赖的扩展点也可以从URL拿到配置信息,所有的扩展点自己定好配置的Key后,配置信息从URL上从最外层传入。URL在配置传递上即是一条总线。
在Dubbo的ExtensionLoader的扩展点类开对应的Adaptive实现是在加载扩展点里动态生成。指定提取的URL的Key通过@Adaptive注解在接口方法上提供。
4)扩展点自动激活
对于集合类扩展点,比如:Filter, InvokerListener, ExportListener, TelnetHandler, StatusChecker等,
可以同时加载多个实现,此时,可以用自动激活来简化配置 @active
@Activate(group = "provider", value = "xxx") //
只对提供方激活,group可选"provider"或"consumer"
Dubbo的使用
官方文档 spi,api手册
http://code.alibabatech.com/wiki/display/dubbo/Developer+Guide-zh
http://code.alibabatech.com/wiki/display/dubbo/User+Guide-zh
其他
领域模型
在Dubbo的核心领域模型中:
- Protocol是服务域,它是Invoker暴露和引用的主功能入口,它负责Invoker的生命周期管理。
- Invoker是实体域,它是Dubbo的核心模型,其它模型都向它靠扰,或转换成它,它代表一个可执行体,可向它发起invoke调用,它有可能是一个本地的实现,也可能是一个远程的实现,也可能一个集群实现。
- Invocation是会话域,它持有调用过程中的变量,比如方法名,参数等。
基本原则
- 采用Microkernel + Plugin模式,Microkernel只负责组将Plugin,Dubbo自身的功能也是通过扩展点实现的,也就是Dubbo的所有功能点都可被用户自定义扩展所替换。
- 采用URL作为配置信息的统一格式,所有扩展点都通过传递URL携带配置信息。
属性配置
1)
方法级优先,接口级次之,全局配置再次之。
如果级别一样,则消费方优先,提供方次之。
其中,服务提供方配置,通过URL经由注册中心传递给消费方。
建议由服务提供方设置超时,因为一个方法需要执行多长时间,服务提供方更清楚,如果一个消费方同时引用多个服务,就不需要关心每个服务的超时设置。
2)xml配置和properties配置映射
将XML配置的标签名,加属性名,用点分隔,多个属性拆成多行:
比如:dubbo.application.name=foo等价于<dubbo:application name="foo" />
比如:dubbo.registry.address=10.20.153.10:9090等价于<dubbo:registry address="10.20.153.10:9090" />
如果XML有多行同名标签配置,可用id号区分,如果没有id号将对所有同名标签生效:
比如:dubbo.protocol.rmi.port=1234等价于<dubbo:protocol id="rmi" name="rmi" port="1099" /> (协议的id没配时,缺省使用协议名作为id)
比如:dubbo.registry.china.address=10.20.153.10:9090等价于<dubbo:registry id="china" address="10.20.153.10:9090" />
日志部分
1)配置
Dubo.application.logger=slf4j|jcl|log4j|jdk
Admin
想注册中心订阅,获得信息。
<dubbo:reference id="registryService" interface="com.alibaba.dubbo.registry.RegistryService" check="false" />
目前系统如何扩展dubbo和一些功能
1) 修改的dubbo
暴露“获取服务器信息”服务时的url拼接,要再通常的服务url后增加表示服务器资源信息的参数;
2)扩展功能,新增工程
Filter dispatchLog=com.alibaba.dubbo.log.support.DispatchLogFilter 日志记录
LoadBalance parameter=com.alibaba.dubbo.rpc.cluster.loadbalance.parameter.ParameterLoadBalance动态权重轮询
Cluster failfast_ext=com.alibaba.dubbo.rpc.cluster.support.parameter.FailfastClusterExt