RPC框架

DUBBO

一、dubbo是什么?

dubbo是⼀个分布式服务框架,提供⾼性能和透明化的RPC远程服务调⽤⽅案,以及SOA服务治理方案。说白了其实dubbo就是一个远程调用的分布式框架。dubbo底层默认采用hessian作为序列化协议(支持压缩)

二、dubbo的核心服务是什么?

1.远程通讯: 提供对多种基于长连接的NIO框架抽象封装,包括多种线程模型,序列化,以及“请求-响应”模式的信息交换方式。

2.集群容错: 提供基于接口方法的透明远程过程调用,包括多协议支持,以及软负载均衡,失败容错,地址路由,动态配置等集群支持。

3.自动发现: 基于注册中心目录服务(zookeeper),使服务消费方能动态的查找服务提供方,使地址透明,使服务提供方可以平滑增加或减少机器。

1、Dubbo的SPI机制

在Dubbo中,SPI贯穿整个Dubbo的核心,所以理解Dubbo中的SPI对于理解Dubbo的原理有着至关重要的作用。在Spring中,我们知道SpringFactoriesLoader这个类,它也是一种SPI机制。

关于Java SPI

在了解Dubbo的SPI机制之前,我们先了解下Java提供的SPI (service provider interface) 机制,SPI是JDK内置的一种服务提供发现机制。目前市面上很多框架都用它来做服务的扩展发现。简单的说,它是一种动态替换发现的机制。

举个简单的例子,我们想在运行时动态给它添加实现,你只需要添加一个实现,然后把新的实现描述给JDK知道就行了。大家耳熟能详的如JDBC,日志框架都有用到。

实现 Java SPI 需要遵循的标准

我们如何去实现一个标准的 SPI 发现机制呢?其实很简单,只需要满足以下提交就行了 :

1、需要在 classpath 下创建一个目录,该目录命名必须是:META-INF/service

2、在该目录下创建一个 properties 文件,该文件需要满足以下几个条件 :

2.1 文件名必须是扩展的接口的全路径名称

2.2 文件内部描述的是该扩展接口的所有实现类

2.3 文件的编码格式是 UTF-8

3、通过 java.util.ServiceLoader 的加载机制来发现

Java SPI 的实际应用

SPI 在很多地方有应用,可能大家都没有关注,最常用的就是 JDBC 驱动,我们来看看是怎么应用的。

JDK 本身提供了数据访问的 api。在 java.sql 这个包里面 ,我们在连接数据库的时候,一定需要用到 java.sql.Driver 这个接口对吧。然后去看了下 java.sql.Driver 的源码,发现 Driver 并没有实现,而是提供了一套标准的 api 接口。因为我们在实际应用中用的比较多的是 mysql,所以我们去 mysql 的包里面看到一个如下的目录结构

 

这个java.sql.Driver文件里就指明了mysql的驱动实现类

Java SPI 的缺点

1、JDK 标准的 SPI 会一次性加载实例化扩展点的所有实现,什么意思呢?就是如果你在 META-INF/service 下的文件里面加了 N 个实现类,那么 JDK 启动的时候都会一次性全部加载。那么如果有的扩展点实现初始化很耗时或者如果有些实现类并没有用到, 那么会很浪费资源

2、如果扩展点加载失败,会导致调用方报错,而且这个错误很难定位到是这个原因

Dubbo中的SPI机制

Dubbo也用了SPI思想,不过没有用JDK的SPI机制,是自己实现的一套SPI机制。在Dubbo的源码中,很多地方会存在下面这样的三种代码,分别是自适应扩展点、指定名称的扩展点、激活扩展点。

ExtensionLoader.getExtensionLoader(xxx.class).getAdaptiveExtension();

ExtensionLoader.getExtensionLoader(xxx.class).getExtension(name);

ExtensionLoader.getExtensionLoader(xxx.class).getActivateExtension(url, key);

比如,Protocol接口,在运行的时候dubbo会判断一下应该选用这个Protocol接口的哪个实现类来实例化对象。

