IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> SPI机制 -> 正文阅读

[网络协议]SPI机制

Dubbo spi机制是在jdk spi机制的基础上做了功能增强处理,在实际使用过程中是否需要dubbo的spi机制具体还是要看业务场景,在介绍dubbo spi之前,我们先对jdk的spi机制做个简单了解。

SPI英文全称Service Provider Interface,中译服务提供接口,先看下它的运行机制:将接口的服务实现类的全限定名配置在文件中,在具体使用服务过程中通过服务加载器读取配置文件,加载具体的服务实现类。
在这里插入图片描述
接下来再看下它的一个基本使用。

  1. 首先需要定义一个接口类,如Action:

    public interface Action {
    
        void doAction();
    }
    
  2. 根据实际业务逻辑创建一个实现类,如HelloAction

    public class HelloAction implements Action {
    
        public HelloAction() {
            System.out.println("****HelloAction  constructor******");
        }
    
        @Override
        public void doAction() {
            System.out.println("*****************HelloAction***********************");
        }
    }
    
  3. 定义配置文件,在META-INF/services目录下(没有就新建)新建一个以接口类全限定名命名的文件,如:org.xx.xx.Action,在文件中添加具体的实现,内容为实现类全限定名,如果有多个实现类以换行符为分隔符

    org.xx.xx.HelloAction
    org.xx.xx.WorldAction
    
  4. 最后一步就是编写具体的服务查找代码

    ServiceLoader<Action> serviceLoader = ServiceLoader.load(Action.class);
    Iterator<Action> iterator = serviceLoader.iterator();
    while (iterator.hasNext()){
        iterator.next().doAction();
    }
    

Jdk spi机制有几个约定:

  • 服务接口配置文件必须在classpath的META-INF/services目录下。
  • 实现类要有无参构造函数。
  • 服务提供接口可以是接口类或抽象类,其实用具体的实现类也是可以正常运行的,但是没意义。

Dubbo与jdk最大的不同是对扩展实现这块做了分类处理,它将服务实现分为以下几类:

在这里插入图片描述

  • 包装扩展点:服务实现类以服务接口作为构造函数参数,包装的对象是普通扩展点和集合扩展点,一般是用来增加额外逻辑处理,比如调用的Filter,引用暴露的Listener。
  • 集合扩展点:部分场景中需要同时使用多个实现的时候,可以用dubbo内置的激活机制来获取。
  • 自适应扩展点:设计出它的目的就是为了能够在程序执行过程中动态调整所需的扩展实现。目前dubbo中的自适应扩展点分为两种,一是通过@Adaptive来指定一个扩展实现作为自适应扩展点,二是dubbo根据预先设定的逻辑自动生成一个代理类在执行方法时根据参数动态选择具体扩展实现。
  • 普通扩展点:除了包装扩展点和自适应扩展点之外的实现。

dubbo spi的加载过程:

dubbo自定义了扩展加载器ExtensionLoader,加载服务实现的过程中,首先会通过加载策略加载接口的所有服务实现类,再使用反射机制实例化具体服务实现,如果有需要还会对其属性做IOC操作,对其本身做包装操作。

