Dubbo服务的注册与发现

Dubbo服务的注册与发现

前言

最近参与的项目是一个基于Dubbo的项目,在开发过程中有些同事对于Dubbo服务的注册与发现机制,似乎不太了解。所以我抽空和他简单聊了聊我对Dubbo机制的了解。

正文

Java SPI

SPI即服务提供商接口,它是一种动态加载服务实现者的机制,通过JavaSPI我们可以优雅地根据一个接口来获取该接口的所有实现类。

值得注意的是使用的Java SPI需要有以下的前提:
1.需要在 classpath 下创建一个目录,该目录命名必须是:META-INF/service
2.在该目录下创建一个 properties 文件,该文件需要满足以下几个条件 :

  • 文件名必须是扩展的接口的全路径名称
  • 文件内部描述的是该扩展接口的所有实现类
  • 文件的编码格式是 UTF-8

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

PS:META-INF文件夹相当于一个信息包,目录中的文件和目录获得Java 2平台的认可与解释,用来配置应用程序、扩展程序、类加载器和服务。

我们最常用的mysql驱动 mysql-connector-java就是借助JavaSPI来实现的

  • Java定义了java.sql.Driver用于API接口用于进行数据库连接,但是没有去实现它。
  • Java通过SPI机制可以适配不同的数据源,只要其驱动类实现java.sql.Driver接口即可。
    在这里插入图片描述

但是JavaSPI存在一个弊端,Java SPI 在查找扩展实现类的时候遍历 SPI 的配置文件并且将实现类全部实例化,假设一个实现类初始化过程比较消耗资源且耗时,但是你的代码里面又用不上它,这就产生了资源的浪费。所以说 Java SPI 无法按需加载实现类。

Dubbo SPI

Dubbo发现服务实现者的方式也是借助SPI的设计思想,但与JavaSPI还是有区别的。

Dubbo 对配置文件目录的约定,不同于 Java SPI ,Dubbo 分为了三类目录:

  • META-INF/services/ 目录:该目录下的 SPI 配置文件是为了用来兼容 Java SPI 。
  • META-INF/dubbo/ 目录:该目录存放用户自定义的 SPI 配置文件。
  • META-INF/dubbo/internal/ 目录:该目录存放 Dubbo 内部使用的 SPI 配置文件。

Dubbo对外接口和其实现类的对应关系是配置在文件中的,ExtensionLoader会解析文件将数据添加到一个Map中,我们把获取对外接口的实现类的过程称为获取扩展点,它可以分为三种:
自适应扩展点:它是根据class进行匹配,同一个class只能有一个

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

指定名称的扩展点

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

激活扩展点

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

这里以 Dubbo Protocol(RPC协议) 为例

Dubbo 官方支持10种通讯协议,Dubbo通过SPI机制来实现适配不同协议

<dubbo:protocol name="dubbo"  port="20880"  threads="1000" />

Dubbo Protocol会采用自适应的方式进行扩展

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

Dubbo默认提供一个dubbo的Protocol扩展点

  • @SPI("dubbo"): 用来定义扩展点
  • @Adaptive:将目标标记实现了一个适配器类,会通过上面讲的三种方式去获取扩展点,如@Adaptive({Constants.PROXY_KEY})为自适应扩展点
@SPI("dubbo")  
public interface Protocol {  
      
    int getDefaultPort();  
  
    @Adaptive  
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;  
  
    @Adaptive  
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;  


    void destroy();  
  
}

Dubbo SPI 的工作流程

Dubbo SPI不会一下子去将实现类全部实例化,只会在使用到的时候才会去使用ExtensionLoader去获取扩展点。并且通过反射和缓存机制,进一步提升了动态加载服务实现者的性能。

ExtensionLoader去加载扩展点的流程可以概括为:
1.通过类名得到一个ExtensionLoader
2.通过定义的名字从ExtensionLoader找到实现类实例

  • 如果存在缓存,直接从缓存中获取实例
  • 如果没有缓存,通过反射建个实例,然后执行 set
    方法依赖注入。如果有找到包装类的话,再包一层。如果标记了@Adaptive,会将实例放在缓存中。

Dubbo 注册和引用服务的方式

我们在实际的使用过程中并不需要关心 Dubbo SPI,Dubbo本身对Dubbo注册和引用服务进行了更加简洁地封装。

Dubbo引用外部服务

  • @Reference 用在消费端,表明使用的是服务端的什么服务
@Reference(interfaceClass = IUserService.class,retries=0,check=false,timeout = 50000,mockClass=MockUserService.class)
private IUserService iUserService;

可等同于在xml中这样配置

<!-- 消息推送 -->
<dubbo:reference id="user" interface="com.luo.api.service.IUserService" retries="0" timeout="50000"
                 check="false"
                 mock="com.luo.api.service.mockimpl.MockUserService" />

Dubbo注册对外服务

  • @Service 用在服务提供者中,在类或者接口中声明。
@Service(cluster ="failfast")
public class UserServiceImpl implements IUserService{
}

可等同于在xml中这样配置

<!-- dubbo管理平台接口 -->
<bean id="UserService" class="com.luo.producer.rpcservice.UserServiceImpl" />
<dubbo:service interface="com.luo.api.service.IUserService" ref="UserService"  cluster="failfast"/>

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_40990818/article/details/108350098