概述
文章目录
- 注解配置
- 服务提供方
- 消费方
- 启动时检查
- 集群容错
- 负载均衡
- 服务分组
- 多版本
- 参数验证依赖
- 结果缓存
- 使用泛化调用(消费者端)
- 实现泛化调用(生产者端)
- 回声测试
- 异步调用
- 参数回调
- 事件通知
- 本地存根(相当于过滤器)
- 本地伪装
- 延迟暴露
- 并发控制
- 连接控制
- 令牌验证
- 路由规则
- 参考
注解配置
服务提供方
@Slf4j
@Service //属于Dubbo的@Service注解,非Spring 作用:暴露服务
@Component
public class UserServiceImpl implements UserService {
@Override
public String userAddress(String userId) {
log.info("请求提供者服者参数:[{}]", userId);
return userId;
}
}
消费方
@Slf4j
@Component
public class Uservice {
@Reference
private UserService userService;
public void getUserAddress(String userId) {
String result = userService.userAddress(userId);
log.info("消费端请求结果:[{}]", result);
}
}
启动时检查
Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题,默认 check=“true” 。
可以通过 check=“false” 关闭检查,比如,测试时,有些服务不关心,或者出现了循环依赖,必须有一方先启动。
另外,如果你的 Spring 容器是懒加载的,或者通过 API 编程延迟引用服务,请关闭 check,否则服务临时不可用时,会抛出异常,拿到 null 引用,如果 check=“false” ,总是会返回引用,当服务恢复时,能自动连上。
关闭某个服务的启动时检查 (没有提供者时报错):
<dubbo:reference interface="com.foo.BarService" check="false" />
关闭所有服务的启动时检查 (没有提供者时报错):
<dubbo:consumer check="false" />
集群容错
在集群调用失败时,Dubbo 提供了多种容错方案,缺省为 failover 重试。
[DUBBO] Notify urls for subscribe url
consumer://169.254.75.192/com.myke.api.UserService?
application=user-service-consumer
&category=providers,configurators,routers
&default.check=false&dubbo=2.6.2
&interface=com.myke.api.UserService&methods=userAddress
&pid=21208&side=consumer
×tamp=1583561061678,
urls: [dubbo://169.254.75.192:30880/com.myke.api.UserService?
anyhost=true
&application=user-service-provider&dubbo=2.6.2
&generic=false&interface=com.myke.api.UserService
&methods=userAddress&pid=17392
&side=provider×tamp=1583561004341,
empty://169.254.75.192/com.myke.api.UserService?
application=user-service-consumer
&category=configurators&default.check=false&dubbo=2.6.2
&interface=com.myke.api.UserService&methods=userAddress&pid=21208
&side=consumer×tamp=1583561061678,
empty://169.254.75.192/com.myke.api.UserService?
application=user-service-consumer&category=routers
&default.check=false&dubbo=2.6.2
&interface=com.myke.api.UserService&methods=userAddress
&pid=21208&side=consumer×tamp=1583561061678],
dubbo version: 2.6.2, current host: 169.254.75.192
消费者调用分析
-
为接口常见代理
-
获取集群容错对象,默认
FailoverClusterInvoker
//-- 创建代理 com.alibaba.dubbo.config.spring.ReferenceBean#getObject com.alibaba.dubbo.config.ReferenceConfig#init // dubbo 的顶层接口 interfaceClass = GenericService.class; // 创建代理 ref = createProxy(map); // create service proxy return (T) proxyFactory.getProxy(invoker); com.alibaba.dubbo.rpc.proxy.AbstractProxyFactory#getProxy(com.alibaba.dubbo.rpc.Invoker<T>, java.lang.Class<?>[]) public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) { return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), interfaces, new InvokerInvocationHandler(invoker)); } com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler#invoke return invoker.invoke(new RpcInvocation(method, args)).recreate(); // invoker 获取的是一个集群容错对象 // invoker 默认的第一个对象是就是 MockClusterInvoker com.alibaba.dubbo.rpc.cluster.support.wrapper.MockClusterInvoker // invoker 的默认集群容错对象是 FailoverClusterInvoker com.alibaba.dubbo.rpc.cluster.support.FailoverClusterInvoker#doInvoke //获取默认重试次数,即 len 为 3,重试次数小于0时,则默认为1次 // getUrl就是上面的url int len = getUrl().getMethodParameter(invocation.getMethodName(), Constants.RETRIES_KEY, Constants.DEFAULT_RETRIES) + 1; if (len <= 0) { len = 1; } // 获取负载均衡器 Invoker<T> invoker = select(loadbalance, invocation, copyinvokers, invoked); // 调用返回值 Result result = invoker.invoke(invocation);
-
Failover Cluster:
FailoverClusterInvoker
失败自动切换,当出现失败,重试其它服务器 。
通常用于读操作,但重试会带来更长延迟。
可通过 retries=“2” 来设置重试次数(不含第一次),默认共计调用三次服务方
。
重试次数配置如下:<dubbo:service retries=“2” /> 或 <dubbo:reference retries=“2” /> 或 <dubbo:reference> <dubbo:method name="findFoo" retries="2" /> </dubbo:reference>
-
Failfast Cluster :
FailfastClusterInvoker
快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。 -
Failsafe Cluster:
FailsafeClusterInvoker
失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。 -
Failback Cluster
失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。 -
Forking Cluster
并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪
费更多服务资源。可通过 forks=“2” 来设置最大并行数。 -
Broadcast Cluster
广播调用所有提供者,逐个调用,任意一台报错则报错 。通常用于通知所有提供者更新缓存
或日志等本地资源信息。
<dubbo:service cluster=“failsafe” />
负载均衡
- Random LoadBalance
随机
,按权重设置随机概率。
在一个截面上碰撞的概率高,但调用量越大分布越均匀
,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。 - RoundRobin LoadBalance
轮循
,按公约后的权重设置轮循比率。
存在慢的提供者累积请求的问题
,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。 - ConsistentHash LoadBalance
一致性 Hash
,相同参数的请求总是发到同一提供者
。
当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
缺省只对第一个参数 Hash,如果要修改,请配置
<dubbo:parameter key="hash.arguments"value="0,1" />
缺省用 160 份虚拟节点,如果要修改,请配置
<dubbo:parameter key="hash.nodes" value="320" />
hash.arguments : 当进行调用时候根据调用方法的哪几个参数生成key,并根据key来通过一致性hash算法来选择调用结点。
例如调用方法invoke(String s1,String s2); 若hash.arguments为1(默认值),则仅取invoke的参数1(s1)来生成hashCode。
hash.nodes: 为结点的副本数 - LeastActive LoadBalance
最少活跃调用数
,相同活跃数的随机,活跃数指调用前后计数差。
使慢的提供者收到更少请求
,因为越慢的提供者的调用前后计数差会越大。
服务分组
当一个接口有多种实现时,可以用 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.IndexSe
rvice" />
<dubbo:reference id="memberIndexService" group="member" interface="com.xxx.IndewxServi
ce" />
多版本
当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。
可以按照以下的步骤进行版本迁移:
- 在低压力时间段,先升级一半提供者为新版本
- 再将所有消费者升级为新版本
- 然后将剩下的一半提供者升级为新版本
老版本服务提供者配置:
<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" />
如果不需要区分版本,可以按照以下的方式配置 :
<dubbo:reference id="barService" interface="com.foo.BarService" version="*" />
参数验证依赖
api 接口pom依赖
<!--参数验证 start-->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.2.0.Final</version>
</dependency>
<!--参数验证 end-->
参数校验注解
public interface UserService {
String userAddress(@NotBlank(message = "userId 不能为空") String userId);
}
启用参数校验
@Reference(validation = "true")
private UserService userService;
异常信息
javax.validation.ConstraintViolationException:
Failed to validate service:
com.myke.api.UserService,
method: userAddress,
cause: [ConstraintViolationImpl{interpolatedMessage='userId 不能为空',
propertyPath=userAddressArgument0, rootBeanClass=class com.myke.api.UserService_UserAddressParameter_java.lang.String,
messageTemplate='userId 不能为空'}]
结果缓存
lru 基于最近最少使用原则删除多余缓存,保持最热的数据被缓存。
使用泛化调用(消费者端)
泛化接口调用方式主要用于客户端没有 API 接口及模型类元的情况,参数及返回值中的所有POJO 均用 Map 表示,通常用于框架集成,
比如:实现一个通用的服务测试框架,可通过
GenericService 调用所有服务实现。
在 Spring 配置申明 generic=“true” :
<dubbo:reference id="barService" interface="com.foo.BarService" generic="true" />
在 Java 代码获取 barService 并开始泛化调用:
GenericService barService = (GenericService) applicationContext.getBean("barService");
Object result = barService.$invoke("sayHello", new String[] { "java.lang.String" }, new
Object[] { "World" });
实现泛化调用(生产者端)
在 Java 代码中实现 GenericService 接口:
实现 GenericService 接口后,消费者引用改api时,所有调用的方法都会进入到该方法中
package com.foo;
public class MyGenericService implements GenericService {
public Object $invoke(String methodName, String[] parameterTypes, Object[] args) t
hrows GenericException {
if ("sayHello".equals(methodName)) {
return "Welcome " + args[0];
}
}
}
通过 Spring 暴露泛化实现
在 Spring 配置申明服务的实现:
<bean id="genericService" class="com.foo.MyGenericService" />
<dubbo:service interface="com.foo.BarService" ref="genericService" />
回声测试
回声测试用于检测服务是否可用,回声测试按照正常请求流程执行,能够测试整个调用是否通畅,可用于监控。
所有服务自动实现 EchoService 接口,只需将任意服务引用强制转型为 EchoService ,即可使用。
代码:
// 远程服务引用
MemberService memberService = ctx.getBean("memberService");
EchoService echoService = (EchoService) memberService;
// 强制转型为EchoService
// 回声测试可用性
String status = echoService.$echo("OK");
assert(status.equals("OK"));
异步调用
基于 NIO 的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服
务,相对多线程开销较小。
在 consumer.xml 中配置:
<dubbo:reference id="fooService" interface="com.alibaba.foo.FooService">
<dubbo:method name="findFoo" async="true" />
</dubbo:reference>
<dubbo:reference id="barService" interface="com.alibaba.bar.BarService">
<dubbo:method name="findBar" async="true" />
</dubbo:reference>
参数回调
参数回调方式与调用本地 callback 或 listener 相同,只需要在 Spring 的配置文件中声明哪个
参数是 callback 类型即可。
Dubbo 将基于长连接生成反向代理,这样就可以从服务器端调用客户端逻辑
服务提供者配置示例
<bean id="callbackService" class="com.callback.impl.CallbackServiceImpl" />
<dubbo:service interface="com.callback.CallbackService" ref="callbackService"
connections="1" callbacks="1000">
<dubbo:method name="addListener">
<dubbo:argument index="1" callback="true" />
<!--也可以通过指定类型的方式-->
<!--<dubbo:argument type="com.demo.CallbackListener" callback="true" />-->
</dubbo:method>
</dubbo:service>
事件通知
在调用之前、调用之后、出现异常时,会触发 oninvoke 、 onreturn 、 onthrow 三个事件,
可以配置当事件发生时,通知哪个类的哪个方法 。
服务消费者 Callback 接口
interface Notify {
public void onreturn(Person msg, Integer id);
public void onthrow(Throwable ex, Integer id);
}
服务消费者 Callback 配置
<bean id ="demoCallback" class = "com.alibaba.dubbo.callback.implicit.NofifyImpl" />
<dubbo:reference id="demoService" interface="com.alibaba.dubbo.callback.implicit.IDemo
Service" version="1.0.0" group="cn" >
<dubbo:method name="get" async="true" onreturn = "demoCallback.onreturn" onthrow=
"demoCallback.onthrow" />
</dubbo:reference>
本地存根(相当于过滤器)
远程服务后,客户端通常只剩下接口,而实现全在服务器端,但提供方有些时候想在客户端
也执行部分逻辑,比如:做 ThreadLocal 缓存,提前验证参数,调用失败后伪造容错数据等
等,此时就需要在 API 中带上 Stub,客户端生成 Proxy 实例,会把 Proxy 通过构造函数传给
Stub ,然后把 Stub 暴露给用户,Stub 可以决定要不要去调 Proxy。
在 spring 配置文件中按以下方式配置:
<dubbo:service interface="com.foo.BarService" stub="com.foo.BarServiceStub" />
1.Stub 必须有可传入 Proxy 的构造函数。
2.在 interface 旁边放一个 Stub 实现,它实现 BarService 接口,并有一个传入远程BarService 实例的构造函数
本地伪装
本地伪装 通常用于服务降级
,比如某验权服务,当服务提供方全部挂掉后,客户端不抛出
异常,而是通过 Mock 数据返回授权失败。
在 spring 配置文件中按以下方式配置:
<dubbo:reference id="userService" interface="com.myke.consumer.api.UserService" mock="com.myke.consumer.api.UserServiceMock"/>
1.Mock 是 Stub 的一个子集,便于服务提供方在客户端执行容错逻辑,因经常需要在出
现 RpcException (比如网络失败,超时等)时进行容错,而在出现业务异常(比如登录用户 名密码错误)时不需要容错,如果用 Stub,可能就需要捕获并依赖 RpcException 类,而 用 Mock 就可以不依赖 RpcException,因为它的约定就是只有出现 RpcException 时才 执行
。
2.在 interface 旁放一个 Mock 实现,它实现 BarService 接口,并有一个无参构造函数
延迟暴露
延迟到 Spring 初始化完成后,再暴露服务,规避spring的加载死锁问题
<dubbo:service delay="-1" />
或
<dubbo:provider deplay=”-1” />
并发控制
限制 com.foo.BarService 的每个方法,服务器端并发执行(或占用线程池线程数)不能超过
10 个:
<dubbo:service interface="com.foo.BarService" executes="10" />
限制 com.foo.BarService 的 sayHello 方法,服务器端并发执行(或占用线程池线程数)不
能超过 10 个:
<dubbo:service interface="com.foo.BarService">
<dubbo:method name="sayHello" executes="10" />
</dubbo:service>
限制 com.foo.BarService 的每个方法,每客户端并发执行(或占用连接的请求数)不能超过
10 个:
<dubbo:service interface="com.foo.BarService" actives="10" />
<dubbo:reference interface="com.foo.BarService" actives="10" />
限制 com.foo.BarService 的 sayHello 方法,每客户端并发执行(或占用连接的请求数)不
能超过 10 个:
<dubbo:service interface="com.foo.BarService">
<dubbo:method name="sayHello" actives="10" />
</dubbo:service>
<dubbo:reference interface="com.foo.BarService">
<dubbo:method name="sayHello" actives="10" />
</dubbo:service>
如果 <dubbo:service> 和 <dubbo:reference> 都配了actives, <dubbo:reference> 优先
连接控制
服务端连接控制
限制服务器端接受的连接不能超过 10 个
<dubbo:provider protocol=“dubbo” accepts=“10” />
<dubbo:protocol name=“dubbo” accepts=“10” />
客户端连接控制
限制客户端服务使用连接不能超过 10 个
<dubbo:reference interface=“com.foo.BarService” connections=“10” />
<dubbo:service interface=“com.foo.BarService” connections=“10” />
延迟连接
延迟连接用于减少长连接数。当有调用发起时,再创建长连接。
<dubbo:protocol name=“dubbo” lazy=“true” />
注意:该配置只对使用长连接的 dubbo 协议生效。
粘滞连接
粘滞连接用于有状态服务,尽可能让客户端总是向同一提供者发起调用,除非该提供者挂
了,再连另一台。
粘滞连接将自动开启延迟连接,以减少长连接数。
<dubbo:protocol name=“dubbo” sticky=“true” />
令牌验证
通过令牌验证在注册中心控制权限,以决定要不要下发令牌给消费者,可以防止消费者绕过
注册中心访问提供者,另外通过注册中心可灵活改变授权方式,而不需修改或升级提供者
可以全局设置开启令牌验证:
<!--随机token令牌,使用UUID生成-->
<dubbo:provider interface="com.foo.BarService" token="true" />
<!--固定token令牌,相当于密码-->
<dubbo:provider interface="com.foo.BarService" token="123456" />
<!--随机token令牌,使用UUID生成-->
<dubbo:service interface="com.foo.BarService" token="true" />
<!--固定token令牌,相当于密码-->
<dubbo:service interface="com.foo.BarService" token="123456" />
路由规则
路由规则 决定一次 dubbo 服务调用的目标服务器,分为条件路由规则和脚本路由规则,并
且支持可扩展 。
向注册中心写入路由规则的操作通常由监控中心或治理中心的页面完成
registry.register(URL.valueOf(“condition://0.0.0.0/com.foo.BarService?category=routers
&dynamic=false&rule=” + URL.encode(“host = 10.20.153.10 => host = 10.20.153.11”) + "));
condition:// 表示路由规则的类型,支持条件路由规则和脚本路由规则,可扩展,必
填。
0.0.0.0 表示对所有 IP 地址生效,如果只想对某个 IP 的生效,请填入具体 IP,必填。
com.foo.BarService 表示只对指定服务生效,必填。
category=routers 表示该数据为动态配置类型,必填。
dynamic=false 表示该数据为持久数据,当注册方退出时,数据依然保存在注册中心,
必填。
enabled=true 覆盖规则是否生效,可不填,缺省生效。
force=false 当路由结果为空时,是否强制执行,如果不强制执行,路由结果为空的路
由规则将自动失效,可不填,缺省为 flase 。
runtime=false 是否在每次调用时执行路由规则,否则只在提供者地址列表变更时预先
执行并缓存结果,调用时直接从缓存中获取路由结果。如果用了参数路由,必须设为
true ,需要注意设置会影响调用的性能,可不填,缺省为 flase 。
priority=1 路由规则的优先级,用于排序,优先级越大越靠前执行,可不填,缺省为
0 。
rule=URL.encode(“host = 10.20.153.10 => host = 10.20.153.11”) 表示路由规则的内容,
必填。
条件路由规则
基于条件表达式的路由规则,如: host = 10.20.153.10 => host = 10.20.153.11
规则:
=> 之前的为消费者匹配条件,所有参数和消费者的 URL 进行对比,当消费者满足匹配
条件时,对该消费者执行后面的过滤规则。
=> 之后为提供者地址列表的过滤条件,所有参数和提供者的 URL 进行对比,消费者最
终只拿到过滤后的地址列表。
如果匹配条件为空,表示对所有消费方应用,如: => host != 10.20.153.11
如果过滤条件为空,表示禁止访问,如: host = 10.20.153.10 =>
表达式:
参数支持:
服务调用信息,如:method, argument 等,暂不支持参数路由
URL 本身的字段,如:protocol, host, port 等
以及 URL 上的所有参数,如:application, organization 等
条件支持:
等号 = 表示"匹配",如: host = 10.20.153.10
不等号 != 表示"不匹配",如: host != 10.20.153.10
值支持:
以逗号 , 分隔多个值,如: host != 10.20.153.10,10.20.153.11
以星号 * 结尾,表示通配,如: host != 10.20.*
以美元符 $ 开头,表示引用消费者参数,如: host = $host
白名单 :
host != 10.20.153.10,10.20.153.11 =>
黑名单:
host = 10.20.153.10,10.20.153.11 =>
参考
Dubbo整合SpringBoot
Dubbo在开发中的一些常用配置
最后
以上就是善良飞鸟为你收集整理的Dubbo 配置总结的全部内容,希望文章能够帮你解决Dubbo 配置总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复