Dubbo

2012年开源,中间几年没有维护,2017年9月份又重拾起来,2018年捐给Apache基金会。 在Dubbo开源之后一些公司进行了使用,在Dubbo停止维护之后,这些公司就基于dubbo自己开发了一个RPC框架,比如京东的jsf,新浪的motan,蚂蚁金服的sofa,当当的dubbox。

通信协议

  • RestTemplate(Http Client)
  • WebService(soap) wsdl(Web Services Description Language)
  • Hessian(基于http协议)
  • RMI(java原生的通信机制)

服务的通信需要考虑的问题

  • 网络通信
  • 序列化、反序列化
  • 服务注册与发现

服务治理的要求

  • 引入注册中心(zk、redis)
  • 如何实现链路监控
  • 服务通信异常(服务调用出现异常的时候要考虑熔断、限流等)
  • 负载均衡(当某个服务做了高可用的时候)

这里演示的demo使用zk作为注册中心,首先安装zk

解压zk 服务conf目录里面的zoo_sample.cfg为zoo.cfg 在zoo.cfg下面添加

# 第一个port是节点之间通信的端口,第二个port用于选举leader节点。
server.1=localhost:2287:3387
server.2=localhost:2288:3388
server.3=localhost:2289:3389

这里我拷贝出来了两台,一共是三台,搭建了一个伪集群,在解压的目录下面创建data和logs文件夹,更改dataDir的路径,同时添加日志存放的路径

# 存储内存中数据库快照的位置,如果不设置参数,更新事务日志将被存储到默认位置
dataDir=/usr/local/java/zkCluster/zk1/data
# 日志存放
dataLogDir=/usr/local/java/zkCluster/zk1/logs

在data目录下面新建myid文件,第一个节点为1,第二个节点为2,第三个节点为3
同时更改每台的端口号,第一台默认为2181可不用动,第二台更改为2182

clientPort=2182

第三台更改为2183

clientPort=2183

然后去每台的bin目录里面通过 ./zkServer.sh start 启动即可

SpringBoot整合Dubbo

1.创建api模块,编写 ISayHelloService 接口

package com.tqz.dubbo.api;/*** <p>* 服务接口** @author tian* @since 2021/2/22 21:53*/
public interface ISayHelloService {String sayHello() throws InterruptedException;
}

2.创建provider模块,并在pom里面添加相关依赖,且该模块依赖于api模块

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-spring-boot-starter</artifactId><version>2.7.1</version></dependency><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo</artifactId><version>2.7.2</version></dependency><dependency><groupId>org.apache.curator</groupId><artifactId>curator-recipes</artifactId><version>4.0.1</version></dependency><dependency><groupId>org.apache.curator</groupId><artifactId>curator-framework</artifactId><version>4.0.1</version><exclusions><exclusion><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId><version>3.4.14</version></dependency><dependency><artifactId>dubbo-api</artifactId><groupId>com.tqz</groupId><version>1.0-SNAPSHOT</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>

在provider模块里面编写 SayHelloServiceImpl 并实现api模块的 ISayHelloService接口,并通过dubbo的 @Service 对外发布服务

package com.tqz.dubbo.provider;import com.tqz.dubbo.api.ISayHelloService;
import org.apache.dubbo.config.annotation.Service;/*** <p>* {@link ISayHelloService}的实现类** @author tian* @since 2021/2/22 21:59*/
@Service(loadbalance = "random",timeout = 50000,cluster = "failsafe")
public class SayHelloServiceImpl implements ISayHelloService {@Overridepublic String sayHello() {/*   try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}*/System.out.println("Come in SayHello()");return "Hello Dubbo";}
}

在resources下面创建application.prpoerties文件,添加dubbo扫包、dubbo服务名称、dubbo注册地址

dubbo.scan.base-packages=com.tqz.dubbo.provider
dubbo.application.name=springboot-dubbo-provider
dubbo.registry.address=zookeeper://ip:2181

创建springboot启动类

package com.tqz.dubbo.provider;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;/*** <p>* 服务发布者的启动类** @author tian* @since 2021/2/22 21:59*/
@SpringBootApplication
public class DubboProviderApplication {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(DubboProviderApplication.class, args);Environment environment = context.getBean(Environment.class);System.out.println(environment.getProperty("dubbo.protocol.port"));}}

