首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

基于Dubbo框架构建分布式服务(3)

基于Dubbo框架构建分布式服务(3)

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

上图中,主要包括了两大主要流程:日志收集并实时处理流程、调用读取实时计算结果流程,我们使用基于Dubbo框架开发的服务来提供实时计算结果读取聊天人数的功能。上图中,实际上业务接口服务器集群也可以基于Dubbo框架构建服务,就看我们想要构建什么样的系统来满足我们的需要。

如果不使用注册中心,服务消费方也能够直接调用服务提供方发布的服务,这样需要服务提供方将服务地址暴露给服务消费方,而且也无法使用监控中心的功能,这种方式成为直连。

如果我们使用注册中心,服务提供方将服务发布到注册中心,而服务消费方可以通过注册中心订阅服务,接收服务提供方服务变更通知,这种方式可以隐藏服务提供方的细节,包括服务器地址等敏感信息,而服务消费方只能通过注册中心来获取到已注册的提供方服务,而不能直接跨过注册中心与服务提供方直接连接。这种方式的好处是还可以使用监控中心服务,能够对服务的调用情况进行监控分析,还能使用Dubbo服务管理中心,方便管理服务,我们在这里使用的是这种方式,也推荐使用这种方式。使用注册中心的Dubbo分布式服务相关组件结构,如下图所示:

下面,开发部署我们的应用,通过如下4个步骤来完成:
  • ●服务接口定义
服务接口将服务提供方(Provider)和服务消费方(Consumer)连接起来,服务提供方实现接口中定义的服务,即给出服务的实现,而服务消费方负责调用服务。我们接口中给出了2个方法,一个是实时查询获取当前聊天室内人数,另一个是查询一天中某个/某些聊天室中在线人数峰值,接口定义如下所示:
[url=][/url]
package org.shirdrn.dubbo.api;import java.util.List;public interface ChatRoomOnlineUserCounterService {     String queryRoomUserCount(String rooms);         List<String> getMaxOnlineUserCount(List<String> rooms, String date, String dateFormat);}[url=][/url]

接口是服务提供方和服务消费方公共遵守的协议,一般情况下是服务提供方将接口定义好后提供给服务消费方。
  • ●服务提供方
服务提供方实现接口中定义的服务,其实现和普通的服务没什么区别,我们的实现类为ChatRoomOnlineUserCounterServiceImpl,代码如下所示:
[url=][/url]
package org.shirdrn.dubbo.provider.service;import java.util.List;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService;import org.shirdrn.dubbo.common.utils.DateTimeUtils;import redis.clients.jedis.Jedis;import redis.clients.jedis.JedisPool;import com.alibaba.dubbo.common.utils.StringUtils;import com.google.common.base.Strings;import com.google.common.collect.Lists;public class ChatRoomOnlineUserCounterServiceImpl implements ChatRoomOnlineUserCounterService {     private static final Log LOG = LogFactory.getLog(ChatRoomOnlineUserCounterServiceImpl.class);     private JedisPool jedisPool;     private static final String KEY_USER_COUNT = "chat::room::play::user::cnt";     private static final String KEY_MAX_USER_COUNT_PREFIX = "chat::room::max::user::cnt::";     private static final String DF_YYYYMMDD = "yyyyMMdd";     public String queryRoomUserCount(String rooms) {          LOG.info("Params[Server|Recv|REQ] rooms=" + rooms);          StringBuffer builder = new StringBuffer();          if(!Strings.isNullOrEmpty(rooms)) {               Jedis jedis = null;               try {                    jedis = jedisPool.getResource();                    String[] fields = rooms.split(",");                    List<String> results = jedis.hmget(KEY_USER_COUNT, fields);                    builder.append(StringUtils.join(results, ","));               } catch (Exception e) {                    LOG.error("", e);               } finally {                    if(jedis != null) {                         jedis.close();                    }               }          }          LOG.info("Result[Server|Recv|RES] " + builder.toString());          return builder.toString();     }         @Override     public List<String> getMaxOnlineUserCount(List<String> rooms, String date, String dateFormat) {          // HGETALL chat::room::max::user::cnt::20150326          LOG.info("Params[Server|Recv|REQ] rooms=" + rooms + ",date=" + date + ",dateFormat=" + dateFormat);          String whichDate = DateTimeUtils.format(date, dateFormat, DF_YYYYMMDD);          String key = KEY_MAX_USER_COUNT_PREFIX + whichDate;          StringBuffer builder = new StringBuffer();          if(rooms != null && !rooms.isEmpty()) {               Jedis jedis = null;               try {                    jedis = jedisPool.getResource();                    return jedis.hmget(key, rooms.toArray(new String[rooms.size()]));               } catch (Exception e) {                    LOG.error("", e);               } finally {                    if(jedis != null) {                         jedis.close();                    }               }          }          LOG.info("Result[Server|Recv|RES] " + builder.toString());          return Lists.newArrayList();     }         public void setJedisPool(JedisPool jedisPool) {          this.jedisPool = jedisPool;     }}[url=][/url]

代码中通过读取Redis中数据来完成调用,逻辑比较简单。对应的Maven POM依赖配置,如下所示:View Code
有关对Dubbo框架的一些依赖,我们单独放到一个通用的Maven Module中(详见后面“附录:Dubbo使用Maven构建依赖配置”),这里不再多说。服务提供方实现,最关键的就是服务的配置,因为Dubbo基于Spring来管理配置和实例,所以通过配置可以指定服务是否是分布式服务,以及通过配置增加很多其它特性。我们的配置文件为provider-cluster.xml,内容如下所示:
[url=][/url]
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"     xmlns:p="http://www.springframework.org/schema/p"     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd     http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
返回列表