Dubbo学习笔记4-常用功能

1. 启动时检查

是消费方配置的属性,在dubbo:reference标签中的属性,如下标示

<dubbo:reference id="xmlService" interface="org.dubbo.api.UserService" check="true"/>

使用check作为标示,如果为true,那么在加载xml的初始化bean的时候,服务不可用,则会抛出错误。
另外的使用方法还有:
1)关闭所有服务的启动时检查 (没有提供者时报错):

<dubbo:consumer check="false" />

2)关闭注册中心启动时检查 (注册订阅失败时报错):

<dubbo:registry check="false" />

2. 性能优化

2.1 集群容错

在集群调用失败时,Dubbo 提供了多种容错方案,缺省为 failover 重试。
使用属性cluster=" failover"来表示。可以在如下标签使用:

<dubbo:reference>
<dubbo:service>
<dubbo:method>

Failover模式

失败自动切换,当出现失败,重试其它服务器 。通常用于读操作,但重试会带来更长延迟。
可通过 retries=“2” 来设置重试次数(不含第一次)。
使用属性retries,可以在如下标签使用:

<dubbo:service retries="2" />

<dubbo:reference retries="2" />

<dubbo:reference>
<dubbo:method name="findFoo" retries="2" />
</dubbo:reference>

FailFast模式

快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。

Failsafe模式

失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。

Failback模式

失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。

Forking模式

并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks=“2” 来设置最大并行数。
使用属性forks,可以在如下标签使用:

<dubbo:service forks="2" />

<dubbo:reference forks="2" />

<dubbo:reference>
<dubbo:method name="findFoo" forks="2" />
</dubbo:reference>

Broadcast模式

广播调用所有提供者,逐个调用,任意一台报错则报错 。通常用于通知所有提供者更新缓存或日志等本地资源信息。

2.2 负载均衡

使用属性loadbalance,可以在如下标签使用:

<dubbo:service loadbalance="random" />

<dubbo:reference loadbalance="random" />

<dubbo:reference>
<dubbo:method name="findFoo" loadbalance="random" />
</dubbo:reference>

Random LoadBalance

随机,按权重设置随机概率。
在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较
均匀,有利于动态调整提供者权重。
实现原理
1) 累积所有权重,同时判断所有权重是否相等
2) 如果所有权重又不相等的,则使用累积权重产生随机数,然后从第一台开始慢慢减去权重,如果减到随机数小于0,则代表选择此服务。
3) 如果所有权重相等,则按照服务数量产生随机数。

RoundRobin LoadBalance

轮循,按公约后的权重设置轮循比率。
存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台
时就卡在那,久而久之,所有请求都卡在调到第二台上。
实现原理
1) 使用ConcurrentMap保存服务名与一个自增的AtomicPositiveInteger整数对应关系
2) 循环所有服务器,获取最大权重和最小权重
3) 如果最大与最小权重不相等,获取自增整数,表示当前权重,用当前权重与总权重取模,取模结果用于所有服务器轮询,如果取模结果等于0且服务器的权重还大于0,那么获取此服务器,如果取模结果不等于0且服务器权重还大于0,则取模结果减一,服务器权重减一,继续循环直到获取服务器。
4) 如果权重都相等,则按照自增数与服务器数量取模,返回取模结果服务器。

LeastActive LoadBalance

最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。
使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
1) 循环所有服务器,记录最小连接服务器,可能存在多个一模一样的。
2) 如果最小连接服务器只有一个,那么直接返回
3) 如果最小连接服务器不止一个,而且权重不一样,按照Random LoadBalance算法给予随机分配。
4) 如果最小连接服务器不止一个,而且权重一样,按照服务器随机分配。

ConsistentHash LoadBalance

1) 一致性 Hash,相同参数的请求总是发到同一提供者。
2) 当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
3) 算法参见:http://en.wikipedia.org/wiki/Consistent_hashing
4) 缺省只对第一个参数 Hash,如果要修改,请配置 <dubbo:parameterkey=“hash.arguments” value=“0,1” />
5) 缺省用 160 份虚拟节点,如果要修改,请配置 <dubbo:parameter key=“hash.nodes”
value=“320” />

2.3 线程模式

线程模式说明

1) 如果事件处理的逻辑能迅速完成,并且不会发起新的 IO 请求,比如只是在内存中记个标识,则直接在 IO 线程上处理更快,因为减少了线程池调度。
2) 但如果事件处理逻辑较慢,或者需要发起新的 IO 请求,比如需要查询数据库,则必须派发到线程池,否则 IO 线程阻塞,将导致不能接收其它请求。
3) 如果用 IO 线程处理事件,又在事件处理过程中发起新的 IO 请求,比如在连接事件中发起登录请求,会报“可能引发死锁”异常,但不会真死锁。

配置