3.创建consumer模块,该模块也依赖于api

   <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-spring-boot-starter</artifactId><version>2.7.1</version></dependency><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo</artifactId><version>2.7.2</version></dependency><dependency><groupId>org.apache.curator</groupId><artifactId>curator-recipes</artifactId><version>4.0.1</version></dependency><dependency><groupId>org.apache.curator</groupId><artifactId>curator-framework</artifactId><version>4.0.1</version><exclusions><exclusion><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId><version>3.4.14</version></dependency><dependency><artifactId>dubbo-api</artifactId><groupId>com.tqz</groupId><version>1.0-SNAPSHOT</version></dependency></dependencies>

同样在resources下面创建application.properties

dubbo.scan.base-packages=com.tqz.dubbo
dubbo.application.name=springboot-dubbo-consumer
dubbo.registry.address=zookeeper://ip:2181

创建 DubboController 类,通过dubbo的注解 @Reference 注解去调用 SayHelloService

package com.tqz.dubbo.consumer;import com.tqz.dubbo.api.ISayHelloService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;/*** <p>* 服务消费端** @author tian* @since 2021/2/22 21:59*/
@RestController
public class DubboController {// Dubbo提供的注解@ReferenceISayHelloService sayHelloService;@GetMapping("/sayhello")public String sayHello() throws InterruptedException {return sayHelloService.sayHello();}
}

创建springboot启动类

package com.tqz.dubbo.consumer;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;/*** <p>* 服务消费端** @author tian* @since 2021/2/22 21:59*/
@SpringBootApplication
public class DubboConsumerApplication {public static void main(String[] args) {SpringApplication.run(DubboConsumerApplication.class, args);}
}

启动两个provider,第一台可不添加参数,dubbo默认端口号为20880,

第二台启动的时候添加jvm参数-Ddubbo.protocol.prot=20881

然后启动consumer,然后访问consumer的 DubboController 里面的接口,观察两台provicer的控制台,可以看到分别有输出。

dubbo默认的调用负载均衡算法为random。

此时我们可以观察dubbo在zookeeper里面的配置信息

客户端通过配置把当前服务的相关信息注册到zookeeper里面,服务端去zookeeper里面拿到服务端的配置然后去进行远程调用。

Dubbo负载均衡算法

Ribbon默认的负载均衡算法为轮循,dubbo默认为随机, 在@Service 注解里面的loadbalance属性为 String loadbalance() default DEFAULT_LOADBALANCE;

该 DEFAULT_LOADBALANCE 为 org.apache.dubbo.rpc.cluster 包下的 Constants 常量类里面的

String DEFAULT_LOADBALANCE = "random";

假设有10台机器

A[3] B[5] C[2]

1~3 4~8 9~10

根据随机值落到哪台机器就代表请求到了哪个服务

设置权重通过 @Service 注解里面的 weight 属性

dubbo里面共有四种负载均衡算法

  • LeastActiveLoadBalance 最小活跃:根据后端服务的吞吐量进行负载,该算法也是最均衡的,dubob里面是基于一个过滤器实现的该算法

  • ConsistentHashLoadBalance 一致性哈希:根据请求方法和请求参数生成一个hash值(hash环)

  • RandomLoadBalance 随机:没什么好说的

  • RoundRobinLoadBalance 轮循 :假设有三台机器,每次请求都会分发到不同的机器上面,可以通过 weight属性配置权重

假设服务调用方使用的 @Reference注解里面的loadbalance 和服务提供者 @Service注解里面使用 loadbalanc注解都配置了负载均衡的算法,且配置的不同的话,客户端是优先于服务端的

负载均衡的算法可以指定到服务的接口以及方法上面,默认为接口层面,可在 @Reference 注解里面 method 属性指定方法,具体的配置规则参考 :https://dubbo.apache.org/zh/docs/v2.7/user/references/xml/dubbo-method

容错

dubbo一共提供了六种容错机制

只需要在@Reference注解配置cluster属性即可

  • failover:重试(默认的情况) retries=2 加上原来调用的一次 一共会调用3次

  • failback: 失败自动回复,后台记录失败请求,定时重发

  • failsafe:失败安全,出现异常的时候直接忽略

  • failfast:快速失败 ,只发起一次调用,失败立即报错。

  • forking:并行调用多个服务,只要一个成功即返回。

  • broadcast:广播调用,逐个调用每个服务者,任何一台报错就失败(通常用于更新所有节点的缓存)

