1、为什么需要 Dubbo?
- 分布式系统中的服务调用和协调问题:在分布式系统中,服务之间的相互依赖会导致复杂的通信和协调问题。Dubbo提供了高效的服务调用和自动注册、发现等功能,使得构建分布式应用程序更加容易。
- 服务治理和服务调用链追踪:Dubbo可以帮助我们实现服务治理、服务调用链追踪、服务降级、服务熔断等功能,这对于复杂的服务环境非常重要。
- 服务拆分和扩展性:随着用户量的增多,应用服务器可能会面临负载压力。此时,我们可以使用Dubbo进行服务拆分,通过增加应用服务器来提高系统的扩展性。
- 动态服务发现和负载均衡:Dubbo提供了动态的服务注册和发现机制,以及负载均衡功能,这可以帮助我们在消费方获取服务提供方地址列表,实现软负载均衡和Failover,降低对F5硬件负载均衡器的依赖。
- 服务间依赖关系的可视化:当服务的调用量越来越大,服务的容量问题就暴露出来。Dubbo可以帮助我们自动画出应用间的依赖关系图,以帮助架构师理清理关系。
2、Dubbo 的主要应用场景?
通常有四点,如下: RPC 分布式服务,拆分应用进行服务化,提高开发效率,调优性能,节省竞争资源配置管理,解 决服务的地址信息剧增,配置困难的问题 服务依赖,解决服务间依赖关系错踪复杂的问题 服务扩容,解决随着访问量的不断增大,动态扩展服务提供方的机器的问题。
3、Dubbo 的核心功能?
Dubbo主要就是如下 3 个核心功能:
(1)Remoting:网络通信框架,提供对多种 NIO 框架抽象封装,包括“同步转异步”和“请求-响应”模式 的信息交换方式。
(2)Cluster:服务框架,提供基于接口方法的透明远程过程调用,包括多协议支持,以及软负载均 衡,失败容错,地址路由,动态配置等集群支持。
(3)Registry:服务注册,基于注册中心目录服务,使服务消费方能动态的查找服务提供方,使地址透 明,使服务提供方可以平滑增加或减少机器。
4、Dubbo 服务注册与发现的流程?
流程说明: Provider(提供者)绑定指定端口并启动服务 指供者连接注册中心,并发本机IP、端口、应用信息和提供服务信息发送至注册中心存储 Consumer(消费者),连接注册中心 ,并发送应用信息、所求服务信息至注册中心 注册中心根据 消费 者所求服务信息匹配对应的提供者列表发送至Consumer 应用缓存。 Consumer 在发起远程调用时基于缓存的消费者列表择其一发起调用。 Provider 状态变更会实时通知注册中心、在由注册中心实时推送至Consumer。
设计的原因: Consumer 与Provider 解偶,双方都可以横向增减节点数。 注册中心对本身可做对等集群,可动态增减节点,并且任意一台宕掉后,将自动切换到另一台去中 心化,双方不直接依懒注册中心,即使注册中心全部宕机短时间内也不会影响服务的调用服务提供 者无状态,任意一台宕掉后,不影响使用。
5、Dubbo 的服务调用流程?
1) 从client到server经历了编码,序列化,反序列化,解码 的正常网络调用流程, 在nettyServer中处理。
2) client采用代理的机制 。
3) server处理请求的方式通常为分发请求到线程池,同步阻塞或异步非阻塞返回结果。
6、Dubbo 支持哪些协议,每种协议的应用场景、优缺点?
dubbo默认:
7、Dubbo 有些哪些注册中心?
推荐使用 Zookeeper 、Nacos作为注册中心,还有 Redis、Multicast、Simple 注册中心,但不推荐。
8、Dubbo 的注册中心集群挂掉,如何正常消费?
可以的,消费者在启动时,消费者会从zk拉取注册的生产者的地址接口等数据,缓存在本地。 每次调用时,按照本地存储的地址进行调用。 消费者本地有一个生产者的列表,他会按照列表继续工作,倒是无法从注册中心去同步最新的服务列 表,短期的注册中心挂掉是不要紧的,但一定要尽快修复。 挂掉是不要紧的,但前提是你没有增加新的服务(节点没有变动),如果你要调用新的服务,则是不能 办到的。
9、Dubbo 集群提供了哪些负载均衡策略?
- Random LoadBalance: 随机选取提供者策略,有利于动态调整提供者权重。截面碰撞率高,调用次数越多,分布越均匀。
- RoundRobin LoadBalance: 轮循选取提供者策略,平均分布,但是存在请求累积的问题。
- LeastActive LoadBalance: 最少活跃调用策略,解决慢提供者接收更少的请求。
- ConstantHash LoadBalance: 一致性 Hash 策略,使相同参数请求总是发到同一提供者,一台机器宕机,可以基于虚拟节点,分摊至其他提供者,避免引起提供者的剧烈变动。
默认为 Random 随机调用。
10、Dubbo 的集群容错方案有哪些?
-
Failover Cluster失败自动切换: dubbo的默认容错方案,当调用失败时自动切换到其他可用的节点,具体的重试次数和间隔时间可用通 过引用服务的时候配置,默认重试次数为1是只调用一次。
-
Failback Cluster失败自动恢复: 在调用失败,记录日志和调用信息,然后返回空结果给consumer,并且通过定时任务每隔5秒对失败的 调用进行重试
-
Failfast Cluster快速失败: 只会调用一次,失败后立刻抛出异常
-
Failsafe Cluster失败安全: 调用出现异常,记录日志不抛出,返回空结果 Forking Cluster并行调用多个服务提供者: 通过线程池创建多个线程,并发调用多个provider,结果保存到阻塞队列,只要有一个provider成功返 回了结果,就会立刻返回结果
-
Broadcast Cluster广播模式: 逐个调用每个provider,如果其中一台报错,在循环调用结束后,抛出异常。
第一个,failover
这也是Dubbo中默认的一个集群容错技术,failover翻译成中文叫故障转移,见名思义,它是指发现服务提供者不可用时会尝试调用集群中其他的服务提供者,dubbo中默认重试2次。源码如下:
public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
List<Invoker<T>> copyInvokers = invokers;
checkInvokers(copyInvokers, invocation);
String methodName = RpcUtils.getMethodName(invocation);
//获取重试次数,默认2次 加当前调用,总共3次
int len = getUrl().getMethodParameter(methodName, RETRIES_KEY, DEFAULT_RETRIES) + 1;
if (len <= 0) {
len = 1;
}
// retry loop.
RpcException le = null; // last exception.
List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyInvokers.size()); // invoked invokers.
Set<String> providers = new HashSet<String>(len);
//循环重试调用
for (int i = 0; i < len; i++) {
//Reselect before retry to avoid a change of candidate `invokers`.
//NOTE: if `invokers` changed, then `invoked` also lose accuracy.
if (i > 0) {
checkWhetherDestroyed();
copyInvokers = list(invocation);
// check again
checkInvokers(copyInvokers, invocation);
}
Invoker<T> invoker = select(loadbalance, invocation, copyInvokers, invoked);
invoked.add(invoker);
RpcContext.getContext().setInvokers((List) invoked);
try {
Result result = invoker.invoke(invocation);
if (le != null && logger.isWarnEnabled()) {
logger.warn("Although retry the method " + methodName
+ " in the service " + getInterface().getName()
+ " was successful by the provider " + invoker.getUrl().getAddress()
+ ", but there have been failed providers " + providers
+ " (" + providers.size() + "/" + copyInvokers.size()
+ ") from the registry " + directory.getUrl().getAddress()
+ " on the consumer " + NetUtils.getLocalHost()
+ " using the dubbo version " + Version.getVersion() + ". Last error is: "
+ le.getMessage(), le);
}
return result;
} catch (RpcException e) {
if (e.isBiz()) {
// biz exception.
throw e;
}
le = e;
} catch (Throwable e) {
le = new RpcException(e.getMessage(), e);
} finally {
providers.add(invoker.getUrl().getAddress());
}
}
throw new RpcException(le.getCode(), "Failed to invoke the method "
+ methodName + " in the service " + getInterface().getName()
+ ". Tried " + len + " times of the providers " + providers
+ " (" + providers.size() + "/" + copyInvokers.size()
+ ") from the registry " + directory.getUrl().getAddress()
+ " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version "
+ Version.getVersion() + ". Last error is: "
+ le.getMessage(), le.getCause() != null ? le.getCause() : le);
}
第二个,fastfail
这种是快速失败机制,就是调用失败时立即报错,通常用于非幂等的操作。
@Override
public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
checkInvokers(invokers, invocation);
Invoker<T> invoker = select(loadbalance, invocation, invokers, null);
try {
return invoker.invoke(invocation);
} catch (Throwable e) {
if (e instanceof RpcException && ((RpcException) e).isBiz()) {
// biz exception.
throw (RpcException) e;
}
//失败了,直接抛异常
throw new RpcException(e instanceof RpcException ? ((RpcException) e).getCode() : 0,
"Failfast invoke providers " + invoker.getUrl() + " " + loadbalance.getClass().getSimpleName()
+ " select from all providers " + invokers + " for service " + getInterface().getName()
+ " method " + invocation.getMethodName() + " on consumer " + NetUtils.getLocalHost()
+ " use dubbo version " + Version.getVersion()
+ ", but no luck to perform the invocation. Last error is: " + e.getMessage(),
e.getCause() != null ? e.getCause() : e);
}
}
第三个,failsafe
这种是失败安全机制,失败之后不抛异常也不重试,而是忽略异常,单纯记录一下日志,返回空结果。源码如下:
@Override
public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
try {
checkInvokers(invokers, invocation);
Invoker<T> invoker = select(loadbalance, invocation, invokers, null);
return invoker.invoke(invocation);
} catch (Throwable e) {
//忽略异常,记录日志,返回空结果
logger.error("Failsafe ignore exception: " + e.getMessage(), e);
return AsyncRpcResult.newDefaultAsyncResult(null, null, invocation); // ignore
}
}
第四个,broadcast
这种是广播调用,任何一个服务提供者报错就报错。这种集群容错方式的使用场景是用来更新本地缓存,如果某个数据在某台服务器更新了,可用同步到集群中的其他服务器。
public Result doInvoke(final Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
checkInvokers(invokers, invocation);
RpcContext.getContext().setInvokers((List) invokers);
RpcException exception = null;
Result result = null;
//调用所有服务提供者
for (Invoker<T> invoker : invokers) {
try {
result = invoker.invoke(invocation);
} catch (RpcException e) {
exception = e;
logger.warn(e.getMessage(), e);
} catch (Throwable e) {
exception = new RpcException(e.getMessage(), e);
logger.warn(e.getMessage(), e);
}
}
//只要有一台异常,就报错
if (exception != null) {
throw exception;
}
return result;
}
第五个,failback
这种是失败降级,失败后会记录请求并且定时重试,忽略异常后返回空
源码如下:
@Override
protected Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
Invoker<T> invoker = null;
try {
checkInvokers(invokers, invocation);
invoker = select(loadbalance, invocation, invokers, null);
return invoker.invoke(invocation);
} catch (Throwable e) {
logger.error("Failback to invoke method " + invocation.getMethodName() + ", wait for retry in background. Ignored exception: "
+ e.getMessage() + ", ", e);
//新增失败的任务 到时间轮队列
addFailed(loadbalance, invocation, invokers, invoker);
return AsyncRpcResult.newDefaultAsyncResult(null, null, invocation); // ignore
}
}
第六个,forking
这种是并行调用,只要有1个成功就成功,全部失败才算失败,可通过forks="2"
来设置最大并行数。
源码如下:
//循环每个服务提供者,多线程并行调用
for (final Invoker<T> invoker : selected) {
executor.execute(new Runnable() {
@Override
public void run() {
try {
Result result = invoker.invoke(invocation);
ref.offer(result);
} catch (Throwable e) {
int value = count.incrementAndGet();
//失败次数大于等于了提供者数量,说明全部失败
if (value >= selected.size()) {
ref.offer(e);
}
}
}
});
}
try {
Object ret = ref.poll(timeout, TimeUnit.MILLISECONDS);
//如果有失败记录,说明全部都失败了则失败
if (ret instanceof Throwable) {
Throwable e = (Throwable) ret;
throw new RpcException(e instanceof RpcException ? ((RpcException) e).getCode() : 0, "Failed to forking invoke provider " + selected + ", but no luck to perform the invocation. Last error is: " + e.getMessage(), e.getCause() != null ? e.getCause() : e);
}
//否则返回成功
return (Result) ret;
} catch (InterruptedException e) {
throw new RpcException("Failed to forking invoke provider " + selected + ", but no luck to perform the invocation. Last error is: " + e.getMessage(), e);
}
11、Dubbo 支持哪些序列化方式?
- Dubbo:单一长连接和 NIO 异步通讯,适合大并发小数据量的服务调用,以及消费者远大于提供者。传输协议 TCP,异步 Hessian 序列化。Dubbo推荐使用dubbo协议。
- RMI:采用 JDK 标准的 RMI 协议实现,传输参数和返回参数对象需要实现 Serializable 接口,使用 Java 标准序列化机制,使用阻塞式短连接,传输数据包大小混合,消费者和提供者个数差不多,可传文件,传输协议 TCP。多个短连接 TCP 协议传输,同步传输,适用常规的远程服务调用和 RMI 互操作。在依赖低版本的 Common-Collections 包,Java 序列化存在安全漏洞。
- WebService:基于 WebService 的远程调用协议,集成 CXF 实现,提供和原生 WebService 的互操作。多个短连接,基于 HTTP 传输,同步传输,适用系统集成和跨语言调用。
- HTTP:基于 Http 表单提交的远程调用协议,使用 Spring 的 HttpInvoke 实现。多个短连接,传输协议 HTTP,传入参数大小混合,提供者个数多于消费者,需要给应用程序和浏览器 JS 调用。
- Hessian:集成 Hessian 服务,基于 HTTP 通讯,采用 Servlet 暴露服务,Dubbo 内嵌 Jetty 作为服务器时默认实现,提供与 Hession 服务互操作。多个短连接,同步 HTTP 传输,Hessian 序列化,传入参数较大,提供者大于消费者,提供者压力较大,可传文件。
- Memcache:基于 Memcache实现的 RPC 协议。
- Redis:基于 Redis 实现的RPC协议。
12、说说一次 Dubbo 服务请求流程?
13、说说 Dubbo 工作原理
工作原理分 10 层:
第一层:service 层,接口层,给服务提供者和消费者来实现的(留给开发人员来实现);
第二层:config 层,配置层,主要是对 Dubbo 进行各种配置的,Dubbo 相关配置;
第三层:proxy 层,服务代理层,透明生成客户端的 stub 和服务单的 skeleton,调用的是接口,实现 类没有,所以得生成代理,代理之间再进行网络通讯、负责均衡等;
第四层:registry 层,服务注册层,负责服务的注册与发现;
第五层:cluster 层,集群层,封装多个服务提供者的路由以及负载均衡,将多个实例组合成一个服 务;
第六层:monitor 层,监控层,对 rpc 接口的调用次数和调用时间进行监控;
第七层:protocol 层,远 程调用层,封装 rpc 调用;
第八层:exchange 层,信息交换层,封装请求响应模式,同步转异步;
第九层:transport 层,网络传输层,抽象 mina 和 netty 为统一接口;
第十层:serialize 层,数据序列化层。
14、注册中心挂了,consumer 还能不能调用 provider?
因为刚开始初始化的时候,consumer 会将需要的所有提供者的地址等信息拉取到本地缓存,所以注册 中心挂了可以继续通信。 但是 provider 挂了,那就没法调用了。
15、怎么实现动态感知服务下线的呢?
服务订阅通常有 pull 和 push 两种方式:
pull 模式需要客户端定时向注册中心拉取配置;
push 模式采用注册中心主动推送数据给客户端。
Dubbo ZooKeeper 注册中心采用是事件通知与客户端拉取方式。 服务第一次订阅的时候将会拉取对应目录下全量数据,然后在订阅的节点注册一个 watcher。 一旦目录节点下发生任何数据变化, ZooKeeper 将会通过 watcher 通知客户端。 客户端接到通知,将会重新拉取该目录下全量数据,并重新注册 watcher。
利用这个模式,Dubbo 服务就可以做到服务的动态发现。 注意:ZooKeeper 提供了“心跳检测”功能,它会定时向各个服务提供者发送一个请求(实际上建立的是 一个 socket 长连接),如果长期没有响应,服务中心就认为该服务提供者已经“挂了”,并将其剔除。
16、服务提供者没挂,但在注册中心里看不到?
首先,确认服务提供者是否连接了正确的注册中心,不只是检查配置中的注册中心地址,而且要检查实 际的网络连接。 其次,看服务提供者是否非常繁忙,比如压力测试,以至于没有CPU片段向注册中心发送心跳,这种情 况减小压力将自动恢复。
17、说说Dubbo的优先级配置
配置优先级别
1.以timeout为例,显示了配置的查找顺序,其他retries,loadbalance等类似。
(1) 方法级优先,接口级次之,全局配置在次之
(2) 如果级别一样,则消费方优先,提供方次之
(3) 其中,服务提供方配置,通过URL经由注册中心传递给消费方
2.建议由服务提供方设置超时,因为一个方法需要执行多长时间,服务提供方更清楚,如果一个消费方 同时引用多个服务,就不需要关心每个服务的超时设置。
18、聊聊Dubbo SPI机制?
SPI(Service Provider Interface),是一种服务发现机制,其实就是将结构的实现类写入配置当中,在服 务加载的时候将配置文件独处,加载实现类,这样就可以在运行的时候,动态的帮助接口替换实现类。 Dubbo的SPI其实是对java的SPI进行了一种增强,可以按需加载实现类之外,增加了 IOC 和 AOP 的特 性,还有自适应扩展机制。 SPI在dubbo应用很多,包括协议扩展、集群扩展、路由扩展、序列化扩展等等。 Dubbo对于文件目录的配置分为了三类。
1.META-INF/services/ 目录: 该目录下的 SPI 配置文件是为了用来兼容 Java SPI 。
2.META-INF/dubbo/ 目录: 该目录存放用户自定义的 SPI 配置文件。
3.META-INF/dubbo/internal/ 目录: 该目录存放 Dubbo 内部使用的 SPI 配置文件。
19、Dubbo的SPi和JAVA的SPI有什么区别?
Java Spi Java SPI 在查找扩展实现类的时候,遍历 SPI 的配置文件,并且将实现类全部实例化 Dubbo Spi
1,对 Dubbo 进行扩展,不需要改动 Dubbo 的源码
2,延迟加载,可以一次只加载自己想要加载的扩展实现。
3,增加了对扩展点 IOC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点。
4,Dubbo 的扩展机制能很好的支持第三方 IoC 容器,默认支持 Spring Bean。
20、说下Dubbo的服务引用的流程。
1.Dubbo客户端根据config文件里的信息从注册中心里订阅服务,并缓存到本地,后续的服务相关信息 的会动态更新到本地。
2.DubboProtocol根据provider的地址和接口连接到服务端server,开启客户端client,再创建 invoker。
3.用invoker为服务接口生成代理对象,这个代理对象是用来远程调用。 相关流程如下图所示。
21、dubbo配置
1. XML配置
Dubbo的XML配置是最早的配置方式,在配置过程中需要编写XML文件,指定Dubbo相关的标签和属性。其中,最基本的配置是服务提供者和服务消费者的相关配置,如下:
<!-- 服务提供者注册到注册中心 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<!-- 暴露服务 -->
<dubbo:service interface="com.xxx.xxxService" ref="xxxServiceImpl" timeout="3000" />
<!-- 引用服务 -->
<dubbo:reference interface="com.xxx.xxxService" id="xxxService" timeout="3000" />
在这段XML配置中,我们首先在标签中指定了注册中心的地址信息,然后在标签中指定了服务提供者的接口和实现类,以及引用服务的接口和实现。timeout属性用于设置超时时间。
除了服务提供者和服务消费者的相关配置外,还有其他相关的配置。比如,可以使用dubbo:protocol标签指定Dubbo使用的协议类型和端口号,同时也可以在dubbo:method标签中为方法单独指定超时时间等参数。
- 服务提供者:
- DemoService.java:
package org.apache.dubbo.demo;
public interface DemoService {
String sayHello(String name);
}
- 在服务提供方实现接口
- DemoServiceImpl.java:
package org.apache.dubbo.demo.provider;
import org.apache.dubbo.demo.DemoService;
public class DemoServiceImpl implements DemoService {
public String sayHello(String name) {
return "Hello " + name;
}
}
- 用 Spring 配置声明暴露服务
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder/>
<dubbo:application name="demo-provider"/>
<dubbo:registry address="zookeeper://${zookeeper.address:127.0.0.1}:2181"/>
<dubbo:provider token="true"/>
<bean id="demoService" class="org.apache.dubbo.samples.basic.impl.DemoServiceImpl"/>
<dubbo:service interface="org.apache.dubbo.samples.basic.api.DemoService" ref="demoService"/>
</beans>
- 加载 Spring 配置
public class Application {
public static void main(String[] args) throws InterruptedException {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-demo-provider.xml");
context.start();
System.out.println("dubbo service started");
// to hang up main thread
new CountDownLatch(1).await();
}
}
- 服务消费者
- 通过 Spring 配置引用远程服务
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder/>
<dubbo:application name="demo-consumer"/>
<dubbo:registry address="zookeeper://${zookeeper.address:127.0.0.1}:2181"/>
<dubbo:reference id="demoService" check="true" interface="org.apache.dubbo.samples.basic.api.DemoService"/>
</beans>
- 加载 Spring 配置,并调用远程服务
public class Application {
public static void main(String[] args) throws IOException {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-demo-consumer.xml");
context.start();
GreetingsService greetingsService = (GreetingsService) context.getBean("greetingsService");
String message = greetingsService.sayHi("dubbo");
System.out.println("Receive result ======> " + message);
System.in.read();
System.exit(0);
}
}
2. 注解配置
注解配置是一种比较简便的配置方式,可以直接在Java代码中使用注解指定Dubbo相关的配置信息。使用注解配置时,需要在配置类中添加@EnableDubbo注解开启Dubbo相关功能,示例如下:
@Configuration
@EnableDubbo(scanBasePackages = "com.xxx.service.impl")
public class DubboConfig {
}
其中,scanBasePackages属性指定了Dubbo扫描的包路径。然后就可以在服务提供者和服务消费者的实现类中使用Dubbo提供的注解进行配置,示例如下:
@Service(timeout = 3000)
public class XxxServiceImpl implements XxxService {
@Override
public String hello(String name) {
return "Hello " + name;
}
}
对于服务消费者,则可以使用@DubboReference注解引用服务,示例如下:
@Service
public class XxxConsumer {
@DubboReference(timeout = 3000)
private XxxService xxxService;
public String hello(String name) {
return xxxService.hello(name);
}
}
在这个例子中,我们使用@DubboReference注解指定了服务的接口和超时时间,然后在XxxConsumer类中通过xxxService调用服务。
3. 属性配置
属性配置是一种比较灵活的配置方式,它允许我们在配置文件中定义属性,然后在代码中读取这些属性并进行相关操作。使用属性配置时,我们需要在代码中创建一个DubboProperties对象,并将其中的属性值通过@ConfigurationProperties注解注入到该对象中。示例如下:
@Component
@ConfigurationProperties(prefix = "dubbo")
public class DubboProperties {
private String registryAddress;
// get/set方法省略
}
然后,我们可以使用这个DubboProperties对象中定义的属性配置Dubbo相关的参数,示例如下:
@Service(timeout = "#{dubboProperties.timeout}")
public class XxxServiceImpl implements XxxService {
@Autowired
private DubboProperties dubboProperties;
@Override
public String hello(String name) {
return "Hello " + name;
}
}
在这个例子中,我们使用了#{dubboProperties.timeout}的方式,读取DubboProperties对象中的timeout属性来指定服务超时时间。我们还可以在DubboProviders对象中定义其他属性,在需要的地方使用${}的方式读取这些属性。
常用注解
@DubboService 注解
定义好 Dubbo 服务接口后,提供服务接口的实现逻辑,并用 @DubboService
注解标记,就可以实现 Dubbo 的服务暴露
@DubboService
public class DemoServiceImpl implements DemoService {}
如果要设置服务参数,@DubboService
也提供了常用参数的设置方式。如果有更复杂的参数设置需求,则可以考虑使用其他设置方式
@DubboService(version = "1.0.0", group = "dev", timeout = 5000)
public class DemoServiceImpl implements DemoService {}
@DubboReference 注解
@Component
public class DemoClient {
@DubboReference
private DemoService demoService;
}
@DubboReference
注解将自动注入为 Dubbo 服务代理实例,使用 demoService 即可发起远程服务调用
@EnableDubbo 注解
@EnableDubbo
注解必须配置,否则将无法加载 Dubbo 注解定义的服务,@EnableDubbo
可以定义在主类上
@SpringBootApplication
@EnableDubbo
public class ProviderApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(ProviderApplication.class, args);
}
}
Spring Boot 注解默认只会扫描 main 类所在的 package,如果服务定义在其它 package 中,需要增加配置 EnableDubbo(scanBasePackages = {"org.apache.dubbo.springboot.demo.provider"})
22、Dubbo容器
23、Dubbo日式组件设计
适配众多日志框架,包括自定义日志打印格式。