好了歌

好了歌(曹雪芹《红楼梦》中诗词)

好了歌,中国著名古典章回体小说《红楼梦》中经典诗词,小说中为跛足道人所做,甄士隐彻悟后进行进一步注解,表现了作者现实主义和宗教思想。文中还有与之相和的《好了歌注》,承接并引申了《好了歌》的思想。诗歌内容隐射小说情节,表达了作者对现实的愤懑和失望,以及对自由的追求和向往!

甄士隐知投人不著,心中未免悔恨,再兼上年惊唬,急忿怨痛已伤,暮年之人,贫病交攻,竟渐渐的露出那下世的光景来。可巧这日拄了拐杖挣到街前散散心时,忽见那边来了一个跛足道人,疯癫落拓,麻鞋鹑衣,
口内念着几句言词道:
世人都晓神仙好,惟有功名忘不了!
古今将相在何方?荒冢一堆草没了。
世人都晓神仙好,只有金银忘不了!
终朝只恨聚无多,及到多时眼闭了。
世人都晓神仙好,只有娇妻忘不了!
君生日日说恩情,君死又随人去了。
世人都晓神仙好,只有儿孙忘不了!
痴心父母古来多,孝顺儿孙谁见了?
士隐听了,便迎上来道:“你满口说些甚么?只听见些‘好了’‘好了’。”
那道人笑道:“你若果听见‘好了’二字,还算你明白。可知世上万般,好便是了,了便是好。若不了,便不好;若要好,须是了。我这歌儿便名《好了歌》。”
士隐本是有夙慧的,一闻此言,心中早已彻悟,因笑道:“且住!待我将你这《好了歌》注解出来何如?”道人笑道:“你就请解。”
士隐乃说道:
陋室空堂,当年笏满床。
衰草枯杨,曾为歌舞场。
蛛丝儿结满雕梁,绿纱今又在蓬窗上。
说甚么脂正浓、粉正香,如何两鬓又成霜?
昨日黄土陇头送白骨,今宵红灯帐底卧鸳鸯。
金满箱,银满箱,转眼乞丐人皆谤。
正叹他人命不长,那知自己归来丧!
训有方,保不定日后作强梁。
择膏粱,谁承望流落在烟花巷!
因嫌纱帽小,致使锁枷杠,
昨怜破袄寒,今嫌紫蟒长。
乱烘烘你方唱罢我登场,反认他乡是故乡。
甚荒唐,到头来都是为他人作嫁衣裳。

写给自己也写给你 自己到底该何去何从

写给自己也写给你,自己到底该何去何从

能有一项长期爱好,看电影或者听歌收集旧书都行。这些爱好说白了就是让无聊的日子看起来有那么一点意义。

不要担心长时间不读书,你刷的微博,是在刷新信息量。看的电影也能充斥知识量。

看书看电影无须在意评论家说什么,有你喜欢的作家或演员就行。

过了三十岁,不管在什么地方吵架,都挺二的,尤其在网上。

五毛从来不值一提,更不配顺口一骂。

无论是谁,一有空就随便抓个人骂来骂去的,都不是什么好人。

别轻易贬低别人,同一个世界,同一个泥潭,无论黑衬衫白衬衫都带着污渍。

接下来说点小事 :))

不要占用别人车位,临时占用要留电话,否则车被划了轮胎被扎了都不好意思嚷嚷。

记得排队,记得上扶梯站右边,记得打车时别抢到别人前面,记得让自己方便的时候别给别人制造不快。

飞机没停稳何苦打开行李舱拿着背包就往外冲呢?我们不是最讨厌拥挤吗?

既然你改变不了什么,就改改自己的暴脾气,顺便去做一个正常人,当正常人越来越多,活着就没那么纠结,就会舒服一些,这就是改变。

别人给的伤害不能让你变强大,但你要学会躲避他们,不是说你惹不起,是你浪费不起那时间。把时间浪费在那种人身上,与自残无异。

关于朋友这件事,合得来就多加珍惜,不合拍就别浪费精力浪费时间辛苦维持了。

说到爱情,别为此寻死觅活,也别要求对方爱自己爱一辈子,那就是一段经历而已。