它会去找你配置的Protocol,将你配置的Protocol实现类加载到JVM中来,然后实例化对象,就用你配置的那个Protocol实现类就可以了。

Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

总的来说,思路和 SPI 是差不多。都是基于约定的路径下制定配置文件。目的是通过配置的方式轻松实现功能的扩展。相比JDK SPI不同的地方是。会用key-value的方式保存实现类,在ExtensionLoader 中。从而可以加载指定某实现类以及自动注入

2、dubbo原理

解析服务

1、基于 dubbo.jar 内的 META-INF/spring.handlers 配置,Spring 在遇到 dubbo 名称空间时,会回调 DubboNamespaceHandler。

2、所有 dubbo 的标签,都统一用 DubboBeanDefinitionParser 进行解析,基于一对一属性映射,将 XML 标签解析为 Bean 对象。

3、在 ServiceConfig.export() 或 ReferenceConfig.get() 初始化时,将 Bean 对象转换 URL 格式,所有 Bean 属性转成 URL 的参数。

4、然后将 URL 传给 协议扩展点,基于扩展点的 扩展点自适应机制,根据 URL 的协议头,进行不同协议的服务暴露或引用。

暴露服务

1. 只暴露服务端口:

在没有注册中心,直接暴露提供者的情况下,ServiceConfig 解析出的 URL 的格式为:dubbo://service-host/com.foo.FooService?version=1.0.0。

基于扩展点自适应机制,通过 URL 的 dubbo:// 协议头识别,直接调用 DubboProtocol的 export() 方法,打开服务端口。

2. 向注册中心暴露服务:

在有注册中心,需要注册提供者地址的情况下,ServiceConfig 解析出的 URL 的格式为: registry://registry-host/com.alibaba.dubbo.registry.RegistryService?export=URL.encode("dubbo://service-host/com.foo.FooService?version=1.0.0"),

基于扩展点自适应机制,通过 URL 的 registry:// 协议头识别,就会调用 RegistryProtocol 的 export()方法,将 export 参数中的提供者 URL,先注册到注册中心。

再重新传给 Protocol 扩展点进行暴露: dubbo://service-host/com.foo.FooService?version=1.0.0,然后基于扩展点自适应机制,通过提供者 URL 的 dubbo:// 协议头识别,就会调用 DubboProtocol 的 export()方法,打开服务端口。

引用服务

1. 直连引用服务:

在没有注册中心,直连提供者的情况下,ReferenceConfig 解析出的 URL 的格式为:dubbo://service-host/com.foo.FooService?version=1.0.0。

基于扩展点自适应机制,通过 URL 的 dubbo:// 协议头识别,直接调用 DubboProtocol 的 refer() 方法,返回提供者引用。

2. 从注册中心发现引用服务:

在有注册中心,通过注册中心发现提供者地址的情况下,ReferenceConfig 解析出的 URL 的格式为:registry://registry-host/com.alibaba.dubbo.registry.RegistryService?refer=URL.encode("consumer://consumer-host/com.foo.FooService?version=1.0.0")。

基于扩展点自适应机制,通过 URL 的 registry:// 协议头识别,就会调用 RegistryProtocol 的 refer()方法,基于 refer 参数中的条件,查询提供者 URL,如: dubbo://service-host/com.foo.FooService?version=1.0.0。

基于扩展点自适应机制,通过提供者 URL 的 dubbo:// 协议头识别,就会调用 DubboProtocol 的 refer()方法,得到提供者引用。

然后 RegistryProtocol 将多个提供者引用,通过 Cluster 扩展点,伪装成单个提供者引用返回。

拦截服务

基于扩展点自适应机制,所有的 Protocol 扩展点都会自动套上 Wrapper 类。

基于 ProtocolFilterWrapper 类,将所有 Filter 组装成链,在链的最后一节调用真实的引用。

基于 ProtocolListenerWrapper 类,将所有 InvokerListener 和 ExporterListener 组装集合,在暴露和引用前后,进行回调。

包括监控在内,所有附加功能,全部通过 Filter 拦截实现。

服务提供方与消费方分析

1、服务提供方

步骤

调用顺序

备注

容器启动