主要修饰在dubbo:protocol标签下面
dispatcher
1) all 所有消息都派发到线程池,包括请求,响应,连接事件,断开事件,心跳等。
2) direct 所有消息都不派发到线程池,全部在 IO 线程上直接执行。message 只有请求响应消息派发到线程池,其它连接断开事件,心跳等消息,直接在 IO线程上执行。
3) execution 只请求消息派发到线程池,不含响应,响应和其它连接断开事件,心跳等消息,直接在 IO 线程上执行。
4) connection 在 IO 线程上,将连接断开事件放入队列,有序逐个执行,其它消息派发到线程池。
threadPool
1) fixed 固定大小线程池,启动时建立线程,不关闭,一直持有。(缺省)
2) cached 缓存线程池,空闲一分钟自动删除,需要时重建。
3) limited 可伸缩线程池,但池中的线程数只会增长不会收缩。只增长不收缩的目的是为了避免收缩时突然来了大流量引起的性能问题。
threads
线程数,只有在fixed才有效。默认100.
iothreads
io线程池大小(固定大小),默认cpu个数+1。

2.4 连接控制

connections

修饰< dubbo:service>、dubbo:reference。表示每个客户端最多的连接数,默认是0,表示单个长连接。以dubbo协议为例,一般连接是共享的,默认是单个连接,当请求量过大时,可以说适当调高。连接数超过此值,会抛出异常。

accepts

修饰< dubbo:protocol>。表示服务器允许的连接数,默认0,表示无限。连接数超过此值,会抛出异常。

2.5 并发控制

executes

修饰dubbo:reference。每个客户端每个服务每个方法的最大并发调用数。如果同时请求数量超过此值,会抛出异常。

actives

修饰dubbo:service。服务提供者每服务每方法最大可并行执行请求数。如果客户端请求数超过此数量,则会等待,等到有资源才能调用。

2.6 优化方案

在这里插入图片描述
1) 当consumer发起一个请求时,首先经过active limit(参数actives)进行方法级别的限制,其实现方式为CHM中存放计数器(AtomicInteger),请求时加1,请求完成(包括异常)减1,如果超过actives则等待有其他请求完成后重试或者超时后失败。
2) 从多个连接(connections)中选择一个连接发送数据,对于默认的netty实现来说,由于可以复用连接,默认一个连接就可以。不过如果你在压测,且只有一个consumer,一个provider,此时适当的加大connections确实能够增强网络传输能力。但线上业务由于有多个consumer多个provider,因此不建议增加connections参数。
3) 连接到达provider时(如dubbo的初次连接),首先会判断总连接数是否超限(acceps),超过限制连接将被拒绝。
4) 连接成功后,具体的请求交给io thread处理。io threads虽然是处理数据的读写,但io部分为异步,更多的消耗的是cpu,因此iothreads默认cpu个数+1是比较合理的设置,不建议调整此参数。
5) 数据读取并反序列化以后,交给业务线程池处理,默认情况下线程池为fixed,且排队队列为0(queues),这种情况下,最大并发等于业务线程池大小(threads),如果希望有请求的堆积能力,可以调整queues参数。如果希望快速失败由其他节点处理(官方推荐方式),则不修改queues,只调整threads。
6) execute limit(参数executes)是方法级别的并发限制,原理与actives类似,只是少了等待的过程,即受限后立即失败。
7) tps,控制指定时间内(默认60s)的请求数。注意目前dubbo默认没有支持该参数,需要加一个META-INF/dubbo/com.alibaba.dubbo.rpc.Filter文件,文件内容为:
tps=com.alibaba.dubbo.rpc.filter.TpsLimitFilter
从上面的分析,可以看出如果consumer数actives>provider数threads且queues=0,则会存在部分请求无法申请到资源,重试也有很大几率失败。 当需要对一个接口的不同方法进行不同的并发控制时使用executes,否则调整threads就可以。

3. 服务提供

3.1 多协议

使用属性protocol来配置接口的协议。
1)服务提供方定义协议,使用如下配置:

<dubbo:protocol name="dubbo" port="20880" />
    <dubbo:protocol name="rmi" port="1099" />
    <dubbo:protocol name="hessian" port="8080" />

2)服务提供方配置接口的协议,使用如下配置:

<!-- 支持hessian,dubbo协议 -->
    <dubbo:service interface="org.dubbo.api.HessianProtocol" ref="hessianProtocolService" protocol="hessian,dubbo"/>
    <!-- 支持dubbo协议 -->
    <dubbo:service interface="org.dubbo.api.DubboProtocol" ref="dubboProtocolService" protocol="dubbo"/>
    <!-- 支持rmi协议 -->
    <dubbo:service interface="org.dubbo.api.RmiProtocol" ref="rmiProtocolService" protocol="rmi"/>

3)服务消费方可以配置协议,配置如下:

<dubbo:reference id="hessianService" interface="org.dubbo.api.HessianProtocol" protocol="dubbo"/>
    <dubbo:reference id="dubboService" interface="org.dubbo.api.DubboProtocol" protocol="dubbo"/>
    <dubbo:reference id="rmiService" interface="org.dubbo.api.RmiProtocol"/>

说明:1)协议可以支持多个,使用逗号隔开
2)服务消费方使用协议,只能使用服务提供方里面定义的
3)hessian需要引入4.0.7的包
4)协议详细信息参照协议章节

3.2 多注册中心

1) 注册中心的标签是dubbo:registry,其中的id属性,用于标示注册中心。如下代码:

<dubbo:registry id="chinaRegistry" address="zookeeper://localhost:2181"/>
<dubbo:registry id="USRegistry" address="zookeeper://localhost:2182"/>

2) 服务提供者接口服务dubbo:service暴露时,使用registry属性对应注册中心

<dubbo:service interface="org.dubbo.api.DemoService" ref="demoService" protocol="dubbo" registry="chinaRegistry"/>
<dubbo:service interface="org.dubbo.api.DemoService" ref="demoService" protocol="dubbo" registry="USRegistry"/>

3) 服务消费者dubbo:reference引用服务,使用registry属性对应注册中心

<dubbo:reference id="chinaService" interface="org.dubbo.api.DemoService" registry="chinaRegistry"/>
<dubbo:reference id="USService" interface="org.dubbo.api.DemoService" registry="USRegistry"/>

3.3 服务分组

属性

当一个接口有多种实现时,可以用 group 区分。
group属性,value可以随意定义。主要修饰dubbo:servicedubbo:reference标签。当服务提供者设置此属性,服务消费者也必须设置,也可以使用*代替所有分组。

服务提供者

属性group修饰dubbo:service,代码如下:

<dubbo:service interface="org.dubbo.api.UserService" ref="demoService" protocol="dubbo" group="user"/>
    <dubbo:service interface="org.dubbo.api.ChargeService" ref="demoService" protocol="dubbo" group="charge"/>

服务消费者

属性group修饰dubbo:reference,代码如下:

<dubbo:reference id="userService" interface="org.dubbo.api.UserService" group="user"/>
    <dubbo:reference id="chargeService" interface="org.dubbo.api.ChargeService" group="charge"/>

3.4 多版本

属性

属性version,value以X.XX.XX这样的形式。也可以使用代替所有版本,如果有多版本,但是又使用,那么会随机调用其中一个。
当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引
用。可以按照以下的步骤进行版本迁移:

  1. 在低压力时间段,先升级一半提供者为新版本
  2. 再将所有消费者升级为新版本
  3. 然后将剩下的一半提供者升级为新版本

服务提供者

属性version修饰dubbo:service,代码如下:

<dubbo:service interface="org.dubbo.api.DemoService" ref="demoService" protocol="dubbo" version="1.0.0"/>

服务消费者

属性version修饰dubbo:reference,代码如下:

<dubbo:reference id="demoService" interface="org.dubbo.api.DemoService" version="1.0.0"/>

3.5 分组聚合

按组合并返回结果 ,比如菜单服务,接口一样,但有多种实现,用group区分,现在消费方需从每种group中调用一次返回结果,合并结果返回,这样就可以实现聚合菜单项。

属性

属性merger,可以修饰dubbo:referencedubbo:method标签。Value可以是true、一个合并器或者一个合并方法。与group一起使用。

指定合并器

服务提供方:

<dubbo:service interface="org.dubbo.api.MergerService" ref="mergerServiceOne" protocol="dubbo" group="old" />
<dubbo:service interface="org.dubbo.api.MergerService" ref="mergerServiceTwo" protocol="dubbo" group="new" />

服务消费方:
group属性使用*代表所有分组,也可以使用逗号分隔。

<dubbo:reference id="mergerService" interface="org.dubbo.api.MergerService" group="*" merger="myMerger"/>

合并器:
1) 在服务消费方的目录resources/META-INFO/dubbo/com.alibaba.dubbo.rpc.cluster.Merger建立这个文件,文件内容如下:

 myMerger=org.dubbo.consumer.MyMerger

2) 建立org.dubbo.consumer.MyMerger的java类,实现Merger接口,实现merge方法,代码如下:

package org.dubbo.consumer;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.dubbo.rpc.cluster.Merger;
public class MyMerger implements Merger<List<String>> {
	@Override
	public List<String> merge(List<String>... arg0) {
		ArrayList<String> result = new ArrayList<>();
		for (List<String> stringList : arg0) {
			for (String string : stringList) {
				// 只有大于2的数据才会被返回
				if (string != null && string.compareTo("2") >= 0)
					result.add(string);
			}
		}
		return result;
	}
}

指定合并方法

merger属性为.方法名称();这样的模式。

猜你喜欢

转载自blog.csdn.net/linwu_2006_2006/article/details/95195904