服务降级

  • 异常降级:服务异常提供一个默认的数据

  • 限流降级:假设qps为10,达到了20之后返回系统繁忙,请稍后重试

  • 熔断降级:10s之后超过50%请求响应时间达到5s

  • mock机制:为Dubbo提供的,类似于的Hystrix的熔断,配置一个降级类,去实现要调用的接口。然后再Reference注解的mock属性里面执行该降级类的全限定类名,例如:

    @Reference(mock = "com.tqz.dubbo.client.SayHelloServiceMock")
    ISayHelloService sayHelloService;
    
    package com.tqz.dubbo.consumer;import com.tqz.dubbo.api.ISayHelloService;/*** <p>* 降级处理的类* * @author tian* @since 2021/3/1 21:32*/
    public class SayHelloServiceMock implements ISayHelloService {@Overridepublic String sayHello() {return "服务端发生异常, 被降解了。返回兜底数据。。。";}
    }
    

Dubbo2.7新功能

这里借鉴了Spring Cloud的Config,支持客户端以及服务端的配置放在配置中心,dubbo里面有以下几种实现配置中心,分别是Nacos/Apollo/Zookeeper/Consul/Etcd.

新版的dashboard也做了改变,github clone地址:https://github.com/apache/dubbo-admin

拉下来之后更改dubbo-admin-server模块的注册中心的配置,根据自己选择的配置中心去进行注册

然后根据官网的命令去编译、启动,默认的账号和密码都是root。第三步编译的时候可能会有点慢,因为admin的界面使用vue写的,需要去进行安装,最好先使用淘宝的镜像会比较快。

启动完之后输入用户名root,密码root就可以在服务查询里面看到我们对外暴露的服务,测试里面还可以进行服务的调用。

Dubbo整合配置中心

在我们provider模块配置dubbo注册中心的的地址。

### dubbo配置中心的地址
dubbo.config-center.address=zookeeper://ip:2181
### dubbo配置中心的应用名称,类似于nacos中的namespace
dubbo.config-center.app-name=springboot-dubbo-provider

然后在控制台的配置管理里面新建一个配置,这里应用名要和上面dubbo.config-center.app-name配置的一样,否则找不到。

然后再zookeeper的客户端去查看,发现里面多了一个config的文件夹,这里面就是我们上面配置的内容。

SPI应用

JDK的SPI

JDK1.6提供了一个接口 ServiceLoader,这也是Java原生的spi机制,在META-IFN/services新建一个接口全名的UTF-8编码的文件,里面写上具体实现类的全名,这样调用方通过Java的ServiceLoad接口动态的去加载接口的实现类。

Spring的SPI

Spring里面有个 SpringFactoriesLoader会去加载 META-INF/spring.factories,按照Properties格式填上自己的接口以及实现类,key为接口、注解、抽象类,value是相应的实现类,value可以有多个,以逗号分隔。
Spring Boot的自动装配就是这么做的。

Dubbo的SPI

Dubbo的spi结合了JDK和Spring两种,以接口的全限定类名为文件的名称,文件里面是Properties格式的,key为我们自定义的协议名称,value为我们的实现类,下面来演示一个demo。

1.实现 Protocol 接口,端口号自定义一下。
package com.tqz.dubbo.provider;import org.apache.dubbo.common.URL;
import org.apache.dubbo.rpc.Exporter;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Protocol;
import org.apache.dubbo.rpc.RpcException;/*** <p>* 自定义协议** @autoor tianqingzhao* @since 2021/3/13 15:27*/
public class MyProtocol implements Protocol {/*** 自定义默认端口** @return*/@Overridepublic int getDefaultPort() {return 1234;}/*** 暴露远程服务,底层默认就是启动了Netty* 1. 协议在接收请求时,应记录请求来源方地址信息:RpcContext.getContext().setRemoteAddress();<br>* 2. export()必须是幂等的,也就是暴露同一个URL的Invoker两次,和暴露一次没有区别。<br>* 3. export()传入的Invoker由框架实现并传入,协议不需要关心。<br>** @param invoker 服务的执行体* @param <T>     服务的类型* @return 暴露服务的引用,用于取消暴露* @throws RpcException 当暴露服务出错时抛出,比如端口已占用*/@Overridepublic <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {return null;}/*** 调用远程服务* 1. 当用户调用refer()所返回的Invoker对象的invoke()方法时,协议需相应执行同URL远端export()传入的Invoker对象的invoke()方法。<br>* 2. refer()返回的Invoker由协议实现,协议通常需要在此Invoker中发送远程请求。<br>* 3. 当url中有设置check=false时,连接失败不能抛出异常,需内部自动恢复。<br>** @param type 服务的类型* @param url  远程服务的URL地址* @param <T>  服务的类型* @return 服务的本地代理* @throws RpcException 当连接服务提供方失败时抛出*/@Overridepublic <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {return null;}/*** 销毁*/@Overridepublic void destroy() {}
}
2.在resources下面新建META-INF/dubbo文件夹