com.alibaba.dubbo.container.Main.main(args);dubbo.properties -> dubbo.container -> container.start()container -> spring, log4j, jetty...

[dubbo-container-spring] SpringContainer.java -> [服务提供方]classpath*:META-INF/spring/*.xml -> dubbo.xsd

当直接调用com.alibaba.dubbo.container.Main.main(args)方法启动dubbo时则会默认启动这三个容器

bean初始化

等自定义标签 -> [dubbo-config-spring] DubboNamespaceHandler.java -> new DubboBeanDefinitionParser(ServiceBean.class, true)

spring对bean的初始化操作

[dubbo-config-spring] ServiceBean.java -> afterPropertiesSet()-> setProvider(), setApplication(), setModule(), setRegistries, setMonitor(), setProtocols, setPath() -> export()

自定义标签的解析和初始化。注意,当service标签delay属性配置为负数时,在onApplicationEvent()方法中调用 export()方法注册服务并启动相应的协议的服务器,比如netty服务。

连接注册中心

ServiceBean.export() -> ServiceConfig.java -> doExportUrlsFor1Protocol() -> 暴露协议protocol.export()

protocol=ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension()---Protocol$Adaptive.java-> ExtensionLoader.loadExtensionClasses() -> loadFile()-> META-INF/dubbo/internal/,META-INF/dubbo/,META-INF/services/-> [举例dubbo-rpc-api] 配置文件com.alibaba.dubbo.rpc.Protocol-> filter,listener,mock -> 各类的export()

protocol.export()调用链 -> [dubbo-registry-api] RegistryProtocol.export()-> getRegistry()---(ZookeeperRegistry.java -> 扩展的ZookeeperClient)-> registry.subscribe()加入监听列表

注册到注册中心,

并加入监听列表监听注册中心的消息通知。

启动协议服务器

protocol.export()调用链 -> [dubbo-rpc-rest] RestProtocol.export()

-> doExport() -> server.start(), server.deploy()

根据相应协议启动相应服务器

2、服务消费方

步骤

调用顺序

备注

容器启动

参考服务提供方

 同provider

bean初始化

等自定义标签 -> [dubbo-config-spring] DubboNamespaceHandler.java-> new DubboBeanDefinitionParser(ReferenceBean.class, false)

 同provider

连接注册中心

ReferenceBean.java -> afterPropertiesSet() -> ReferenceConfig.init() -> createProxy() -> refprotocol.refer()

refprotocol=ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension()---Protocol$Adaptive.java-> ExtensionLoader.loadExtensionClasses() -> loadFile()-> META-INF/dubbo/internal/,META-INF/dubbo/,META-INF/services/-> [举例dubbo-rpc-api] com.alibaba.dubbo.rpc.Protocol -> filter,listener,mock -> 各类的refer()

protocol.refer()调用链 -> [dubbo-registry-api] RegistryProtocol.refer()-> getRegistry()---(ZookeeperRegistry.java -> 扩展的ZookeeperClient) -> doRefer()-> RegistryDirectory.subscribe() -> registry.subscribe()

 同provider

服务接口初始化

ReferenceConfig.init() -> createProxy()-> AbstractProxyFactory.getProxy() -> JavassistProxyFactory.getProxy()

service接口在实际运行时是 com.alibaba.dubbo.common.bytecode.Proxy 类的实例

 同provider

HSF、Dubbo和Spring Cloud

上表中从几个方面比较了三者之间的功能,可以发现从服务服务发现、服务治理和服务间调用这些功能上看三者基本都具备,只有Dubbo在服务治理方面稍弱。但其实Dubbo也可以和Spring Cloud Netflix Hystrix整合。而HSF和Dubbo使用RPC进行服务间调用,效率上比Spring Cloud使用的Http调用要好得多。

SpringCloud:其实它是微服务开发的一整套解决方案,包含众多的组件

HSF和Dubbo相比,Dubbo拓展性更好,而HSF性能会比Dubbo好(因为Dubbo在一个调用链路里面会存在很多Filter的拦截,这个相对于HSF会存在一个的性能差问题。)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值