别问朋友借钱,除非是治病救人。欠钱到期还不了,要提前说,别羞于启齿错过时间等朋友问起时再支支吾吾闹得彼此都不痛快。

过去会越来越多,未来会越来越少,直到没有。当过去变成记忆,就变成身体的一部分,你忘不掉的。

当你感到自己真笨,这也干不好,那也做不好的时候,你已经是聪明人了。

不要担心这个致癌,那个致癌,这个不吃那个不吃,放心,我们最后都要死于癌症。

别人让你点菜,不会点就老老实实说不会点,别一张嘴就出来俩字:随便。

只要活着就总有那么一段时间,充满灰暗,躁动不安,可是除了面对,好像也没有别的办法。

当你感到时光漫长,实际上已经没有多少时间了。

老人倒了要扶,当年的坏人变老了,但不是所有的老人都变坏了。相信我,只有看热闹的越来越少,我们的下半生才有希望。

你会臃肿,你会白头,你将来也会倒在路边,但你不会去诬赖那个扶你的人。

你不会,我相信。

最后,

移民不是真正的理想,真正的理想是留在这个国家,管好自己并力所能及的帮助他人,以及,参与它的每一次改变。

来源:http://www.myexception.cn/other/1768786.html

基于Dubbo框架构建分布式服务 集群容错

Dubbo是Alibaba开源的分布式服务框架,我们可以非常容易地通过Dubbo来构建分布式服务,并根据自己实际业务应用场景来选择合适的集群容错模式,这个对于很多应用都是迫切希望的,只需要通过简单的配置就能够实现分布式服务调用,也就是说服务提供方(Provider)发布的服务可以天然就是集群服务,比如,在实时性要求很高的应用场景下,可能希望来自消费方(Consumer)的调用响应时间最短,只需要选择Dubbo的Forking Cluster模式配置,就可以对一个调用请求并行发送到多台对等的提供方(Provider)服务所在的节点上,只选择最快一个返回响应的,然后将调用结果返回给服务消费方(Consumer),显然这种方式是以冗余服务为基础的,需要消耗更多的资源,但是能够满足高实时应用的需求。
有关Dubbo服务框架的简单使用,可以参考我的其他两篇文章(《基于Dubbo的Hessian协议实现远程调用》,《Dubbo实现RPC调用使用入门》,后面参考链接中已给出链接),这里主要围绕Dubbo分布式服务相关配置的使用来说明与实践。

Dubbo服务集群容错

假设我们使用的是单机模式的Dubbo服务,如果在服务提供方(Provider)发布服务以后,服务消费方(Consumer)发出一次调用请求,恰好这次由于网络问题调用失败,那么我们可以配置服务消费方重试策略,可能消费方第二次重试调用是成功的(重试策略只需要配置即可,重试过程是透明的);但是,如果服务提供方发布服务所在的节点发生故障,那么消费方再怎么重试调用都是失败的,所以我们需要采用集群容错模式,这样如果单个服务节点因故障无法提供服务,还可以根据配置的集群容错模式,调用其他可用的服务节点,这就提高了服务的可用性。
首先,根据Dubbo文档,我们引用文档提供的一个架构图以及各组件关系说明,如下所示:

上述各个组件之间的关系(引自Dubbo文档)说明如下:

  • 这里的Invoker是Provider的一个可调用Service的抽象,Invoker封装了Provider地址及Service接口信息。
  • Directory代表多个Invoker,可以把它看成List,但与List不同的是,它的值可能是动态变化的,比如注册中心推送变更。
  • Cluster将Directory中的多个Invoker伪装成一个Invoker,对上层透明,伪装过程包含了容错逻辑,调用失败后,重试另一个。
  • Router负责从多个Invoker中按路由规则选出子集,比如读写分离,应用隔离等。
  • LoadBalance负责从多个Invoker中选出具体的一个用于本次调用,选的过程包含了负载均衡算法,调用失败后,需要重选。

我们也简单说明目前Dubbo支持的集群容错模式,每种模式适应特定的应用场景,可以根据实际需要进行选择。Dubbo内置支持如下6种集群模式:

  • Failover Cluster模式