然后再创建一个 Protocol接口的全限定类名文件

3.里面写上我们自己的协议名称以及实现类
myProtocol=com.tqz.dubbo.provider.MyProtocol
4.通过 ExtensionLoader拿到根据协议名称获取该协议。
Protocol myProtocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("myProtocol");
System.out.println(myProtocol.getDefaultPort());
5.根据协议拿到我们自定义的端口号,这就是Dubbo的SPI机制。

看下Dubbo大概哪些地方用到了SPI

SPI扩展点

静态扩展点

从ExtensionLoader.getExtensionLoader(Protocol.class)为入口,这里面其实就是放到一个ConcurrentMap里面,key为我们的接口,这里就暂且指定为我们测试的 Protocol接口,value为 ExtensionLoader,这个方法中第三个if的判断非常重要,它是判断我们的接口是否有 @SPI注解,如果没有的话就抛异常。

    @SuppressWarnings("unchecked")public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {// 是否为空,空直接抛异常if (type == null) {throw new IllegalArgumentException("Extension type == null");}// 是否为接口,不是接口直接抛异常if (!type.isInterface()) {throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");}// 该接口没有@SPI注解直接抛异常if (!withExtensionAnnotation(type)) {throw new IllegalArgumentException("Extension type (" + type +") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");}// 缓存,就是一个ConcurrentMapExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);if (loader == null) {EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);}return loader;}

根据返回的 ExtensionLoader在再调用 #getExtension(String name) 方法,第一次进来肯定为空,会去调用#createExtension(String name, boolean wrap)方法。

 @SuppressWarnings("unchecked")public T getExtension(String name) {return getExtension(name, true);}public T getExtension(String name, boolean wrap) {if (StringUtils.isEmpty(name)) {throw new IllegalArgumentException("Extension name == null");}if ("true".equals(name)) {// 默认扩展点return getDefaultExtension();}// 该Holder也是默认的一个缓存,里面也是一个ConcurrentMapfinal Holder<Object> holder = getOrCreateHolder(name);Object instance = holder.get();if (instance == null) {synchronized (holder) {instance = holder.get();if (instance == null) {// MyProtocol对应的实例instance = createExtension(name, wrap);holder.set(instance);}}}return (T) instance;}

在createExtension(String name, boolean wrap)里面有一行

Class<?> clazz = getExtensionClasses().get(name);

从getExtensionClasses()方法跟进去,在该方法里面回去调用到 loadExtensionClasses方法,该方法在2.7.7版本中做了一些更改,

去循环初始化好的strategies成员属性,可以看到该属性是 LoadingStrategy接口类型的数组,该接口有一个方法为directory(),也就是会返回一个文件夹的名称。它一共有四个实现类,分别是:

  • DubboExternalLoadingStrategy,对应 META-INF/dubbo/external/ (2.7.7新增的)
  • META-INF/dubbo/internal/,对应 META-INF/dubbo/internal/
  • META-INF/dubbo/,对应 META-INF/dubbo/
  • META-INF/services/,对应 META-INF/services/

loadDirectory()方法就是加载对应路径的类,至于加载两次是要兼容旧版本,因为在捐给Apache之前,2.7.0之前版本的包名都是以 com.alibaba 开头的,在捐给Apache之后,都是以 org.apache 开头的包名。

private Map<String, Class<?>> loadExtensionClasses() {cacheDefaultExtensionName();Map<String, Class<?>> extensionClasses = new HashMap<>();for (LoadingStrategy strategy : strategies) {loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache", "com.alibaba"), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());}return extensionClasses;
}

Debug可以发现最终加载了九个协议,全部都扔到了一个HashMap中,其中可以看到我们自定义实现的myProtocol

然后再回到createExtension()方法,通过我们的自定义的myProtocol名称获取一个MyProtocol实例对象,同时再放到 EXTENSION_INSTANCES属性中,该属性又是一个Map结构,然后把我们的实现类给返回去。

    @SuppressWarnings("unchecked")private T createExtension(String name, boolean wrap) {Class<?> clazz = getExtensionClasses().get(name);if (clazz == null) {throw findException(name);}try {T instance = (T) EXTENSION_INSTANCES.get(clazz);if (instance == null) {EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());instance = (T) EXTENSION_INSTANCES.get(clazz);}injectExtension(instance);......initExtension(instance);return instance;} catch (Throwable t) {throw new IllegalStateException("Extension instance (name: " + name + ", class: " +type + ") couldn't be instantiated: " + t.getMessage(), t);}}

自适应扩展点

自适应扩展点通过 @Adaptive 注解来实现,他有两种作用方式,一种是在类上面,一种是在方法上面。首先来看一下类上面的,

Compiler adaptiveExtension = ExtensionLoader.getExtensionLoader(Compiler.class).getAdaptiveExtension();
System.out.println(adaptiveExtension);

会输出 org.apache.dubbo.common.compiler.support.AdaptiveCompiler@442d9b6e

我们点开 AdaptiveCompiler 这个类,可以看到他上面有一个 @Adaptive 注解,这种是作用在类上面的。

Protocol 接口里面的export()方法和refer()方法上面也有这两个注解,我们再 getExtensionLoader()方法里面传入该接口,断点跟到createAdaptiveExtensionClass()方法可以发现就是动态生成字节码去加载

把这段代码拷贝出来格式化一下,该类实现于我们的 Protocol 接口,然后再export()方法里面进行判断我们的协议名称是否为空,如果为空就使用默认的dubbo协议

public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {public void destroy() {throw new UnsupportedOperationException("The method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");}public int getDefaultPort() {throw new UnsupportedOperationException("The method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");}public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");if (arg0.getUrl() == null)throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");org.apache.dubbo.common.URL url = arg0.getUrl();String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());if (extName == null)throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);return extension.export(arg0);}public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1) throws org.apache.dubbo.rpc.RpcException {if (arg1 == null) throw new IllegalArgumentException("url == null");org.apache.dubbo.common.URL url = arg1;String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());if (extName == null)throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);return extension.refer(arg0, arg1);}
}

