Dubbo spi机制_dubbo负载均衡

Dubbo spi机制_dubbo负载均衡前言SPI(ServiceProviderInterface):服务提供接口本文主要介绍dubbo源码中大量使用的SPI机制,dubbo中的spi和jdk中的不同SPIdubbo中提供了一个ExtensionLoader.getLoadingStrategies()方法,但是在dubbo3.0.6版本已经废弃,取而代之的是几个区分了模块的类基于名称的扩展点ClusterfailsafeCluster=ApplicationModel.defaultModel().getExtensio

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全家桶1年46,售后保障稳定

前言

SPI(Service Provider Interface):服务提供接口

本文主要介绍dubbo源码中大量使用的SPI机制,至于什么是SPI,请自行查阅

dubbo SPI

dubbo中提供了一个ExtensionLoader.getLoadingStrategies()方法,但是在dubbo3.0.6版本已经废弃,取而代之的是几个区分了模块的类,ApplicationModel、FrameworkModel、ModuleModel

dubbo中的spi规则是这样的,首先在META-INF/dubbo/internal下会有很多的SPI接口类,这些接口都是标记了@SPI注解的
在这里插入图片描述
以其中的一个org.apache.dubbo.rpc.cluster.Cluster接口为例,其文件内容如下,以下value值都是Protocol接口的实现类

mock=org.apache.dubbo.rpc.cluster.support.wrapper.MockClusterWrapper
failover=org.apache.dubbo.rpc.cluster.support.FailoverCluster
failfast=org.apache.dubbo.rpc.cluster.support.FailfastCluster
failsafe=org.apache.dubbo.rpc.cluster.support.FailsafeCluster
failback=org.apache.dubbo.rpc.cluster.support.FailbackCluster
forking=org.apache.dubbo.rpc.cluster.support.ForkingCluster
available=org.apache.dubbo.rpc.cluster.support.AvailableCluster
mergeable=org.apache.dubbo.rpc.cluster.support.MergeableCluster
broadcast=org.apache.dubbo.rpc.cluster.support.BroadcastCluster
zone-aware=org.apache.dubbo.rpc.cluster.support.registry.ZoneAwareCluster

Jetbrains全家桶1年46,售后保障稳定

@SPI(Cluster.DEFAULT)
public interface Cluster { 
   

    String DEFAULT = "failover";

    /** * Merge the directory invokers to a virtual invoker. * * @param <T> * @param directory * @return cluster invoker * @throws RpcException */
    @Adaptive
    <T> Invoker<T> join(Directory<T> directory, boolean buildFilterChain) throws RpcException;

    static Cluster getCluster(ScopeModel scopeModel, String name) { 
   
        return getCluster(scopeModel, name, true);
    }

    static Cluster getCluster(ScopeModel scopeModel, String name, boolean wrap) { 
   
        if (StringUtils.isEmpty(name)) { 
   
            name = Cluster.DEFAULT;
        }
        return ScopeModelUtil.getApplicationModel(scopeModel).getExtensionLoader(Cluster.class).getExtension(name, wrap);
    }
}

基于名称的扩展点

Cluster failsafeCluster = ApplicationModel.defaultModel().getExtensionLoader(Cluster.class)
                .getExtension("failsafe");

看一下getExtension方法

public T getExtension(String name) { 
   
        T extension = getExtension(name, true);
        if (extension == null) { 
   
            throw new IllegalArgumentException("Not find extension: " + name);
        }
        return extension;
    }

调用底层getExtension时第二个参数表示是否需要包装,这里是true,何为包装呢
比如上述的一个org.apache.dubbo.rpc.cluster.support.wrapper.MockClusterWrapper,其后缀是Wrapper,官方的包装类基本都是以Wrapper结尾的,看一下实现类

public class MockClusterWrapper implements Cluster { 
   

    private final Cluster cluster;

    public MockClusterWrapper(Cluster cluster) { 
   
        this.cluster = cluster;
    }

    @Override
    public <T> Invoker<T> join(Directory<T> directory, boolean buildFilterChain) throws RpcException { 
   
        return new MockClusterInvoker<T>(directory,
                this.cluster.join(directory, buildFilterChain));
    }

    public Cluster getCluster() { 
   
        return cluster;
    }
}

它有一个构造器,接受了一个当前的接口对象Cluster,这是一种很明显的装饰器模式,当获取实现类时如果传递wrap参数是true时,会使用所有的包装类进行层层包装,用装饰器模式进行装饰