配置值为failover。这种模式是Dubbo集群容错默认的模式选择,调用失败时,会自动切换,重新尝试调用其他节点上可用的服务。对于一些幂等性操作可以使用该模式,如读操作,因为每次调用的副作用是相同的,所以可以选择自动切换并重试调用,对调用者完全透明。可以看到,如果重试调用必然会带来响应端的延迟,如果出现大量的重试调用,可能说明我们的服务提供方发布的服务有问题,如网络延迟严重、硬件设备需要升级、程序算法非常耗时,等等,这就需要仔细检测排查了。
例如,可以这样显式指定Failover模式,或者不配置则默认开启Failover模式,配置示例如下:

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" />
4</dubbo:service>

上述配置使用Failover Cluster模式,如果调用失败一次,可以再次重试2次调用,服务级别调用超时时间为100ms,调用方法queryRoomUserCount的超时时间为80ms,允许重试2次,最坏情况调用花费时间160ms。如果该服务接口org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService还有其他的方法可供调用,则其他方法没有显式配置则会继承使用dubbo:service配置的属性值。

  • Failfast Cluster模式

配置值为failfast。这种模式称为快速失败模式,调用只执行一次,失败则立即报错。这种模式适用于非幂等性操作,每次调用的副作用是不同的,如写操作,比如交易系统我们要下订单,如果一次失败就应该让它失败,通常由服务消费方控制是否重新发起下订单操作请求(另一个新的订单)。

  • Failsafe Cluster模式

配置值为failsafe。失败安全模式,如果调用失败, 则直接忽略失败的调用,而是要记录下失败的调用到日志文件,以便后续审计。

  • Failback Cluster模式

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

  • Forking Cluster模式

配置值为forking。并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。

  • Broadcast Cluster模式

配置值为broadcast。广播调用所有提供者,逐个调用,任意一台报错则报错(2.1.0开始支持)。通常用于通知所有提供者更新缓存或日志等本地资源信息。
上面的6种模式都可以应用于生产环境,我们可以根据实际应用场景选择合适的集群容错模式。如果我们觉得Dubbo内置提供的几种集群容错模式都不能满足应用需要,也可以定制实现自己的集群容错模式,因为Dubbo框架给我提供的扩展的接口,只需要实现接口com.alibaba.dubbo.rpc.cluster.Cluster即可,接口定义如下所示:

01@SPI(FailoverCluster.NAME)
02public interface Cluster {
03
04    /**
05     * Merge the directory invokers to a virtual invoker.
06     * @param <T>
07     * @param directory
08     * @return cluster invoker
09     * @throws RpcException
10     */
11    @Adaptive
12    <T> Invoker<T> join(Directory<T> directory) throws RpcException;
13
14}

关于如何实现一个自定义的集群容错模式,可以参考Dubbo源码中内置支持的汲取你容错模式的实现,6种模式对应的实现类如下所示:

1com.alibaba.dubbo.rpc.cluster.support.FailoverCluster
2com.alibaba.dubbo.rpc.cluster.support.FailfastCluster
3com.alibaba.dubbo.rpc.cluster.support.FailsafeCluster
4com.alibaba.dubbo.rpc.cluster.support.FailbackCluster
5com.alibaba.dubbo.rpc.cluster.support.ForkingCluster
6com.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" />
5</dubbo:service>

上述配置,也体现了Dubbo配置的继承性特点,也就是dubbo:service元素配置了loadbalance=”random”,则该元素的子元素dubbo:method如果没有指定负载均衡策略,则默认为loadbalance=”random”,否则如果dubbo:method指定了loadbalance=”leastactive”,则使用子元素配置的负载均衡策略覆盖了父元素指定的策略(这里调用queryRoomUserCount方法使用leastactive负载均衡策略)。
当然,Dubbo框架也提供了实现自定义负载均衡策略的接口,可以实现com.alibaba.dubbo.rpc.cluster.LoadBalance接口,接口定义如下所示:

01/**
02* LoadBalance. (SPI, Singleton, ThreadSafe)
03*
04* <a href="http://en.wikipedia.org/wiki/Load_balancing_(computing)">Load-Balancing</a>
05*
06* @see com.alibaba.dubbo.rpc.cluster.Cluster#join(Directory)
07* @author qian.lei
08* @author william.liangf
09*/
10@SPI(RandomLoadBalance.NAME)
11public interface LoadBalance {
12
13     /**
14     * select one invoker in list.
15     * @param invokers invokers.
16     * @param url refer url
17     * @param invocation invocation.
18     * @return selected invoker.
19     */
20    @Adaptive("loadbalance")
21     <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
22
23}