激活扩展点

该扩展点类似于Spring Boot自动装配里面的 @ConditionalOnBean@ConditionalOnClass 等注解,当达到某一个条件的时候会自动激活加载。一个类如果有多个扩展点的话,可以使用自动激活进行加载。dubbo里面的 Filter 接口就用到了此处,使用 @Activate 注解来进行配置,该注解里面有可以看下有哪些实现。

但是dubbo并没有全部加载,只是默认初始化了10个,初始化的规则是根据实现类上面的 @Activate 注解的value属性是否有值,一旦有的话就不加载,只有我们再value里面配置了spi配置文件里面的key之后才会加载。

List<Filter> cache = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(url, "");
System.out.println(cache);

可以通过上面代码默认的是有10个Filter

这里随便找一个没有加载的,比如 CacheFilter 这个类,其中group属性表示实在服务提供者生效还是在服务调用者生效。

在url里面添加一个cache参数,就可以看到 CacheFilter 了。

URL url = new URL("", "", 0);
url = url.addParameter("cache", "cache");
List<Filter> cache = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(url, "");
System.out.println(cache);


这大概就是Dubbo的SPI加载实现。

持续更新服务注册、发布、调用、容错

阿里巴巴中间件之Dubbo相关推荐

  1. Dubbo Mesh | 阿里巴巴中间件团队在 Service Mesh 的实践和探索(附PPT)

    精彩观点导读: » 我们去探索一项技术,并不会仅仅因为其先进性,而是因为我们目前遇到了一些无法解决的问题,而这项技术正好能解决这个问题. » 所有软件最重要的使命不是满足功能要求,而是演进,从而持续成 ...

  2. 分布式中间件之Dubbo详解

    文章内容输出来源:拉勾教育Java高薪训练营 心得体会: 在拉勾教育高薪训练营里经过三个月多的学习,很辛苦但也收获很多,主要体现在:1.以前是工作遇到了哪方面的问题才去学习并解决,很难形成知识体系,在 ...

  3. 阿里巴巴中间件开源项目盘点(持续更新)

    Apache Dubbo 是一款高性能.轻量级的开源Java RPC框架,是国内影响力最大.使用最广泛的开源服务框架之一,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动 ...

  4. 阿里巴巴中间件在 Serverless 技术领域的探索

    Serverless 话题涉及范围极广,几乎包含了代码管理.测试.发布.运维和扩容等与应用生命周期关联的所有环节.AWS Lambda 是 Serverless 领域的标志性产品,但如果将其应用于核心 ...

  5. 阿里巴巴中间件之Seata

    一.在了解Seata之前先简单看一下两阶段提交 1.准备阶段 事务协调者(事务管理器)给每个参与者(资源管理器)发送 Prepare消息,每个参与者要么直接返回失败(如权限验证失败),要么在本地执行事 ...

  6. mysql分库分表 tddl,阿里巴巴中间件TDDL用于连接数据库,分表分库查询

    1.创建数据源 由DBA来做. 一般数据源有3层: (1)Matrix 分库分表,数据路由,对应一个应用,下面有若干个group (2)Group 主备切换,读写分离,对应一组主备数据库,下面有若干a ...

  7. 阿里巴巴中间件性能挑战赛(MOM篇)

    先贴一下赛题: 实现一个基于发布-订阅模型的消息中间件(broker+client) 必选特性: 提供可靠消息服务,broker要保证数据同步落盘才能向生产者返回发送成功的ack,并保证投递给所有的消 ...

  8. 阿里巴巴宣布开源限流降级中间件——Sentinel

    近日,阿里巴巴中间件团队宣布开源 Sentinel,并发布了首个社区版本v0.1.0.GitHub地址为:https://github.com/alibaba/Sentinel . 关于Sentine ...

  9. 重磅剧透!阿里巴巴计划开源 Nacos,为Dubbo生态发展铺路

    作者:郭平 来源:阿里巴巴中间件 本文章是根据郭平老师在上海Dubbo沙龙的演讲稿进行整理,意在为大家展示最真实.最一手的沙龙技术干货.(PPT在文末哦!) 贡献Dubbo生态 Nacos开源计划 在 ...