根据Cluster接口找到其中名为failsafe的实现类,这里是org.apache.dubbo.rpc.cluster.support.FailsafeCluster实现类,根据上述装饰器模式,因为这里只有一个包装类,所以这里获得的是MockClusterWrapper(FailsafeCluster)

自适应扩展点

Cluster adaptiveExtension = ApplicationModel.defaultModel().getExtensionLoader(Cluster.class)
                .getAdaptiveExtension();

什么是自适应扩展点呢,首先看一下Cluster接口,自适应接口的必须满足下述两个需求的其中之一

  1. 接口有@Adaptive注解
  2. 至少有一个方法是有@Adaptive注解的

场景1,接口上有@Adaptive注解,那么这里直接获取到该实现类
场景2,这种场景会比较复杂,会根据字节码重组,动态编译生成一个动态类,类似于jdk的动态代理,这里我从源码内部把生成的字节码的字符串找出来是这样的,生成到内存的是一个Cluster$Adaptive对象,可以看到其内部最终从Directory 里面getUrl之后,从url中获取了一个cluster参数,这个cluster参数可以在@Adaptive上指定,否则有默认的生成规则,当url中没有cluster参数时会使用@SPI注解中的默认值failover

基于这个自适应扩展点,那么使用这个类,如果url中的cluster不一样,那么就会使用不同的cluster实现类,实现根据url来动态的切换实现类,而获得的自适应扩展类则不需要任何变化

需要注意标记在方法上的支持自适应方法中必须要带url,或者其一个参数中通过getUrl方法可以获得到一个url,比如Directory,只有可以获取到url才能根据url进行自适应

public class Cluster$Adaptive implements org.apache.dubbo.rpc.cluster.Cluster { 
   

    public Invoker join(Directory arg0, boolean arg1) throws org.apache.dubbo.rpc.RpcException { 
   
        if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.cluster.Directory argument == null");
        if (arg0.getUrl() == null)
            throw new IllegalArgumentException("org.apache.dubbo.rpc.cluster.Directory argument getUrl() == null");
        URL url = arg0.getUrl();
        // 获取url中cluster的参数
        String extName = url.getParameter("cluster", "failover");
        if (extName == null)
            throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.cluster.Cluster) name from url (" + url.toString() + ") use keys([cluster])");
        ScopeModel scopeModel = ScopeModelUtil.getOrDefault(url.getScopeModel(),Cluster.class);
        // 根据cluster的value获取
        Cluster extension =  scopeModel.getExtensionLoader(Cluster.class).getExtension(extName);
        return extension.join(arg0, arg1);
    }

    public org.apache.dubbo.rpc.cluster.Cluster getCluster(org.apache.dubbo.rpc.model.ScopeModel arg0, java.lang.String arg1) { 
   
        throw new UnsupportedOperationException("The method public static org.apache.dubbo.rpc.cluster.Cluster org.apache.dubbo.rpc.cluster.Cluster.getCluster(org.apache.dubbo.rpc.model.ScopeModel,java.lang.String) of interface org.apache.dubbo.rpc.cluster.Cluster is not adaptive method!");
    }

    public org.apache.dubbo.rpc.cluster.Cluster getCluster(org.apache.dubbo.rpc.model.ScopeModel arg0, java.lang.String arg1, boolean arg2) { 
   
        throw new UnsupportedOperationException("The method public static org.apache.dubbo.rpc.cluster.Cluster org.apache.dubbo.rpc.cluster.Cluster.getCluster(org.apache.dubbo.rpc.model.ScopeModel,java.lang.String,boolean) of interface org.apache.dubbo.rpc.cluster.Cluster is not adaptive method!");
    }
}

激活扩展点

激活扩展点的实现类上都会有@Activate注解,比如我自定义一个Filter的激活扩展点,我定义了value=mykey,表示只有当url中有mykey这样一个key才返回当前过滤器,group是分组,我设置了字符串provider和consumer,表示当我的group查询provider或者consumer时当前过滤器返回,并且在META-INF下定义好该SPI

@Activate(value = "myKey", group = { 
   CommonConstants.PROVIDER, CommonConstants.CONSUMER})
