Dubbo是Alibaba开源的分布式服务框架,咱们能够很是容易地经过Dubbo来构建分布式服务,并根据本身实际业务应用场景来选择合适的集群容错模式,这个对于不少应用都是迫切但愿的,只须要经过简单的配置就可以实现分布式服务调用,也就是说服务提供方(Provider)发布的服务能够自然就是集群服务,好比,在实时性要求很高的应用场景下,可能但愿来自消费方(Consumer)的调用响应时间最短,只须要选择Dubbo的Forking Cluster模式配置,就能够对一个调用请求并行发送到多台对等的提供方(Provider)服务所在的节点上,只选择最快一个返回响应的,而后将调用结果返回给服务消费方(Consumer),显然这种方式是以冗余服务为基础的,须要消耗更多的资源,可是可以知足高实时应用的需求。
有关Dubbo服务框架的简单使用,能够参考个人其余两篇文章(《基于Dubbo的Hessian协议实现远程调用》,《Dubbo实现RPC调用使用入门》,后面参考连接中已给出连接),这里主要围绕Dubbo分布式服务相关配置的使用来讲明与实践。java
Dubbo服务集群容错git
假设咱们使用的是单机模式的Dubbo服务,若是在服务提供方(Provider)发布服务之后,服务消费方(Consumer)发出一次调用请求,刚好此次因为网络问题调用失败,那么咱们能够配置服务消费方重试策略,可能消费方第二次重试调用是成功的(重试策略只须要配置便可,重试过程是透明的);可是,若是服务提供方发布服务所在的节点发生故障,那么消费方再怎么重试调用都是失败的,因此咱们须要采用集群容错模式,这样若是单个服务节点因故障没法提供服务,还能够根据配置的集群容错模式,调用其余可用的服务节点,这就提升了服务的可用性。
首先,根据Dubbo文档,咱们引用文档提供的一个架构图以及各组件关系说明,以下所示:
上述各个组件之间的关系(引自Dubbo文档)说明以下:github
- 这里的Invoker是Provider的一个可调用Service的抽象,Invoker封装了Provider地址及Service接口信息。
- Directory表明多个Invoker,能够把它当作List,但与List不一样的是,它的值多是动态变化的,好比注册中心推送变动。
- Cluster将Directory中的多个Invoker假装成一个Invoker,对上层透明,假装过程包含了容错逻辑,调用失败后,重试另外一个。
- Router负责从多个Invoker中按路由规则选出子集,好比读写分离,应用隔离等。
- LoadBalance负责从多个Invoker中选出具体的一个用于本次调用,选的过程包含了负载均衡算法,调用失败后,须要重选。
咱们也简单说明目前Dubbo支持的集群容错模式,每种模式适应特定的应用场景,能够根据实际须要进行选择。Dubbo内置支持以下6种集群模式:web
配置值为failover。这种模式是Dubbo集群容错默认的模式选择,调用失败时,会自动切换,从新尝试调用其余节点上可用的服务。对于一些幂等性操做可使用该模式,如读操做,由于每次调用的反作用是相同的,因此能够选择自动切换并重试调用,对调用者彻底透明。能够看到,若是重试调用必然会带来响应端的延迟,若是出现大量的重试调用,可能说明咱们的服务提供方发布的服务有问题,如网络延迟严重、硬件设备须要升级、程序算法很是耗时,等等,这就须要仔细检测排查了。
例如,能够这样显式指定Failover模式,或者不配置则默认开启Failover模式,配置示例以下:redis
1 |
< dubbo:service interface = "org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService" version = "1.0.0" |
2 |
cluster = "failover" retries = "2" timeout = "100" ref = "chatRoomOnlineUserCounterService" protocol = "dubbo" > |
3 |
< dubbo:method name = "queryRoomUserCount" timeout = "80" retries = "2" /> |
上述配置使用Failover Cluster模式,若是调用失败一次,能够再次重试2次调用,服务级别调用超时时间为100ms,调用方法queryRoomUserCount的超时时间为80ms,容许重试2次,最坏状况调用花费时间160ms。若是该服务接口org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService还有其余的方法可供调用,则其余方法没有显式配置则会继承使用dubbo:service配置的属性值。算法
配置值为failfast。这种模式称为快速失败模式,调用只执行一次,失败则当即报错。这种模式适用于非幂等性操做,每次调用的反作用是不一样的,如写操做,好比交易系统咱们要下订单,若是一次失败就应该让它失败,一般由服务消费方控制是否从新发起下订单操做请求(另外一个新的订单)。spring
配置值为failsafe。失败安全模式,若是调用失败, 则直接忽略失败的调用,而是要记录下失败的调用到日志文件,以便后续审计。apache
配置值为failback。失败自动恢复,后台记录失败请求,定时重发。一般用于消息通知操做。json
配置值为forking。并行调用多个服务器,只要一个成功即返回。一般用于实时性要求较高的读操做,但须要浪费更多服务资源。api
配置值为broadcast。广播调用全部提供者,逐个调用,任意一台报错则报错(2.1.0开始支持)。一般用于通知全部提供者更新缓存或日志等本地资源信息。
上面的6种模式均可以应用于生产环境,咱们能够根据实际应用场景选择合适的集群容错模式。若是咱们以为Dubbo内置提供的几种集群容错模式都不能知足应用须要,也能够定制实现本身的集群容错模式,由于Dubbo框架给我提供的扩展的接口,只须要实现接口com.alibaba.dubbo.rpc.cluster.Cluster便可,接口定义以下所示:
01 |
@SPI (FailoverCluster.NAME) |
02 |
public interface Cluster { |
05 |
* Merge the directory invokers to a virtual invoker. |
12 |
<T> Invoker<T> join(Directory<T> directory) throws RpcException; |
关于如何实现一个自定义的集群容错模式,能够参考Dubbo源码中内置支持的汲取你容错模式的实现,6种模式对应的实现类以下所示:
1 |
com.alibaba.dubbo.rpc.cluster.support.FailoverCluster |
2 |
com.alibaba.dubbo.rpc.cluster.support.FailfastCluster |
3 |
com.alibaba.dubbo.rpc.cluster.support.FailsafeCluster |
4 |
com.alibaba.dubbo.rpc.cluster.support.FailbackCluster |
5 |
com.alibaba.dubbo.rpc.cluster.support.ForkingCluster |
6 |
com.alibaba.dubbo.rpc.cluster.support.AvailableCluster |
可能咱们初次接触Dubbo时,不知道如何在实际开发过程当中使用Dubbo的集群模式,后面咱们会以Failover Cluster模式为例开发咱们的分布式应用,再进行详细的介绍。
Dubbo服务负载均衡
Dubbo框架内置提供负载均衡的功能以及扩展接口,咱们能够透明地扩展一个服务或服务集群,根据须要很是容易地增长/移除节点,提升服务的可伸缩性。Dubbo框架内置提供了4种负载均衡策略,以下所示:
- Random LoadBalance:随机策略,配置值为random。能够设置权重,有利于充分利用服务器的资源,高配的能够设置权重大一些,低配的能够稍微小一些
- RoundRobin LoadBalance:轮询策略,配置值为roundrobin。
- LeastActive LoadBalance:配置值为leastactive。根据请求调用的次数计数,处理请求更慢的节点会受到更少的请求
- ConsistentHash LoadBalance:一致性Hash策略,具体配置方法能够参考Dubbo文档。相同调用参数的请求会发送到同一个服务提供方节点上,若是某个节点发生故障没法提供服务,则会基于一致性Hash算法映射到虚拟节点上(其余服务提供方)
在实际使用中,只须要选择合适的负载均衡策略值,配置便可,下面是上述四种负载均衡策略配置的示例:
1 |
< dubbo:service interface = "org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService" version = "1.0.0" |
2 |
cluster = "failover" retries = "2" timeout = "100" loadbalance = "random" |
3 |
ref = "chatRoomOnlineUserCounterService" protocol = "dubbo" > |
4 |
< dubbo:method name = "queryRoomUserCount" timeout = "80" retries = "2" loadbalance = "leastactive" /> |
上述配置,也体现了Dubbo配置的继承性特色,也就是dubbo:service元素配置了loadbalance=”random”,则该元素的子元素dubbo:method若是没有指定负载均衡策略,则默认为loadbalance=”random”,不然若是dubbo:method指定了loadbalance=”leastactive”,则使用子元素配置的负载均衡策略覆盖了父元素指定的策略(这里调用queryRoomUserCount方法使用leastactive负载均衡策略)。
固然,Dubbo框架也提供了实现自定义负载均衡策略的接口,能够实现
核心技术:Maven,Springmvc mybatis shiro, Druid, Restful, Dubbo, ZooKeeper,Redis,FastDFS,ActiveMQ,Nginx
1. 项目核心代码结构截图

项目模块依赖
特别提醒:开发人员在开发的时候能够将本身的业务REST服务化或者Dubbo服务化
2. 项目依赖介绍
2.1 后台管理系统、Rest服务系统、Scheculer定时调度系统依赖以下图:

2.2 Dubbo独立服务项目依赖以下图:

3. 项目功能部分截图:







zookeeper、dubbo服务启动


dubbo管控台







REST服务平台



com.alibaba.dubbo.rpc.cluster.LoadBalance接口,接口定义以下所示:
02 |
* LoadBalance. (SPI, Singleton, ThreadSafe) |
06 |
* @see com.alibaba.dubbo.rpc.cluster.Cluster#join(Directory) |
08 |
* @author william.liangf |
10 |
@SPI (RandomLoadBalance.NAME) |
11 |
public interface LoadBalance { |
14 |
* select one invoker in list. |
15 |
* @param invokers invokers. |
16 |
* @param url refer url |
17 |
* @param invocation invocation. |
18 |
* @return selected invoker. |
20 |
@Adaptive ( "loadbalance" ) |
21 |
<T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException; |
如何实现一个自定义负载均衡策略,能够参考Dubbo框架内置的实现,以下所示的3个实现类:
1 |
com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance |
2 |
com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance |
3 |
com.alibaba.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance |
Dubbo服务集群容错实践
手机应用是以聊天室为基础的,咱们须要收集用户的操做行为,而后计算聊天室中在线人数,并实时在手机应用端显示人数,整个系统的架构如图所示:
上图中,主要包括了两大主要流程:日志收集并实时处理流程、调用读取实时计算结果流程,咱们使用基于Dubbo框架开发的服务来提供实时计算结果读取聊天人数的功能。上图中,实际上业务接口服务器集群也能够基于Dubbo框架构建服务,就看咱们想要构建什么样的系统来知足咱们的须要。
若是不使用注册中心,服务消费方也可以直接调用服务提供方发布的服务,这样须要服务提供方将服务地址暴露给服务消费方,并且也没法使用监控中心的功能,这种方式成为直连。
若是咱们使用注册中心,服务提供方将服务发布到注册中心,而服务消费方能够经过注册中心订阅服务,接收服务提供方服务变动通知,这种方式能够隐藏服务提供方的细节,包括服务器地址等敏感信息,而服务消费方只能经过注册中心来获取到已注册的提供方服务,而不能直接跨过注册中心与服务提供方直接链接。这种方式的好处是还可使用监控中心服务,可以对服务的调用状况进行监控分析,还能使用Dubbo服务管理中心,方便管理服务,咱们在这里使用的是这种方式,也推荐使用这种方式。使用注册中心的Dubbo分布式服务相关组件结构,以下图所示:
下面,开发部署咱们的应用,经过以下4个步骤来完成:
服务接口将服务提供方(Provider)和服务消费方(Consumer)链接起来,服务提供方实现接口中定义的服务,即给出服务的实现,而服务消费方负责调用服务。咱们接口中给出了2个方法,一个是实时查询获取当前聊天室内人数,另外一个是查询一天中某个/某些聊天室中在线人数峰值,接口定义以下所示:
01 |
package org.shirdrn.dubbo.api; |
03 |
import java.util.List; |
05 |
public interface ChatRoomOnlineUserCounterService { |
07 |
String queryRoomUserCount(String rooms); |
09 |
List<String> getMaxOnlineUserCount(List<String> rooms, String date, String dateFormat); |
接口是服务提供方和服务消费方公共遵照的协议,通常状况下是服务提供方将接口定义好后提供给服务消费方。
服务提供方实现接口中定义的服务,其实现和普通的服务没什么区别,咱们的实现类为ChatRoomOnlineUserCounterServiceImpl,代码以下所示:
01 |
package org.shirdrn.dubbo.provider.service; |
03 |
import java.util.List; |
05 |
import org.apache.commons.logging.Log; |
06 |
import org.apache.commons.logging.LogFactory; |
07 |
import org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService; |
08 |
import org.shirdrn.dubbo.common.utils.DateTimeUtils; |
10 |
import redis.clients.jedis.Jedis; |
11 |
import redis.clients.jedis.JedisPool; |
13 |
import com.alibaba.dubbo.common.utils.StringUtils; |
14 |
import com.google.common.base.Strings; |
15 |
import com.google.common.collect.Lists; |
17 |
public class ChatRoomOnlineUserCounterServiceImpl implements ChatRoomOnlineUserCounterService { |
19 |
private static final Log LOG = LogFactory.getLog(ChatRoomOnlineUserCounterServiceImpl. class ); |
20 |
private JedisPool jedisPool; |
21 |
private static final String KEY_USER_COUNT = "chat::room::play::user::cnt" ; |
22 |
private static final String KEY_MAX_USER_COUNT_PREFIX = "chat::room::max::user::cnt::" ; |
23 |
private static final String DF_YYYYMMDD = "yyyyMMdd" ; |
25 |
public String queryRoomUserCount(String rooms) { |
26 |
LOG.info( "Params[Server|Recv|REQ] rooms=" + rooms); |
27 |
StringBuffer builder = new StringBuffer(); |
28 |
if (!Strings.isNullOrEmpty(rooms)) { |
31 |
jedis = jedisPool.getResource(); |
32 |
String[] fields = rooms.split( "," ); |
33 |
List<String> results = jedis.hmget(KEY_USER_COUNT, fields); |
34 |
builder.append(StringUtils.join(results, "," )); |
35 |
} catch (Exception e) { |
43 |
LOG.info( "Result[Server|Recv|RES] " + builder.toString()); |
44 |
return builder.toString(); |
48 |
public List<String> getMaxOnlineUserCount(List<String> rooms, String date, String dateFormat) { |
49 |
// HGETALL chat::room::max::user::cnt::20150326 |
50 |
LOG.info( "Params[Server|Recv|REQ] rooms=" + rooms + ",date=" + date + ",dateFormat=" + dateFormat); |
51 |
String whichDate = DateTimeUtils.format(date, dateFormat, DF_YYYYMMDD); |
52 |
String key = KEY_MAX_USER_COUNT_PREFIX + whichDate; |
53 |
StringBuffer builder = new StringBuffer(); |
54 |
if (rooms != null && !rooms.isEmpty()) { |
57 |
jedis = jedisPool.getResource(); |
58 |
return jedis.hmget(key, rooms.toArray( new String[rooms.size()])); |
59 |
} catch (Exception e) { |
67 |
LOG.info( "Result[Server|Recv|RES] " + builder.toString()); |
68 |
return Lists.newArrayList(); |
71 |
public void setJedisPool(JedisPool jedisPool) { |
72 |
this .jedisPool = jedisPool; |
代码中经过读取Redis中数据来完成调用,逻辑比较简单。对应的Maven POM依赖配置,以下所示:
03 |
< groupId >org.shirdrn.dubbo</ groupId > |
04 |
< artifactId >dubbo-api</ artifactId > |
05 |
< version >0.0.1-SNAPSHOT</ version > |
08 |
< groupId >org.shirdrn.dubbo</ groupId > |
09 |
< artifactId >dubbo-commons</ artifactId > |
10 |
< version >0.0.1-SNAPSHOT</ version > |
13 |
< groupId >redis.clients</ groupId > |
14 |
< artifactId >jedis</ artifactId > |
15 |
< version >2.5.2</ version > |
18 |
< groupId >org.apache.commons</ groupId > |
19 |
< artifactId >commons-pool2</ artifactId > |
20 |
< version >2.2</ version > |
23 |
< groupId >org.jboss.netty</ groupId > |
24 |
< artifactId >netty</ artifactId > |
25 |
< version >3.2.7.Final</ version > |
有关对Dubbo框架的一些依赖,咱们单独放到一个通用的Maven Module中(详见后面“附录:Dubbo使用Maven构建依赖配置”),这里再也不多说。服务提供方实现,最关键的就是服务的配置,由于Dubbo基于Spring来管理配置和实例,因此经过配置能够指定服务是不是分布式服务,以及经过配置增长不少其它特性。咱们的配置文件为provider-cluster.xml,内容以下所示:
01 |
<? xml version = "1.0" encoding = "UTF-8" ?> |
09 |
< bean class = "org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" > |
10 |
< property name = "systemPropertiesModeName" value = "SYSTEM_PROPERTIES_MODE_OVERRIDE" /> |
11 |
< property name = "ignoreResourceNotFound" value = "true" /> |
12 |
< property name = "locations" > |
14 |
< value >classpath*:jedis.properties</ value > |
19 |
< dubbo:application name = "chatroom-cluster-provider" /> |
20 |
< dubbo:registry address = "zookeeper://zk1:2181?backup=zk2:2181,zk3:2181" /> |
22 |
< dubbo:protocol name = "dubbo" port = "20880" /> |
24 |
< dubbo:service interface = "org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService" version = "1.0.0" |
25 |
cluster = "failover" retries = "2" timeout = "1000" loadbalance = "random" actives = "100" executes = "200" |
26 |
ref = "chatRoomOnlineUserCounterService" protocol = "dubbo" > |
27 |
< dubbo:method name = "queryRoomUserCount" timeout = "500" retries = "2" loadbalance = "roundrobin" actives = "50" /> |
30 |
< bean id = "chatRoomOnlineUserCounterService" class = "org.shirdrn.dubbo.provider.service.ChatRoomOnlineUserCounterServiceImpl" > |
31 |
< property name = "jedisPool" ref = "jedisPool" /> |
34 |
< bean id = "jedisPool" class = "redis.clients.jedis.JedisPool" destroy-method = "destroy" > |
35 |
< constructor-arg index = "0" > |
36 |
< bean class = "org.apache.commons.pool2.impl.GenericObjectPoolConfig" > |
37 |
< property name = "maxTotal" value = "${redis.pool.maxTotal}" /> |
38 |
< property name = "maxIdle" value = "${redis.pool.maxIdle}" /> |
39 |
< property name = "minIdle" value = "${redis.pool.minIdle}" /> |
40 |
< property name = "maxWaitMillis" value = "${redis.pool.maxWaitMillis}" /> |
41 |
< property name = "testOnBorrow" value = "${redis.pool.testOnBorrow}" /> |
42 |
< property name = "testOnReturn" value = "${redis.pool.testOnReturn}" /> |
43 |
< property name = "testWhileIdle" value = "true" /> |
46 |
< constructor-arg index = "1" value = "${redis.host}" /> |
47 |
< constructor-arg index = "2" value = "${redis.port}" /> |
48 |
< constructor-arg index = "3" value = "${redis.timeout}" /> |
上面配置中,使用dubbo协议,集群容错模式为failover,服务级别负载均衡策略为random,方法级别负载均衡策略为roundrobin(它覆盖了服务级别的配置内容),其余一些配置内容能够参考Dubbo文档。咱们这里是从Redis读取数据,因此使用了Redis链接池。
启动服务示例代码以下所示:
01 |
package org.shirdrn.dubbo.provider; |
03 |
import org.shirdrn.dubbo.provider.common.DubboServer; |
05 |
public class ChatRoomClusterServer { |
07 |
public static void main(String[] args) throws Exception { |
08 |
DubboServer.startServer( "classpath:provider-cluster.xml" ); |
上面调用了DubboServer类的静态方法startServer,以下所示:
01 |
public static void startServer(String config) { |
02 |
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config); |
06 |
} catch (IOException e) { |
方法中主要是初始化Spring IoC容器,所有对象都交由容器来管理。
服务消费方就容易了,只须要知道注册中心地址,并引用服务提供方提供的接口,消费方调用服务实现以下所示:
01 |
package org.shirdrn.dubbo.consumer; |
03 |
import java.util.Arrays; |
04 |
import java.util.List; |
06 |
import org.apache.commons.logging.Log; |
07 |
import org.apache.commons.logging.LogFactory; |
08 |
import org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService; |
09 |
import org.springframework.context.support.AbstractXmlApplicationContext; |
10 |
import org.springframework.context.support.ClassPathXmlApplicationContext; |
12 |
public class ChatRoomDubboConsumer { |
14 |
private static final Log LOG = LogFactory.getLog(ChatRoomDubboConsumer. class ); |
16 |
public static void main(String[] args) throws Exception { |
17 |
AbstractXmlApplicationContext context = new ClassPathXmlApplicationContext( "classpath:consumer.xml" ); |
20 |
ChatRoomOnlineUserCounterService chatRoomOnlineUserCounterService = (ChatRoomOnlineUserCounterService) context.getBean( "chatRoomOnlineUserCounterService" ); |
21 |
getMaxOnlineUserCount(chatRoomOnlineUserCounterService); |
22 |
getRealtimeOnlineUserCount(chatRoomOnlineUserCounterService); |
30 |
private static void getMaxOnlineUserCount(ChatRoomOnlineUserCounterService liveRoomOnlineUserCountService) { |
31 |
List<String> maxUserCounts = liveRoomOnlineUserCountService.getMaxOnlineUserCount( |
32 |
Arrays.asList( new String[] { "1482178010" , "1408492761" , "1430546839" , "1412517075" , "1435861734" }), "20150327" , "yyyyMMdd" ); |
33 |
LOG.info( "After getMaxOnlineUserCount invoked: maxUserCounts= " + maxUserCounts); |
36 |
private static void getRealtimeOnlineUserCount(ChatRoomOnlineUserCounterService liveRoomOnlineUserCountService) |
37 |
throws InterruptedException { |
38 |
String rooms = "1482178010,1408492761,1430546839,1412517075,1435861734" ; |
39 |
String onlineUserCounts = liveRoomOnlineUserCountService.queryRoomUserCount(rooms); |
40 |
LOG.info( "After queryRoomUserCount invoked: onlineUserCounts= " + onlineUserCounts); |
对应的配置文件为consumer.xml,内容以下所示:
01 |
<? xml version = "1.0" encoding = "UTF-8" ?> |
08 |
< dubbo:application name = "chatroom-consumer" /> |
09 |
< dubbo:registry address = "zookeeper://zk1:2181?backup=zk2:2181,zk3:2181" /> |
11 |
< dubbo:reference id = "chatRoomOnlineUserCounterService" interface = "org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService" version = "1.0.0" > |
12 |
< dubbo:method name = "queryRoomUserCount" retries = "2" /> |
也能够根据须要配置dubbo:reference相关的属性值,也能够配置dubbo:method指定调用的方法的配置信息,详细配置属性能够参考Dubbo官方文档。
开发完成提供方服务后,在本地开发调试的时候能够怎么简单怎么作,若是是要部署到生产环境,则须要打包后进行部署,能够参考下面的Maven POM配置:
04 |
< groupId >org.apache.maven.plugins</ groupId > |
05 |
< artifactId >maven-shade-plugin</ artifactId > |
06 |
< version >1.4</ version > |
08 |
< createDependencyReducedPom >true</ createDependencyReducedPom > |
12 |
< phase >package</ phase > |
18 |
< transformer implementation = "org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" /> |
19 |
< transformer implementation = "org.apache.maven.plugins.shade.resource.ManifestResourceTransformer" > |
20 |
< mainClass >org.shirdrn.dubbo.provider.ChatRoomClusterServer</ mainClass > |
这里也给出Maven POM依赖的简单配置:
3 |
< groupId >org.shirdrn.dubbo</ groupId > |
4 |
< artifactId >dubbo-api</ artifactId > |
5 |
< version >0.0.1-SNAPSHOT</ version > |
咱们开发的服务应该是分布式的,首先是经过配置内容来决定,例如设置集群模式、设置负载均衡模式等,而后在部署的时候,能够在多个节点上同一个服务,这样多个服务都会注册到Dubbo注册中心,若是某个节点上的服务不可用了,能够根据咱们配置的策略来选择其余节点上的可用服务,后面经过Dubbo服务管理中心和监控中心就能更加清楚明了。
Dubbo服务管理与监控
咱们须要在安装好管理中心和监控中心之后,再将上面的开发的提供方服务部署到物理节点上,而后就可以经过管理中心和监控中心来查看对应的详细状况。
安装Dubbo服务管理中心,须要选择一个Web容器,咱们使用Tomcat服务器。首先下载Dubbo管理中心安装文件dubbo-admin-2.5.3.war,或者直接从源码构建获得该WAR文件。这里,咱们已经构建好对应的WAR文件,而后进行安装,执行以下命令:
1 |
cd apache-tomcat-6.0.35 |
3 |
unzip ~/dubbo-admin-2.5.3.war -d webapps/ROOT |
修改配置文件~/apache-tomcat-6.0.35/webapps/ROOT/WEB-INF/dubbo.properties,指定咱们的注册中心地址以及登陆密码,内容以下所示:
1 |
dubbo.registry.address=zookeeper://zk1:2181?backup=zk2:2181,zk3:2181 |
2 |
dubbo.admin.root.password=root |
3 |
dubbo.admin.guest.password=guest |
而后,根据须要修改~/apache-tomcat-6.0.35/conf/server.xml配置文件,主要是Tomcat HTTP 端口号(我这里使用8083端口),完成后能够直接启动Tomcat服务器:
1 |
cd ~/apache-tomcat-6.0.35/ |
而后访问地址http://10.10.4.130:8083/便可,根据配置文件指定的root用户密码,就能够登陆Dubbo管理控制台。
咱们将上面开发的服务提供方服务,部署到2个独立的节点上(192.168.14.1和10.10.4.125),而后能够经过Dubbo管理中心查看对应服务的情况,如图所示:
上图中能够看出,该服务有两个独立的节点能够提供,由于配置的集群模式为failover,若是某个节点的服务发生故障没法使用,则会自动透明地重试另外一个节点上的服务,这样就不至于出现拒绝服务的状况。若是想要查看提供方某个节点上的服务详情,能够点击对应的IP:Port连接,示例如图所示:
上图能够看到服务地址:
1 |
dubbo://10.10.4.125:20880/org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService?actives=100&anyhost=true&application=chatroom-cluster-provider&cluster=failover&dubbo=0.0.1-SNAPSHOT&executes=200&interface=org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService&loadbalance=random&methods=getMaxOnlineUserCount,queryRoomUserCount&pid=30942&queryRoomUserCount.actives=50&queryRoomUserCount.loadbalance=leastactive&queryRoomUserCount.retries=2&queryRoomUserCount.timeout=500&retries=2&revision=0.0.1-SNAPSHOT&side=provider&timeout=1000×tamp=1427793652814&version=1.0.0 |
若是咱们直接暴露该地址也是能够的,不过这种直连的方式对服务消费方不是透明的,若是之后IP地址更换,也会影响调用方,因此最好是经过注册中心来隐蔽服务地址。同一个服务所部署在的多个节点上,也就对应对应着多个服务地址。另外,也能够对已经发布的服务进行控制,如修改访问控制、负载均衡相关配置内容等,能够经过上图中“消费者”查看服务消费方调用服务的状况,如图所示:
也在管理控制台能够对消费方进行管理控制。
Dubbo监控中心是以Dubbo服务的形式发布到注册中心,和普通的服务时同样的。例如,我这里下载了Dubbo自带的简易监控中心文件dubbo-monitor-simple-2.5.3-assembly.tar.gz,能够解压缩之后,修改配置文件~/dubbo-monitor-simple-2.5.3/conf/dubbo.properties的内容,以下所示:
01 |
dubbo.container=log4j,spring,registry,jetty |
02 |
dubbo.application.name=simple-monitor |
03 |
dubbo.application.owner= |
04 |
dubbo.registry.address=zookeeper://zk1:2181?backup=zk2:2181,zk3:2181 |
05 |
dubbo.protocol.port=7070 |
07 |
dubbo.jetty.directory=${user.home}/monitor |
08 |
dubbo.charts.directory=${dubbo.jetty.directory}/charts |
09 |
dubbo.statistics.directory=${user.home}/monitor/statistics |
10 |
dubbo.log4j.file=logs/dubbo-monitor-simple.log |
11 |
dubbo.log4j.level=WARN |
而后启动简易监控中心,执行以下命令:
1 |
cd ~/dubbo-monitor-simple-2.5.3 |
这里使用了Jetty Web容器,访问地址http://10.10.4.130:8087/就能够查看监控中心,Applications选项卡页面包含了服务提供方和消费方的基本信息,如图所示:
上图主要列出了全部提供方发布的服务、消费方调用、服务依赖关系等内容。
接着,查看Services选项卡页面,包含了服务提供方提供的服务列表,如图所示:
点击上图中Providers连接就能看到服务提供方的基本信息,包括服务地址等,如图所示:
点击上图中Consumers连接就能看到服务消费方的基本信息,包括服务地址等,如图所示:
因为上面是Dubbo自带的一个简易监控中心,可能所展示的内容并不能知足咱们的须要,因此能够根据须要开发本身的监控中心。Dubbo也提供了监控中心的扩展接口,若是想要实现本身的监控中心,能够实现接口com.alibaba.dubbo.monitor.MonitorFactory和com.alibaba.dubbo.monitor.Monitor,其中MonitorFactory接口定义以下所示:
02 |
* MonitorFactory. (SPI, Singleton, ThreadSafe) |
04 |
* @author william.liangf |
07 |
public interface MonitorFactory { |
15 |
Monitor getMonitor(URL url); |
Monitor接口定义以下所示:
2 |
* Monitor. (SPI, Prototype, ThreadSafe) |
4 |
* @see com.alibaba.dubbo.monitor.MonitorFactory#getMonitor(com.alibaba.dubbo.common.URL) |
5 |
* @author william.liangf |
7 |
public interface Monitor extends Node, MonitorService { |
具体定义内容能够查看MonitorService接口,再也不累述。
总结
Dubbo还提供了其余不少高级特性,如路由规则、参数回调、服务分组、服务降级等等,并且不少特性在给出内置实现的基础上,还给出了扩展的接口,咱们能够给出自定义的实现,很是方便并且强大。更多能够参考Dubbo官网用户手册和开发手册。
附录:Dubbo使用Maven构建依赖配置
02 |
< spring.version >3.2.8.RELEASE</ spring.version > |
03 |
< project.build.sourceEncoding >UTF-8</ project.build.sourceEncoding > |
08 |
< groupId >com.alibaba</ groupId > |
09 |
< artifactId >dubbo</ artifactId > |
10 |
< version >2.5.3</ version > |
13 |
< groupId >org.springframework</ groupId > |
14 |
< artifactId >spring</ artifactId > |
17 |
< groupId >org.apache.zookeeper</ groupId > |
18 |
< artifactId >zookeeper</ artifactId > |
21 |
< groupId >org.jboss.netty</ groupId > |
22 |
< artifactId >netty</ artifactId > |
27 |
< groupId >org.springframework</ groupId > |
28 |
< artifactId >spring-core</ artifactId > |
29 |
< version >${spring.version}</ version > |
32 |
< groupId >org.springframework</ groupId > |
33 |
< artifactId >spring-beans</ artifactId > |
34 |
< version >${spring.version}</ version > |
37 |
< groupId >org.springframework</ groupId > |
38 |
< artifactId >spring-context</ artifactId > |
39 |
< version >${spring.version}</ version > |
42 |
< groupId >org.springframework</ groupId > |
43 |
< artifactId >spring-context-support</ artifactId > |
44 |
< version >${spring.version}</ version > |
47 |
< groupId >org.springframework</ groupId > |
48 |
< artifactId >spring-web</ artifactId > |
49 |
< version >${spring.version}</ version > |
53 |
< groupId >org.slf4j</ groupId > |
54 |
< artifactId >slf4j-api</ artifactId > |
55 |
< version >1.6.2</ version > |
58 |
< groupId >log4j</ groupId > |
59 |
< artifactId >log4j</ artifactId > |
60 |
< version >1.2.16</ version > |
63 |
< groupId >org.javassist</ groupId > |
64 |
< artifactId >javassist</ artifactId > |
65 |
< version >3.15.0-GA</ version > |
68 |
< groupId >com.alibaba</ groupId > |
69 |
< artifactId >hessian-lite</ artifactId > |
70 |
< version >3.2.1-fixed-2</ version > |
73 |
< groupId >com.alibaba</ groupId > |
74 |
< artifactId >fastjson</ artifactId > |
75 |
< version >1.1.8</ version > |
78 |
< groupId >org.jvnet.sorcerer</ groupId > |
79 |
< artifactId >sorcerer-javac</ artifactId > |
80 |
< version >0.8</ version > |
83 |
< groupId >org.apache.zookeeper</ groupId > |
84 |
< artifactId >zookeeper</ artifactId > |
85 |
< version >3.4.5</ version > |
88 |
< groupId >com.github.sgroschupf</ groupId > |
89 |
< artifactId >zkclient</ artifactId > |
90 |
< version >0.1</ version > |
93 |
< groupId >org.jboss.netty</ groupId > |
94 |
< artifactId >netty</ artifactId > |
95 |
< version >3.2.7.Final</ version > |