最新文章

  1. 程序员应该吃透的集合List
  2. 匿名函数的this指向为什么是window?
  3. linux消息信号丢失,Linux信号丢失问题分析
  4. 【简洁+注释】剑指 Offer 32 - II. 从上到下打印二叉树 II
  5. “大数据与精准营销沙龙”成功在京召开
  6. 正则学习之量词匹配分析
  7. 残缺、时间一起的爱情
  8. c语言快速数独生成器
  9. 2021-08-27
  10. 微型计算机外观分为,2015计算机应用基础单选练习题1.1
  11. drcom宽带认证登录超时_drcom宽带认证客户端登录超时
  12. Dubbo实现分布式架构原理
  13. CPE/CPA/CPL/CPC/CPM/CPO/CPS/CPV/CPT/CPP广告
  14. 【PyTorch】Balanced_CE_loss 实现
  15. 企业风险管理与内部控制常见问题与思考
  16. Linux安装jdk,mysql,tomcat,redis和nginx
  17. PPP协议讲解(PPP连接状态、PPP报文)
  18. 组合数求解与(扩展)卢卡斯定理
  19. ES6 - lterator
  20. 高通欲购买Arm股份且不排除全盘收购,苹果AR操作系统商标RealityOS曝光,报废汽车塑料能变石墨烯,今日更多大新闻在此...

热门文章

  1. 排查Java的内存问题
  2. 百度语音识别JAVA代码_【百度语音识别】JavaAPI方式语音识别示例MP3转PCM
  3. 人脸识别之Face++
  4. oracle export utf-8,Linux操作系统下终端乱码的终极解决方案 export LANG=zh_CN.UTF-8 export LANG=en_US...
  5. clickhouse使用
  6. C++的clog与cerr
  7. 诚之和:5个目前最热门岗位的IT岗位,你是哪个?
  8. 物联网时代来临 企业将面临哪些挑战?
  9. linux启动Webnet命令,在发布.netCore WebApi服务并在Linux上部署(一)
  10. python时间转换器