阿里巴巴中间件之Dubbo
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相关推荐
- Dubbo Mesh | 阿里巴巴中间件团队在 Service Mesh 的实践和探索(附PPT)
精彩观点导读: » 我们去探索一项技术,并不会仅仅因为其先进性,而是因为我们目前遇到了一些无法解决的问题,而这项技术正好能解决这个问题. » 所有软件最重要的使命不是满足功能要求,而是演进,从而持续成 ...
- 分布式中间件之Dubbo详解
文章内容输出来源:拉勾教育Java高薪训练营 心得体会: 在拉勾教育高薪训练营里经过三个月多的学习,很辛苦但也收获很多,主要体现在:1.以前是工作遇到了哪方面的问题才去学习并解决,很难形成知识体系,在 ...
- 阿里巴巴中间件开源项目盘点(持续更新)
Apache Dubbo 是一款高性能.轻量级的开源Java RPC框架,是国内影响力最大.使用最广泛的开源服务框架之一,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动 ...
- 阿里巴巴中间件在 Serverless 技术领域的探索
Serverless 话题涉及范围极广,几乎包含了代码管理.测试.发布.运维和扩容等与应用生命周期关联的所有环节.AWS Lambda 是 Serverless 领域的标志性产品,但如果将其应用于核心 ...
- 阿里巴巴中间件之Seata
一.在了解Seata之前先简单看一下两阶段提交 1.准备阶段 事务协调者(事务管理器)给每个参与者(资源管理器)发送 Prepare消息,每个参与者要么直接返回失败(如权限验证失败),要么在本地执行事 ...
- mysql分库分表 tddl,阿里巴巴中间件TDDL用于连接数据库,分表分库查询
1.创建数据源 由DBA来做. 一般数据源有3层: (1)Matrix 分库分表,数据路由,对应一个应用,下面有若干个group (2)Group 主备切换,读写分离,对应一组主备数据库,下面有若干a ...
- 阿里巴巴中间件性能挑战赛(MOM篇)
先贴一下赛题: 实现一个基于发布-订阅模型的消息中间件(broker+client) 必选特性: 提供可靠消息服务,broker要保证数据同步落盘才能向生产者返回发送成功的ack,并保证投递给所有的消 ...
- 阿里巴巴宣布开源限流降级中间件——Sentinel
近日,阿里巴巴中间件团队宣布开源 Sentinel,并发布了首个社区版本v0.1.0.GitHub地址为:https://github.com/alibaba/Sentinel . 关于Sentine ...
- 重磅剧透!阿里巴巴计划开源 Nacos,为Dubbo生态发展铺路
作者:郭平 来源:阿里巴巴中间件 本文章是根据郭平老师在上海Dubbo沙龙的演讲稿进行整理,意在为大家展示最真实.最一手的沙龙技术干货.(PPT在文末哦!) 贡献Dubbo生态 Nacos开源计划 在 ...
最新文章
- 程序员应该吃透的集合List
- 匿名函数的this指向为什么是window?
- linux消息信号丢失,Linux信号丢失问题分析
- 【简洁+注释】剑指 Offer 32 - II. 从上到下打印二叉树 II
- “大数据与精准营销沙龙”成功在京召开
- 正则学习之量词匹配分析
- 残缺、时间一起的爱情
- c语言快速数独生成器
- 2021-08-27
- 微型计算机外观分为,2015计算机应用基础单选练习题1.1
- drcom宽带认证登录超时_drcom宽带认证客户端登录超时
- Dubbo实现分布式架构原理
- CPE/CPA/CPL/CPC/CPM/CPO/CPS/CPV/CPT/CPP广告
- 【PyTorch】Balanced_CE_loss 实现
- 企业风险管理与内部控制常见问题与思考
- Linux安装jdk,mysql,tomcat,redis和nginx
- PPP协议讲解(PPP连接状态、PPP报文)
- 组合数求解与(扩展)卢卡斯定理
- ES6 - lterator
- 高通欲购买Arm股份且不排除全盘收购,苹果AR操作系统商标RealityOS曝光,报废汽车塑料能变石墨烯,今日更多大新闻在此...
热门文章
- 排查Java的内存问题
- 百度语音识别JAVA代码_【百度语音识别】JavaAPI方式语音识别示例MP3转PCM
- 人脸识别之Face++
- oracle export utf-8,Linux操作系统下终端乱码的终极解决方案 export LANG=zh_CN.UTF-8 export LANG=en_US...
- clickhouse使用
- C++的clog与cerr
- 诚之和:5个目前最热门岗位的IT岗位,你是哪个?
- 物联网时代来临 企业将面临哪些挑战?
- linux启动Webnet命令,在发布.netCore WebApi服务并在Linux上部署(一)
- python时间转换器