如何实现一个自定义负载均衡策略,可以参考Dubbo框架内置的实现,如下所示的3个实现类:

1com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance
2com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance
3com.alibaba.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance

Dubbo服务集群容错实践

手机应用是以聊天室为基础的,我们需要收集用户的操作行为,然后计算聊天室中在线人数,并实时在手机应用端显示人数,整个系统的架构如图所示:

上图中,主要包括了两大主要流程:日志收集并实时处理流程、调用读取实时计算结果流程,我们使用基于Dubbo框架开发的服务来提供实时计算结果读取聊天人数的功能。上图中,实际上业务接口服务器集群也可以基于Dubbo框架构建服务,就看我们想要构建什么样的系统来满足我们的需要。
如果不使用注册中心,服务消费方也能够直接调用服务提供方发布的服务,这样需要服务提供方将服务地址暴露给服务消费方,而且也无法使用监控中心的功能,这种方式成为直连。
如果我们使用注册中心,服务提供方将服务发布到注册中心,而服务消费方可以通过注册中心订阅服务,接收服务提供方服务变更通知,这种方式可以隐藏服务提供方的细节,包括服务器地址等敏感信息,而服务消费方只能通过注册中心来获取到已注册的提供方服务,而不能直接跨过注册中心与服务提供方直接连接。这种方式的好处是还可以使用监控中心服务,能够对服务的调用情况进行监控分析,还能使用Dubbo服务管理中心,方便管理服务,我们在这里使用的是这种方式,也推荐使用这种方式。使用注册中心的Dubbo分布式服务相关组件结构,如下图所示:

下面,开发部署我们的应用,通过如下4个步骤来完成:

  • 服务接口定义

服务接口将服务提供方(Provider)和服务消费方(Consumer)连接起来,服务提供方实现接口中定义的服务,即给出服务的实现,而服务消费方负责调用服务。我们接口中给出了2个方法,一个是实时查询获取当前聊天室内人数,另一个是查询一天中某个/某些聊天室中在线人数峰值,接口定义如下所示:

01package org.shirdrn.dubbo.api;
02
03import java.util.List;
04
05public interface ChatRoomOnlineUserCounterService {
06
07     String queryRoomUserCount(String rooms);
08    
09     List<String> getMaxOnlineUserCount(List<String> rooms, String date, String dateFormat);
10}

接口是服务提供方和服务消费方公共遵守的协议,一般情况下是服务提供方将接口定义好后提供给服务消费方。

  • 服务提供方

服务提供方实现接口中定义的服务,其实现和普通的服务没什么区别,我们的实现类为ChatRoomOnlineUserCounterServiceImpl,代码如下所示:

01package org.shirdrn.dubbo.provider.service;
02
03import java.util.List;
04
05import org.apache.commons.logging.Log;
06import org.apache.commons.logging.LogFactory;
07import org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService;
08import org.shirdrn.dubbo.common.utils.DateTimeUtils;
09
10import redis.clients.jedis.Jedis;
11import redis.clients.jedis.JedisPool;
12
13import com.alibaba.dubbo.common.utils.StringUtils;
14import com.google.common.base.Strings;
15import com.google.common.collect.Lists;
16
17public class ChatRoomOnlineUserCounterServiceImpl implementsChatRoomOnlineUserCounterService {
18
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";
24
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)) {
29               Jedis jedis = null;
30               try {
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) {
36                    LOG.error("", e);
37               } finally {
38                    if(jedis != null) {
39                         jedis.close();
40                    }
41               }
42          }
43          LOG.info("Result[Server|Recv|RES] " + builder.toString());
44          return builder.toString();
45     }
46    
47     @Override
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()) {
55               Jedis jedis = null;
56               try {
57                    jedis = jedisPool.getResource();
58                    return jedis.hmget(key, rooms.toArray(new String[rooms.size()]));
59               } catch (Exception e) {
60                    LOG.error("", e);
61               } finally {
62                    if(jedis != null) {
63                         jedis.close();
64                    }
65               }
66          }
67          LOG.info("Result[Server|Recv|RES] " + builder.toString());
68          return Lists.newArrayList();
69     }
70    
71     public void setJedisPool(JedisPool jedisPool) {
72          this.jedisPool = jedisPool;
73     }
74
75}

