基于dubbo的分布式项目实例应用
本文主要学习dubbo服务的启动检查、集群容错、服务均衡、线程模型、直连提供者、只定阅、只注册等知识点,希望通过实例演示进一步理解和掌握这些知识点。
启动检查
Dubbo缺省会在启动消费者时检查依赖的服务是否可用,不可用时会抛出异常,阻止Spring初始化完成,以便上线时,能及早发现问题,默认check=true。
关闭没有提供者时报错
<dubbo:reference interface="com.mcweb.api.service.IUserService" id="userService" check="false" />
<!--或者采用全局配置-->
<dubbo:consumer check="false" />
check="false"表示不启动服务提供者mcweb-logic,mcweb-web照样能正常启动。
关闭注册订阅失败时报错
<dubbo:registry check="false" />
check="false"表示,注册中心未启动,mcweb-web照样能正常启动。
集群容错
基本概念
首先,我们要明确dubbo是怎么做集群的。dubbo的集群即同一个服务部署多台机或者同一台机不同端口注册到注册中心,消费者就通过访问规则访问集群内的不同节点的服务。
集群只需多个相同服务注册相同的注册中心。在每个服务提供者配置相同集群策略和访问策略,对于消费者是透明,消费者通过框架决定访问那个服务提供者。消费者通过注册中心提供的服务端的协议信息,决定访问哪个服务。
dubbo的集群容错即当服务消费者调用服务提供者集群中的服务失败时,Dubbo提供了多种容错方案,缺省为failover重试(失败自动切换,当出现失败,重试其它服务器)。Dubbo提供的集群容错模式有:
Failover Cluster(失败自动切换,当出现失败,重试其它服务器。(缺省))
Failfast Cluster(快速失败,只发起一次调用,失败立即报错。)
Failsafe Cluster(失败安全,出现异常时,直接忽略。)
Failback Cluster(失败自动恢复,后台记录失败请求,定时重发。)
Forking Cluster(并行调用多个服务器,只要一个成功即返回。)
Broadcast Cluster(广播调用所有提供者,逐个调用,任意一台报错则报错。)
详细解析参考
http://dubbo.io/User+Guide-zh.htm#UserGuide-zh-%E9%9B%86%E7%BE%A4%E5%AE%B9%E9%94%99
集群模式配置
在《基于dubbo构建分布式项目与服务模块》一文中,我们创建了服务消费者mcweb-web与服务提供者mcweb-logic,现在我们再增加两个服务提供者,和mcweb-logic属于同一个服务,便于区分,将模块名称命名为mcweb-logic-a,mcweb-logic-b。现在mcweb-logic,mcweb-logic-a,mcweb-logic-b都属于同一个服务,均提供IUserService服务。需要将三个相同的服务以不同的端口注册到zookeeper中并指定集群容错模式,集群容错模式也可以在服务提供者配置。
<!--\mcweb\mcweb-web\src\main\resources\spring\dubbo-consumer.xml-->
<dubbo:reference
interface="com.mcweb.api.service.IUserService"
id="userService"
check="false"
protocol="dubbo"
cluster="failover"
retries="2"
/>
<!---或者->
<!--\mcweb\mcweb-logic\src\main\resources\spring\dubbo-provider.xml-->
<dubbo:protocol name="dubbo" port="20880" />
<dubbo:service
interface="com.mcweb.api.service.IUserService"
ref="userService"
protocol="dubbo"
cluster="failover"
retries="2"
/>
运行服务集群
在《基于dubbo构建分布式项目与服务模块》一文中,我们采用了tomcat容器来运行服务消费者和服务提供者,现在我们采用main方法来运行,这种方式通常在测试环境中使用。
<!--\mcweb\mcweb-logic\src\main\java\com\mcweb\logic\test\DubboServiceStart.java-->
public class DubboServiceStart { public static void main(String[] args) throws IOException { ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:spring/applicationContext.xml"); System.out.println("mcweb-logic 服务已启动..."); //为保证服务一直开着,利用输入流的阻塞来模拟 System.in.read(); } }
分别以这种方式运行另外两个服务。在dubbo管控台可以看到:
集群容错测试
服务集群对消费者来说是透明的,消费者只需注明注册中心和需要的服务即可,无需关注服务是单节点还是集群。因为在同一台机子上测试,将消费者的端口改为20883
<!--\mcweb\mcweb-web\src\main\resources\spring\dubbo-consumer.xml-->
<dubbo:protocol name="dubbo" port="20883"/>
现在我们来运行消费者。
<!--\mcweb\mcweb-web\src\main\java\com\mcweb\web\test\DubboConsumerStart.java-->
public class DubboConsumerStart { public static void main(String[] args) throws Exception { ApplicationContext ctx=new ClassPathXmlApplicationContext("classpath*:spring/applicationContext.xml"); System.out.println("mcweb-web 消费者已启动..."); for(int i=0; i<1000;i++) { IUserService userService=(IUserService) ctx.getBean("userService"); User user = userService.query("testid"); System.out.println(i + ":" + user.getUserName()); Thread.sleep(1000); } } }
运行结果:
...
10:Get user from mcweb-logic-b
11:Get user from mcweb-logic
12:Get user from mcweb-logic-a
13:Get user from mcweb-logic-b
14:Get user from mcweb-logic-a
15:Get user from mcweb-logic
16:Get user from mcweb-logic
17:Get user from mcweb-logic
18:Get user from mcweb-logic-b
19:Get user from mcweb-logic
20:Get user from mcweb-logic
...
可见,默认情况下,dubbo服务集群是带有负载均衡的(默认为random算法随机调用),集群中的服务被均匀的调用,在cluster="failover"模式下,当调用失败时,会尝试调用其他服务器。可以在dubbo管控台中对服务进行“倍权”,“半权”,“禁用”等操作观察消费者的调用情况。
负载均衡
基本概念
负载均衡(Load Balance)是分布式系统架构设计中必须考虑的因素之一,它通常指将请求/数据均匀分摊到多个操作单元(集群服务节点)上执行,负载均衡的关键在于均匀。dubbo在服务集群负载均衡时,提供了多种均衡策略,缺省为random随机调用。
Random LoadBalance随机,按权重设置随机概率。
RoundRobin LoadBalance 轮循,按公约后的权重设置轮循比率。
LeastActive LoadBalance 最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。
ConsistentHash LoadBalance 一致性Hash,相同参数的请求总是发到同一提供者。
详细参考
http://dubbo.io/User+Guide-zh.htm#UserGuide-zh-%E9%9B%86%E7%BE%A4%E5%AE%B9%E9%94%99
负载均衡配置
同理,负载均衡可以在消费者或者提供者中配置。
<!--\mcweb\mcweb-web\src\main\resources\spring\dubbo-consumer.xml-->
<dubbo:reference
interface="com.mcweb.api.service.IUserService"
id="userService"
check="false"
protocol="dubbo"
cluster="failover"
retries="2"
loadbalance="random"
/>
我们采用random随机均衡算法,在dubbo管控台中设置mcweb-logic(192.168.2.1:20880),mcweb-logic-a(192.168.2.1:20881),mcweb-logic-b(192.168.2.1:20882)的权重分别为200,100,50。
看看mcweb-web的调用情况:
0:Get user from mcweb-logic-b
1:Get user from mcweb-logic-a
2:Get user from mcweb-logic-a
3:Get user from mcweb-logic-b
4:Get user from mcweb-logic-a
5:Get user from mcweb-logic
6:Get user from mcweb-logic
7:Get user from mcweb-logic-b
8:Get user from mcweb-logic-a
9:Get user from mcweb-logic-b
10:Get user from mcweb-logic
11:Get user from mcweb-logic-a
12:Get user from mcweb-logic-a
13:Get user from mcweb-logic-a
14:Get user from mcweb-logic
15:Get user from mcweb-logic
16:Get user from mcweb-logic
17:Get user from mcweb-logic
18:Get user from mcweb-logic-b
19:Get user from mcweb-logic-b
20:Get user from mcweb-logic
21次调用中,mcweb-logic调用了8次,mcweb-logic-a调用了7次,mcweb-logic-b调用了6次,符合权重越大,调用次数越多。随着调用次数的增多,每个节点上的服务被调用的次数会逐渐趋向设置的权重所占的比率。
线程模型
基本概念
dubbo的线程模型关注的是“请求/响应”是直接在IO线程上执行还是分发到线程池上由线程中的线程去执行具体的服务。说明如下:
l 如果事件处理的逻辑能迅速完成,并且不会发起新的IO请求,则直接在IO线程上处理更快,因为减少了线程池调度。
l 如果事件处理逻辑较慢,或者需要发起新的IO请求,则必须派发到线程池,否则IO线程阻塞,将导致不能接收其它请求。
线程模型配置
线程模型通常在服务提供者的<dubbo:protocol>标签中配置
<dubbo:protocol
name="dubbo"
port="20880"
dispatcher="all"
threadpool="fixed"
threads="100"
/>
dispatcher分发类型取值有:
l all 所有消息都派发到线程池。
l direct 所有消息都不派发到线程池,全部在IO线程上直接执行。
l message 只有请求响应消息派发到线程池。
l execution 只请求消息派发到线程池。
l connection 在IO线程上,将连接断开事件放入队列,有序逐个执行。
threadpool的取值有:
l fixed 固定大小线程池,启动时建立线程,不关闭,一直持有。(缺省)
l cached 缓存线程池,空闲一分钟自动删除,需要时重建。
l limited 可伸缩线程池,但池中的线程数只会增长不会收缩。
详情参考
http://dubbo.io/User+Guide-zh.htm#UserGuide-zh-%E7%BA%BF%E7%A8%8B%E6%A8%A1%E5%9E%8B
直连提供者
直连提供者,从字面上就知道,消费者绕过注册中心直接连接服务提供者,这通常在开发测试中为了方便使用。
修改\mcweb\mcweb-web\src\main\resources\spring\dubbo-consumer.xml
<!-- 注册中心地址 -->
<!-- <dubbo:registry protocol="zookeeper" address="192.168.2.129:2181" check="true"/> -->
<dubbo:reference
interface="com.mcweb.api.service.IUserService"
id="userService"
check="false"
protocol="dubbo"
cluster="failover"
retries="2"
loadbalance="random"
url="dubbo://127.0.0.1:20880"
/>
在<dubbo:reference>中配置url指向提供者,将绕过注册中心,多个地址用分号隔开。注释掉mcweb-web的直 <dubbo:registry ...> 直连接本地的mcweb-logic。看看日志
[DUBBO] Successed connect to server /192.168.2.1:20880 from NettyClient 192.168.2.1 using dubbo version 2.5.3, channel is
[DUBBO] Start NettyClient DESKTOP-HBGAL7B/192.168.2.1 connect to the server /192.168.2.1:20880,
[DUBBO] Refer dubbo service com.mcweb.api.service.IUserService from url dubbo://127.0.0.1:20880/com.mcweb.api.service.IUserService?
mcweb-web 消费者已启动...
0:Get user from mcweb-logic
1:Get user from mcweb-logic
2:Get user from mcweb-logic
只订阅
只订阅是指正在开发中的服务提供者,只订阅自己依赖的服务,而不注册到注册中心,通过直连自己依赖的服务来测试正在开发中的服务。这样可以避免开发未完成的提供者注册到注册中心后影响其它消费者的运行的问题。
禁用注册配置
<dubbo:registry protocol="zookeeper" address="192.168.2.129:2181" register="false"/>
只注册
只注册是指在两个注册中心中,服务提供者只向其中一个注册中心注册服务而不订阅服务。这个又是为了解决什么问题呢,先看下图
服务A和服务B都注册到了两个注册中心中,且都依赖于服务C(需要服务C都在两个注册中心注册),
服务C又依赖服务D (服务C只需要订阅zookeeper2的的D服务,服务D注册到zookeeper2而不从zookeeper2订阅服务)。
禁用订阅配置
<dubbo:registry protocol="zookeeper" address="192.168.2.129:2181" subscribe="false"/>
本文到此,后面我们继续学习多注册中心、服务分组等内容。