dubbo spi的基本使用:

  1. 定义接口并使用@SPI进行类注解,以dubbo内置的org.apache.dubbo.rpc.Filter接口为例

    @SPI
    public interface Filter {
        /**
         * Make sure call invoker.invoke() in your implementation.
         */
        Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException;
    
        interface Listener {
    
            void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation);
    
            void onError(Throwable t, Invoker<?> invoker, Invocation invocation);
        }
    
    }
    
  2. 创建实现类ProviderGenericFilter(ps:invoke方法中的业务逻辑可以根据自己的业务需要编写代码)

    public class ProviderGenericFilter implements Filter {
    
        @Override
        public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
            Class<?>[] parameterTypes = invocation.getParameterTypes();
            if(parameterTypes.length == 0){
                return invoker.invoke(invocation);
            }
            String[] parameterTypeArray = new String[parameterTypes.length];
            for(int index = 0;index<parameterTypes.length;index++){
                parameterTypeArray[index] = parameterTypes[index].getName();
            }
    
            try {
                Method method = ReflectUtils.findMethodByMethodSignature(invoker.getInterface(), invocation.getMethodName(), parameterTypeArray);
                Type[] genericParameterTypes = method.getGenericParameterTypes();
                for(int i = 0;i<invocation.getArguments().length;i++){
                    Object argument = invocation.getArguments()[i];
                    if(argument != null && genericParameterTypes[i] instanceof ParameterizedType ){
                        Object realize = PojoUtils.realize(argument, parameterTypes[i], genericParameterTypes[i]);
                        invocation.getArguments()[i] = realize;
                    }
                }
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            return invoker.invoke(invocation);
        }
    
  3. 配置服务实现,在META-INF/dubbo/目录下创建org.apache.dubbo.rpc.Filter文件名,然后以键值对的格式来配置服务实现:

    providerGenericFilter=org.xx.Filter.ProviderGenericFilter

  4. 获取服务实现

    ExtensionLoader.getExtensionLoader(Action.class).getExtension(“providerGenericFilter”)

自适应扩展点的使用:

  1. 定义接口并使用@SPI进行类注解,可以配置默认的扩展名

    @SPI("refer")
    public interface IListener {
    
        @Adaptive
        void request(URL url);
    
    }
    

    要使用自适应机制,需要采用@Adaptive注解。这里有两种情况需要区分对待:注解在服务实现类上时,则会把当前类当作自适应扩展点来调用,注解在接口方法上,则dubbo会按照预先设定的逻辑生成一个代理类,在真正调用方法时会根据URL配置找到真实使用的扩展实现。

  2. 创建实现,这里我创建了两个实现ReferListener和ExportListener以便清晰展示动态调整扩展点

    public class ReferListener implements IListener {
        @Override
        public void request(URL url) {
            System.out.println("ReferListener ------>>>request");
        }
    }
    
    public class ExportListener implements IListener {
        @Override
        public void request(URL url) {
            System.out.println("ExportListener ------>>>request");
        }
    }
    
  3. 配置服务实现,这点和基本使用中的一样

    export=org.nickyu.spi.ExportListener
    refer=org.nickyu.spi.ReferListener
    
  4. 自适应扩展的具体使用

    public static void main(String[] args) {
        IListener adaptiveExtension = ExtensionLoader.getExtensionLoader(IListener.class).getAdaptiveExtension();
        URL url = new URL("", "", 0).addParameter("i.listener","refer");
        adaptiveExtension.request(url);
    }
    

    结果输出:

在这里插入图片描述

将refer替换成export,运行并观察结果

在这里插入图片描述

dubbo默认会将当前类名加以处理当作URL中的key来获取扩展点,如果不想用默认值也可以通过@Adaptive来指定key

集合扩展点的使用:

每个扩展点都需要@Activate进行注解并配置好组名,dubbo通过匹配定义好的组名来查找具体的扩展实现,以下举例获取Dubbo的Filter接口provider组实现:

URL url = new URL("", "", -1);
String[] names = new String[]{};
List<Filter> activateExtension = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(url, names, CommonConstants.PROVIDER);

有的扩展实现还会在@Activate注解上定义value,这是个数组,想要获取这样的扩展点还需要在URL中配置指定key(可以简单理解为value中的值)和其参数值,以下举例在获取组provider的具体实现基础上额外获取CacheFilter:

URL url = new URL("", "", -1).addParameter("cache", "lru");
String[] names = new String[]{};
List<Filter> activateExtension = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(url, names, CommonConstants.PROVIDER);
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-03-22 20:58:43  更:2022-03-22 21:00:23 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 5:18:12-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码