代码中通过读取Redis中数据来完成调用,逻辑比较简单。对应的Maven POM依赖配置,如下所示:

01<dependencies>
02     <dependency>
03          <groupId>org.shirdrn.dubbo</groupId>
04          <artifactId>dubbo-api</artifactId>
05          <version>0.0.1-SNAPSHOT</version>
06     </dependency>
07     <dependency>
08          <groupId>org.shirdrn.dubbo</groupId>
09          <artifactId>dubbo-commons</artifactId>
10          <version>0.0.1-SNAPSHOT</version>
11     </dependency>
12     <dependency>
13          <groupId>redis.clients</groupId>
14          <artifactId>jedis</artifactId>
15          <version>2.5.2</version>
16     </dependency>
17     <dependency>
18          <groupId>org.apache.commons</groupId>
19          <artifactId>commons-pool2</artifactId>
20          <version>2.2</version>
21     </dependency>
22     <dependency>
23          <groupId>org.jboss.netty</groupId>
24          <artifactId>netty</artifactId>
25          <version>3.2.7.Final</version>
26     </dependency>
27</dependencies>

有关对Dubbo框架的一些依赖,我们单独放到一个通用的Maven Module中(详见后面“附录:Dubbo使用Maven构建依赖配置”),这里不再多说。服务提供方实现,最关键的就是服务的配置,因为Dubbo基于Spring来管理配置和实例,所以通过配置可以指定服务是否是分布式服务,以及通过配置增加很多其它特性。我们的配置文件为provider-cluster.xml,内容如下所示:

01<?xml version="1.0" encoding="UTF-8"?>
02
03<beans xmlns="http://www.springframework.org/schema/beans"
04     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
05     xmlns:p="http://www.springframework.org/schema/p"
06     xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsd
07     http://code.alibabatech.com/schema/dubbohttp://code.alibabatech.com/schema/dubbo/dubbo.xsd">
08
09     <beanclass="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">
13               <list>
14                    <value>classpath*:jedis.properties</value>
15               </list>
16          </property>
17     </bean>
18    
19     <dubbo:application name="chatroom-cluster-provider" />
20     <dubbo:registry address="zookeeper://zk1:2181?backup=zk2:2181,zk3:2181" />
21    
22     <dubbo:protocol name="dubbo" port="20880" />
23    
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" />
28     </dubbo:service>
29    
30     <bean id="chatRoomOnlineUserCounterService"class="org.shirdrn.dubbo.provider.service.ChatRoomOnlineUserCounterServiceImpl" >
31          <property name="jedisPool" ref="jedisPool" />
32     </bean>
33    
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" />
44               </bean>
45          </constructor-arg>
46          <constructor-arg index="1" value="${redis.host}" />
47          <constructor-arg index="2" value="${redis.port}" />
48          <constructor-arg index="3" value="${redis.timeout}" />
49     </bean>
50    
51</beans>

上面配置中,使用dubbo协议,集群容错模式为failover,服务级别负载均衡策略为random,方法级别负载均衡策略为roundrobin(它覆盖了服务级别的配置内容),其他一些配置内容可以参考Dubbo文档。我们这里是从Redis读取数据,所以使用了Redis连接池。
启动服务示例代码如下所示:

01package org.shirdrn.dubbo.provider;
02
03import org.shirdrn.dubbo.provider.common.DubboServer;
04
05public class ChatRoomClusterServer {
06
07     public static void main(String[] args) throws Exception {
08          DubboServer.startServer("classpath:provider-cluster.xml");
09     }
10
11}

上面调用了DubboServer类的静态方法startServer,如下所示:

01public static void startServer(String config) {
02     ClassPathXmlApplicationContext context = newClassPathXmlApplicationContext(config);
03     try {
04          context.start();
05          System.in.read();
06     } catch (IOException e) {
07          e.printStackTrace();
08     } finally {
09          context.close();
10     }
11}