public class LogFilter implements Filter { 
   
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { 
   

        System.out.println("拦截所有dubbo调用-------------------------");

        return invoker.invoke(invocation);
    }
}
URL url = new URL("","",8080);
url = url.addParameter("myKey","111");
List<Filter> activateList = ApplicationModel.defaultModel().getDefaultModule().getExtensionLoader(Filter.class)
                .getActivateExtension(url, "","provider");

当我使用上述方法获取扩展点时即可拿到我自己自定义的LogFilter,因为我要过滤的group=provider,并且url中带了myKey这个key,并且返回了很多其他的Filter

这个激活扩展点机制,就很适合用于各种拦截的场景,比如我要拦截所有dubbo请求的发起,或者拦截所有当前服务的dubbo服务被调用,那么只要像我上述定义的LogFilter一样,指定好group即可,因为dubbo的源码内部就是有一个根据当前是provider还是consumer来获取所有Filter,组成一个调用链路的

根据SPI扩展自己的自定义插件

基于上述自适应扩展机制,比如我想设置某个服务的负载均衡策略为每次只选择第一个,那么只要这样写
根据LoadBalance spi机制,自定义自己的LoadBalance实现

public class FirstLoadBalance implements LoadBalance { 
   
    @Override
    public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException { 
   
        // 测试只选择第一个
        return invokers.get(0);
    }
}

在META-INF/dubbo/internal下定义org.apache.dubbo.rpc.cluster.LoadBalance文件,内容如下

first=com.example.consumer.spi.FirstLoadBalance

指定负载均衡策略,那么这个接口就会使用我们自定义的first的负载均衡策略了

    @DubboReference(
            protocol = "dubbo",
            loadbalance = "first",
            cluster = "failover",check = false,retries = 5)
    private SayHelloService sayHelloService;
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/234412.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • ubuntu安装gcc和g++

    ubuntu安装gcc和g++依赖包含gcc和g++,只需一行命令即可sudoapt-getinstallbuild-essential查看版本g++–versiongcc–version

  • python中如何打开csv文件_python如何读取csv文件

    python中如何打开csv文件_python如何读取csv文件python如何读取csv文件,我们这里需要用到python自带的csv模块,有了这个模块读取数据就变得非常容易了。工具/原料python3方法/步骤1这里以sublimetext3编辑器作为示范,新建一个文档。2我们可以先确认CSV文档是否可以正确打开。并且放在同一个文件夹里面。3importcsv这是第一步要做的,就是调用csv模块。4importcsvfile=open(‘data…

  • 老板口中的一区二区是什么意思?[通俗易懂]

    老板口中的一区二区是什么意思?[通俗易懂]不少作者对SCI期刊有所耳闻,但是对于SCI期刊的级别划分就不清楚了,有的作者看了评职晋升的要求后有了疑问,比如有的要求发表SCI一区期刊。比如某大学教授职称评审就要求以第一作者在《Science》、《Cell》等顶级杂志上发表学术论文1篇;或以第一作者在SCI一区期刊上发表学术论文X篇;或以第一作者在SCI二区及以上期刊发表学术论文X篇。那很多人就搞不清楚什么是SCI一区期刊、二区期刊了。

  • 基于深度学习的图像超分辨率重建技术的研究

    1超分辨率重建技术的研究背景与意义图像分辨率是一组用于评估图像中蕴含细节信息丰富程度的性能参数,包括时间分辨率、空间分辨率及色阶分辨率等,体现了成像系统实际所能反映物体细节信息的能力。相较于低分辨率图像,高分辨率图像通常包含更大的像素密度、更丰富的纹理细节及更高的可信赖度。但在实际上中,受采集设备与环境、网络传输介质与带宽、图像退化模型本身等诸多因素的约束,我们通常并不能直接得到具有边缘锐化、无成块模糊的理想高分辨率图像。提升图像分辨率的最直接的做法是对采集系统中的光学硬件进行改进,但这种做法.

  • 只通过com.alibaba.fastjson.JSONArray实现okHttp下String转换JSONArray

    只通过com.alibaba.fastjson.JSONArray实现okHttp下String转换JSONArray我的Android不能导入常见的那六个包,会严重报错。我改了很久很久还是不能解决错误,也就不能使用net.sf包中的JSONArray直接使用newJSONArray(str)。给像我一样不能导入包的同学介绍一种方法importcom.alibaba.fastjson.JSONArray;importcom.alibaba.fastjson.JSONObject;Stri…

  • 协程

    协程介绍协程:是单线程下的并发,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。、需要强调的是:对比操作系统控制

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号