一、标签示例
provider.xml 示例
<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"
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">
<dubbo:application name="demo-provider"/>
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<dubbo:protocol name="dubbo" port="20890"/>
<bean id="demoService" class="org.apache.dubbo.samples.basic.impl.DemoServiceImpl"/>
<dubbo:service interface="org.apache.dubbo.samples.basic.api.DemoService" ref="demoService"/>
</beans>
改为yml配置为这样:
dubbo:
application:
name: demo-provider
registry:
address: zookeeper://127.0.0.1:2181
protocol:
name: dubbo
port: 20890
scan:
base-packages: org.apache.dubbo.samples.basic.impl # 确保 DemoServiceImpl 在这个包或其子包中
# 注意:在 Spring Boot 中,你通常不需要显式声明 Dubbo 服务,因为 @Service 注解已经足够了
# 但如果你确实需要,你可能需要使用 Dubbo 的 @DubboService(dubbo3.x) 注解或其他方式
consumer.xml示例
<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"
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">
<dubbo:application name="demo-consumer"/>
<dubbo:registry group="aaa" address="zookeeper://127.0.0.1:2181"/>
<dubbo:reference id="demoService" check="false" interface="org.apache.dubbo.samples.basic.api.DemoService"/>
</beans>
#Dubbo Consumer在配置时通常不需要显式指定protocol。
Dubbo Consumer如果没有指定protocol参数,默认会将Provider注册的protocol provider url都获取并转换为Invoker放到Directory中。如果Consumer指定了protocol,则会在Directory中选择相应的protocol provider url转换为Invoker放在Directory中。
然而,在某些情况下,如果Provider同时支持多种协议(如dubbo和rest),而Consumer没有指定protocol,可能会导致Consumer间歇性地使用不同的协议。为了避免这种情况,可以将Consumer的protocol明确指定为所需的协议。
转为yml配置如下:
dubbo:
application:
name: demo-consumer
registry:
address: zookeeper://127.0.0.1:2181
group: aaa
reference:
demoService:
interface: org.apache.dubbo.samples.basic.api.DemoService
check: false
二、标签描述
相关标签描述如下:
<dubbo:service/> (服务端) 用于暴露一个服务,定义服务的元信息,一个服务可以用多个协议暴露,一个服务也可以注册到多个注册中心
<dubbo:reference/> 引用配置(消费端) 用于创建一个远程服务代理,一个引用可以指向多个注册中心
<dubbo:protocol/> 协议配置(服务端) 用于配置提供服务的协议信息,协议由提供方指定,消费方被动接受(所有未明确指定协议的服务都将使用 "dubbo" 协议和 20890 端口),注意与下面dubbo:provider的区别。
<dubbo:application/> 应用配置(服务端,消费端) 用于配置当前应用信息,不管该应用是提供者还是消费者
<dubbo:module/> 模块配置(不常用) 用于配置当前模块信息,可选
<dubbo:registry/> 注册中心配置(服务端,消费端) 用于配置连接注册中心相关信息
<dubbo:monitor/> 监控中心配置 用于配置连接监控中心相关信息,可选 <dubbo:monitor protocol="registry" /> 指定了监控中心使用 "registry" 协议,Dubbo 会从注册中心中获取监控中心的地址,并将服务调用的信息发送到监控中心进行统计和分析。
<dubbo:provider/> 提供方配置(服务端) 当 ProtocolConfig 和 ServiceConfig 某属性没有配置时,采用此缺省值,可选 <dubbo:provider protocol="dubbo" port="20890" threadpool="fixed" threads="200" /> 这些属性会覆盖 <dubbo:protocol> 中的相应设置
<dubbo:consumer/> 消费方配置(消费端) 当 ReferenceConfig 某属性没有配置时,采用此缺省值,可选 <dubbo:consumer timeout="5000" retries="2" loadbalance="random" check="true" /> 设置了消费端远程调用的超时时间为 5000 毫秒,重试次数为 2 次,负载均衡策略为随机,并在启动时检查服务提供者是否存在。此标签定义的配置是全局的,会影响所有通过该消费者进行的服务调用,如果你需要对某个特定的服务调用进行更细粒度的配置,可以使用 <dubbo:reference> 标签,并在其中指定相关的属性。
<dubbo:method/> 方法配置(服务端,消费端) 用于 ServiceConfig 和 ReferenceConfig 指定方法级的配置信息,<dubbo:service> 或 <dubbo:reference> 的子标签,<dubbo:method name="yourMethodName" timeout="3000" retries="2" /> 或 @Method(name = "yourMethodName", timeout = 3000, retries = 2) 设置 3 秒的超时时间和 2 次的重试次数
<dubbo:argument/> 参数配置(不常用) 用于指定方法参数配置,作为 <dubbo:method> 标签的子标签 ,<dubbo:argument index="0" type="java.lang.String" ... /> 为 yourMethodName 方法的第一个参数(索引为0)设置了类型为 java.lang.String
注:如下两个配置有啥区别?
<dubbo:provider protocol="dubbo" port="20890" threadpool="fixed" threads="200" /> <dubbo:protocol name="dubbo" port="20890"/>
<dubbo:protocol>
标签用于配置 Dubbo 服务的协议。当你在应用程序中同时提供了多个服务,并且这些服务希望共享相同的协议配置(如端口号、协议类型等)时,<dubbo:protocol>
就非常有用。该标签配置的是全局的协议参数,会影响所有使用该协议的服务。
<dubbo:provider>
标签用于配置服务提供者的属性,这些属性会覆盖<dubbo:protocol>
中的相应设置(如果它们都存在)。通常,你会在每个<dubbo:service>
标签内部或外部使用<dubbo:provider>
,以便为特定的服务提供者提供定制的配置。<dubbo:provider>
标签并不直接指定协议名称和端口号,而是用于配置如线程池、超时时间等与服务提供者相关的参数。如果要在<dubbo:provider>
中指定协议和端口,这通常是不必要的,因为<dubbo:protocol>
已经提供了这样的全局配置。在某些情况下,你可能希望为特定的服务提供者提供不同的线程池配置或超时时间,这时你可以在
<dubbo:service>
内部使用<dubbo:provider>
标签来实现。但请注意,这样做并不会改变服务的协议和端口号。<dubbo:service interface="org.apache.dubbo.samples.basic.api.DemoService" ref="demoService">
<dubbo:provider threadpool="fixed" threads="200" />
</dubbo:service>
三、引用别的dubbo xml配置
常用于引用公共配置文件。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
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">
<import resource="classpath:dubbo-common.xml" />
<!-- 特定于服务的配置 -->
<dubbo:service interface="com.example.YourService" ref="yourServiceImpl" />
<!-- 其他服务相关配置 -->
</beans>
四、详细配置
1.服务重试。
<dubbo:consumer retries="2"></dubbo:consumer>
2.集群容错。
可配置于服务者和消费者:
<dubbo:service cluster="failsafe" />或<dubbo:reference cluster="failsafe" />
Failover Cluster:失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。 可通过 retries="2"
来设置重试次数(不含第一次)。根据需要可配置于:<dubbo:service retries="2" />或<dubbo:reference retries="2" />或<dubbo:method name="findFoo" retries="2" />。注意:它是默认所选的模式
Failfast Cluster:快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。
Failsafe Cluster: 失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。
Failback Cluster: 失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
Forking Cluster: 并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2"
来设置最大并行数。
Broadcast Cluster: 广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息。
3.负载均衡
如下配置可配置服务端和客户端,或者他们的方法上都可:
<dubbo:service interface="..." loadbalance="roundrobin" />
<dubbo:reference interface="..." loadbalance="roundrobin" />
<dubbo:service interface="...">
<dubbo:method name="..." loadbalance="roundrobin"/>
</dubbo:service>
<dubbo:reference interface="...">
<dubbo:method name="..." loadbalance="roundrobin"/>
</dubbo:reference>
Random LoadBalance:按权重设置随机概率,数值越大接收的请求越多。缺省为 random
随机调用。配置如下:
<dubbo:service interface="com.example.YourService" ref="yourServiceImpl" weight="100" />
@Service(weight = 100)
@Reference(weight = 100)
RoundRobin LoadBalance:按公约后的权重设置轮询比率。会存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。
LeastActive LoadBalance:最少活跃调用数,使慢的提供者收到更少请求,相同活跃数的随机,活跃数指调用前后计数差。
ConsistentHash LoadBalance:一致性 Hash,相同参数的请求总是发到同一提供者。
4.线程模型
不是很理解,回头再来补充(不懂的都是牛逼的知识,也许面试会问)。
5.直连提供者
绕过注册中心点对点直连。
6.只订阅
为方便开发测试,可以让服务提供者开发方,只订阅服务(开发的服务可能依赖其它服务),而不注册正在开发的服务,通过直连测试正在开发的服务,因为如果一个正在开发中的服务提供者注册,可能会影响消费者不能正常运行。
<dubbo:registry address="10.20.153.10:9090" register="false" />
7.多协议
一般不常见,参照如下配置。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<dubbo:application name="world" />
<dubbo:registry id="registry" address="10.20.141.150:9090" username="admin" password="hello1234" />
<!-- 多协议配置 -->
<dubbo:protocol name="dubbo" port="20880" />
<dubbo:protocol name="rmi" port="1099" />
<dubbo:protocol name="hessian" port="8080" />
<!-- 使用dubbo协议暴露服务 -->
<dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService" protocol="dubbo" />
<!-- 使用rmi协议暴露服务 -->
<dubbo:service interface="com.alibaba.hello.api.DemoService" version="1.0.0" ref="demoService" protocol="rmi" />
<!-- 使用多个协议暴露服务 -->
<dubbo:service id="helloService1" interface="com.alibaba.hello.api.HelloService1" version="1.0.0" protocol="dubbo,hessian" />
</beans>
8.多注册中心
不常见,Dubbo 支持同一服务向多注册中心同时注册,或者不同服务分别注册到不同的注册中心上去,甚至可以同时引用注册在不同注册中心上的同名服务。
多注册中心注册:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<dubbo:application name="world" />
<!-- 多注册中心配置 -->
<dubbo:registry id="hangzhouRegistry" address="10.20.141.150:9090" />
<dubbo:registry id="qingdaoRegistry" address="10.20.141.151:9010" default="false" />
<!-- 向多个注册中心注册 -->
<dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService" registry="hangzhouRegistry,qingdaoRegistry" />
</beans>
不同服务使用不同注册中心:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<dubbo:application name="world" />
<!-- 多注册中心配置 -->
<dubbo:registry id="chinaRegistry" address="10.20.141.150:9090" />
<dubbo:registry id="intlRegistry" address="10.20.154.177:9010" default="false" />
<!-- 向中文站注册中心注册 -->
<dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService" registry="chinaRegistry" />
<!-- 向国际站注册中心注册 -->
<dubbo:service interface="com.alibaba.hello.api.DemoService" version="1.0.0" ref="demoService" registry="intlRegistry" />
</beans>
多注册中心引用:
如:CRM 需同时调用中文站和国际站的 PC2 服务,PC2 在中文站和国际站均有部署,接口及版本号都一样,但连的数据库不一样。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<dubbo:application name="world" />
<!-- 多注册中心配置 -->
<dubbo:registry id="chinaRegistry" address="10.20.141.150:9090" />
<dubbo:registry id="intlRegistry" address="10.20.154.177:9010" default="false" />
<!-- 引用中文站服务 -->
<dubbo:reference id="chinaHelloService" interface="com.alibaba.hello.api.HelloService" version="1.0.0" registry="chinaRegistry" />
<!-- 引用国际站站服务 -->
<dubbo:reference id="intlHelloService" interface="com.alibaba.hello.api.HelloService" version="1.0.0" registry="intlRegistry" />
</beans>
9.服务分组
当一个接口有多种实现时,可以用 group 区分。
服务:
<dubbo:service group="feedback" interface="com.xxx.IndexService" />
<dubbo:service group="member" interface="com.xxx.IndexService" />
调用方:
<dubbo:reference id="feedbackIndexService" group="feedback" interface="com.xxx.IndexService" />
<dubbo:reference id="memberIndexService" group="member" interface="com.xxx.IndexService" />
#或调用任意组
<dubbo:reference id="barService" interface="com.foo.BarService" group="*" />
假设我们有一个在线购物平台,其中有一个OrderService
接口,用于处理订单相关的业务逻辑。随着业务的发展,我们可能需要为不同的业务场景或客户群体提供不同的订单处理逻辑。这时,我们可以使用Dubbo的服务分组来实现。如下一看便知:
//首先,我们定义一个OrderService接口,它包含了一些订单处理的方法。
public interface OrderService {
void createOrder(Order order);
Order getOrderById(Long orderId);
// ... 其他订单处理方法
}
//接下来,我们为OrderService接口提供两种实现,分别对应不同的业务场景或客户群体。
//普通订单
@Service(group = "normal")
public class NormalOrderServiceImpl implements OrderService {
// ... 实现订单处理的逻辑(针对普通客户)
}
//VIP订单
@Service(group = "vip")
public class VipOrderServiceImpl implements OrderService {
// ... 实现订单处理的逻辑(针对VIP客户,可能包含一些额外的优惠或服务)
}
//普通调用者
@Reference(group = "normal")
private OrderService normalOrderService;
public void processNormalOrder(Order order) {
normalOrderService.createOrder(order);
// ... 其他处理逻辑
}
//VIP调用者
@Reference(group = "vip")
private OrderService vipOrderService;
public void processVipOrder(Order order) {
vipOrderService.createOrder(order);
// ... VIP客户特有的处理逻辑
}
10.静态服务
不常用,有时候希望人工管理服务提供者的上线和下线,此时需将注册中心标识为非动态管理模式。
<dubbo:registry address="10.20.141.150:9090" dynamic="false" />
11.多版本
当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。
可以按照以下的步骤进行版本迁移:
- 在低压力时间段,先升级一半提供者为新版本
- 再将所有消费者升级为新版本
- 然后将剩下的一半提供者升级为新版本
老版本服务提供者配置:
<dubbo:service interface="com.foo.BarService" version="1.0.0" />
新版本服务提供者配置:
<dubbo:service interface="com.foo.BarService" version="2.0.0" />
老版本服务消费者配置:
<dubbo:reference id="barService" interface="com.foo.BarService" version="1.0.0" />
新版本服务消费者配置:
<dubbo:reference id="barService" interface="com.foo.BarService" version="2.0.0" />
12.分组聚合
通过分组对结果进行聚合并返回聚合后的结果,比如菜单服务,用group区分同一接口的多种实现,现在消费方需从每种group中调用一次并返回结果,对结果进行合并之后返回,这样就可以实现聚合菜单项。
搜索所有分组:
<dubbo:reference interface="com.xxx.MenuService" group="*" merger="true" />
合并指定分组:
<dubbo:reference interface="com.xxx.MenuService" group="aaa,bbb" merger="true" />
13.json泛化调用
直接传递字符串来完成一次调用。即用户可以直接传递参数对象的json字符串来完成一次Dubbo泛化调用。在微服务架构中,服务接口可能会经常变化。如果我们每次都需要在服务消费者端更新接口代码并重新编译,那将非常繁琐。泛化调用让我们无需关心接口的具体定义,只要知道方法名和参数类型,就可以进行调用。
可以将泛化调用想象成一种“黑盒”调用方式。你不需要关心远程服务的内部实现细节,只需要知道方法名、参数类型和参数值,就可以发起调用。
场景:
-
动态脚本调用:比如你有一个 Shell 脚本或 Python 脚本,想要调用 Dubbo 服务,但由于脚本语言与 Java 不兼容,你无法直接调用 Java 接口。这时,你可以使用泛化调用来实现跨语言的调用。
-
API 网关:API 网关是微服务架构中的“门卫”,负责接收外部请求并转发到内部服务。由于 API 网关可能需要调用多个不同的服务,使用泛化调用可以简化代码,提高灵活性。
-
动态代理:在某些场景下,你可能想要创建一个动态代理来调用远程服务。这个代理可能不知道具体的服务接口,但它可以根据用户的请求动态地构建请求并发送给远程服务。这时,泛化调用就派上了用场。
-
测试与模拟:在测试阶段,你可能想要模拟某个远程服务的行为。使用泛化调用,你可以直接发送请求给这个模拟服务,并观察客户端的响应,从而验证客户端的逻辑是否正确。
//服务提供者
public User setUser(User user) {
return user;
}
//实体类
@Data
public class User {
String name;
int age;
}
//进行一次泛化调用
public class GenericInvoke {
public static void main(String[] args) {
ApplicationConfig app = new ApplicationConfig("ConsumerTest");
RegistryConfig reg = new RegistryConfig("nacos://localhost:8848");
DubboBootstrap bootstrap = DubboBootstrap.getInstance();
bootstrap.application(app);
bootstrap.registry(reg);
bootstrap.start();
try {
// 引用远程服务
ReferenceConfig<GenericService> reference = new ReferenceConfig<>();
// 弱类型接口名
reference.setInterface("com.xxx.api.service.TestService");
reference.setGroup("dev");
reference.setVersion("1.0");
reference.setRetries(0);
// RpcContext中设置generic=gson
RpcContext.getContext().setAttachment("generic","gson");
// 声明为泛化接口
reference.setGeneric(true);
reference.setCheck(false);
GenericService genericService = ReferenceConfigCache.getCache().get(reference);
// 传递参数对象的json字符串进行一次调用
Object res = genericService.$invoke("setUser", new String[]{"com.xxx.api.service.User"}, new Object[]{"{'name':'Tom','age':24}"});
System.out.println("result[setUser]:"+res); // 响应结果:result[setUser]:{name=Tom, class=com.xxx.api.service.User, age=24}
} catch (Throwable ex) {
ex.printStackTrace();
}
}
}
14.结果缓存
顾名思义,用于加速热门数据的访问速度,Dubbo 提供声明式缓存,以减少用户加缓存的工作量。
<dubbo:reference interface="com.foo.BarService" cache="lru" />
<dubbo:reference interface="com.foo.BarService">
<dubbo:method name="findBar" cache="lru" />
</dubbo:reference>
缓存类型
lru
基于最近最少使用原则删除多余缓存,保持最热的数据被缓存。threadlocal
当前线程缓存,比如一个页面渲染,用到很多 portal,每个 portal 都要去查用户信息,通过线程缓存,可以减少这种多余访问。jcache
与 JSR107 集成,可以桥接各种缓存实现。
15.收集dubbo广播响应
适用场景:对于一个dubbo消费者,广播调用多个dubbo 提供者,该消费者可以收集所有服务提供者的响应结果。说白了就是能获取多个服务提供者的返回数据。
//服务提供者
@Service
public class DubboHealthServiceImpl implements DubboHealthService {
@Override
public String health() {
// int i = 1/0;
return "i am provider2";
}
}
//消费者
@RestController
public class TestServiceConsumer {
@Reference(interfaceClass = DubboHealthService.class,cluster = "broadcast2")
private DubboHealthService dubboHealthService;
@GetMapping("/health")
public String broadCast(){
try{
dubboHealthService.health();
}catch (Exception e){
Map<String, String> m = RpcContext.getServerContext().getAttachments();
return m.toString()+"|"+"fail";
}
Map<String, String> m = RpcContext.getServerContext().getAttachments();
return m.toString()+"|"+"success";
}
}
16.指定IP
就是钟爱某个IP上的服务,每次都调用它。
// 需要依赖的class
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.cluster.router.address.Address;
@RestController
public class TestServiceConsumer {
@DubboReference(interfaceClass = TestService.class,group = "dev",parameters = {"router","address"})
private TestService testService;
@GetMapping("/invokeByIpPortSpecified")
public String invokeByIp(){
try {
// 根据provider的ip,port创建Address实例
Address address = new Address("10.220.47.253", 20880);
RpcContext.getContext().setObjectAttachment("address", address);
return testService.sayHello("Tom");
}catch (Throwable ex){
return ex.getMessage();
}
}
}
17.回声测试。
用于检测服务是否可用,回声测试按照正常请求流程执行,能够测试整个调用是否通畅,可用于监控。
<dubbo:reference id="memberService" interface="com.xxx.MemberService" />
// 远程服务引用
MemberService memberService = ctx.getBean("memberService");
EchoService echoService = (EchoService) memberService; // 强制转型为EchoService
// 回声测试可用性
String status = echoService.$echo("OK");
assert(status.equals("OK"));
18.隐式参数
19.异步调用
基于 NIO 的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服务,相对多线程开销较小。
//需要服务提供者事先定义 CompletableFuture 签名的服务
public interface AsyncService {
CompletableFuture<String> sayHello(String name);
}
//XML引用服务:
<dubbo:reference id="asyncService" timeout="10000" interface="com.alibaba.dubbo.samples.async.api.AsyncService"/>
// 调用直接返回CompletableFuture
CompletableFuture<String> future = asyncService.sayHello("async call request");
// 增加回调
future.whenComplete((v, t) -> {
if (t != null) {
t.printStackTrace();
} else {
System.out.println("Response: " + v);
}
});
// 早于结果输出
System.out.println("Executed before response return.");
20.参数回调
参数回调方式与调用本地 callback 或 listener 相同,只需要在 Spring 的配置文件中声明哪个参数是 callback 类型即可。Dubbo 将基于长连接生成反向代理,这样就可以从服务器端调用客户端逻辑。
21.事件通知
在调用之前、调用之后、出现异常时,会触发 oninvoke
、onreturn
、onthrow
三个事件,可以配置当事件发生时,通知哪个类的哪个方法。
22.本地伪装
通常用于服务降级,比如某验权服务,当服务提供方全部挂掉后,客户端不抛出异常,而是通过 Mock 数据返回授权失败。
<dubbo:reference interface="com.foo.BarService" mock="true" />
<dubbo:reference interface="com.foo.BarService" mock="com.foo.BarServiceMock" />
<dubbo:reference interface="com.foo.BarService" mock="return null" />
package com.foo;
public class BarServiceMock implements BarService {
public String sayHello(String name) {
// 你可以伪造容错数据,此方法只在出现RpcException时被执行
return "容错数据";
}
}
23.连接控制
连接控制功能可以使用户能够控制和管理进出服务器连接数,限制连接数并设置超时,以确保 Dubbo 系统的稳定性和性能,还允许用户根据 IP 地址、端口和协议配置不同级别的访问控制,保护系统免受恶意流量的影响,并降低服务中断的风险,此外提供了一种监视当前流量和连接状态的方法。
<dubbo:protocol name="dubbo" accepts="10" />
#或
<dubbo:provider protocol="dubbo" accepts="10" />
#或
<dubbo:reference interface="com.foo.BarService" connections="10" />
24.优雅停机
Dubbo 是通过 JDK 的 ShutdownHook 来完成优雅停机的,所以如果用户使用 kill -9 PID
等强制关闭指令,是不会执行优雅停机的,只有通过 kill PID
时,才会执行。
服务提供方
- 停止时,先标记为不接收新请求,新请求过来时直接报错,让客户端重试其它机器。
- 然后,检测线程池中的线程是否正在运行,如果有,等待所有线程执行完成,除非超时,则强制关闭。
服务消费方
- 停止时,不再发起新的调用请求,所有新的调用在客户端即报错。
- 然后,检测有没有请求的响应还没有返回,等待响应返回,除非超时,则强制关闭。
设置优雅停机超时时间,缺省超时时间是 10 秒,如果超时则强制关闭。
# dubbo.properties
dubbo.service.shutdown.wait=15000