方法中主要是初始化Spring IoC容器,全部对象都交由容器来管理。

  • 服务消费方

服务消费方就容易了,只需要知道注册中心地址,并引用服务提供方提供的接口,消费方调用服务实现如下所示:

01package org.shirdrn.dubbo.consumer;
02
03import java.util.Arrays;
04import java.util.List;
05
06import org.apache.commons.logging.Log;
07import org.apache.commons.logging.LogFactory;
08import org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService;
09import org.springframework.context.support.AbstractXmlApplicationContext;
10import org.springframework.context.support.ClassPathXmlApplicationContext;
11
12public class ChatRoomDubboConsumer {
13
14     private static final Log LOG = LogFactory.getLog(ChatRoomDubboConsumer.class);
15    
16     public static void main(String[] args) throws Exception {
17          AbstractXmlApplicationContext context = newClassPathXmlApplicationContext("classpath:consumer.xml");
18          try {
19               context.start();
20               ChatRoomOnlineUserCounterService chatRoomOnlineUserCounterService = (ChatRoomOnlineUserCounterService) context.getBean("chatRoomOnlineUserCounterService");        
21               getMaxOnlineUserCount(chatRoomOnlineUserCounterService);             
22               getRealtimeOnlineUserCount(chatRoomOnlineUserCounterService);             
23               System.in.read();
24          } finally {
25               context.close();
26          }
27         
28     }
29
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);
34     }
35
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);
41     }
42}

对应的配置文件为consumer.xml,内容如下所示:

01<?xml version="1.0" encoding="UTF-8"?>
02
03<beans xmlns="http://www.springframework.org/schema/beans"
04     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
05     xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsd
06     http://code.alibabatech.com/schema/dubbohttp://code.alibabatech.com/schema/dubbo/dubbo.xsd">
07
08     <dubbo:application name="chatroom-consumer" />
09     <dubbo:registry address="zookeeper://zk1:2181?backup=zk2:2181,zk3:2181" />
10    
11     <dubbo:reference id="chatRoomOnlineUserCounterService"interface="org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService" version="1.0.0">
12          <dubbo:method name="queryRoomUserCount" retries="2" />
13     </dubbo:reference>
14
15</beans>

也可以根据需要配置dubbo:reference相关的属性值,也可以配置dubbo:method指定调用的方法的配置信息,详细配置属性可以参考Dubbo官方文档。

  • 部署与验证

开发完成提供方服务后,在本地开发调试的时候可以怎么简单怎么做,如果是要部署到生产环境,则需要打包后进行部署,可以参考下面的Maven POM配置:

01<build>
02     <plugins>
03          <plugin>
04               <groupId>org.apache.maven.plugins</groupId>
05               <artifactId>maven-shade-plugin</artifactId>
06               <version>1.4</version>
07               <configuration>
08                    <createDependencyReducedPom>true</createDependencyReducedPom>
09               </configuration>
10               <executions>
11                    <execution>
12                         <phase>package</phase>
13                         <goals>
14                              <goal>shade</goal>
15                         </goals>
16                         <configuration>
17                              <transformers>
18                                   <transformerimplementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
19                                   <transformerimplementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
20                                        <mainClass>org.shirdrn.dubbo.provider.ChatRoomClusterServer</mainClass>
21                                   </transformer>
22                              </transformers>
23                         </configuration>
24                    </execution>
25               </executions>
26          </plugin>
27     </plugins>
28</build>

这里也给出Maven POM依赖的简单配置:

1<dependencies>
2     <dependency>
3          <groupId>org.shirdrn.dubbo</groupId>
4          <artifactId>dubbo-api</artifactId>
5          <version>0.0.1-SNAPSHOT</version>
6     </dependency>
7</dependencies>

我们开发的服务应该是分布式的,首先是通过配置内容来决定,例如设置集群模式、设置负载均衡模式等,然后在部署的时候,可以在多个节点上同一个服务,这样多个服务都会注册到Dubbo注册中心,如果某个节点上的服务不可用了,可以根据我们配置的策略来选择其他节点上的可用服务,后面通过Dubbo服务管理中心和监控中心就能更加清楚明了。

来源:http://shiyanjun.cn/archives/1075.html