Dubbo學習(二) Dubbo 集羣容錯模式-負載均衡模式

Dubbo是Alibaba開源的分佈式服務框架,咱們能夠很是容易地經過Dubbo來構建分佈式服務,並根據本身實際業務應用場景來選擇合適的集羣容錯模式,這個對於不少應用都是迫切但願的,只須要經過簡單的配置就可以實現分佈式服務調用,也就是說服務提供方(Provider)發佈的服務能夠自然就是集羣服務,好比,在實時性要求很高的應用場景下,可能但願來自消費方(Consumer)的調用響應時間最短,只須要選擇Dubbo的Forking Cluster模式配置,就能夠對一個調用請求並行發送到多臺對等的提供方(Provider)服務所在的節點上,只選擇最快一個返回響應的,而後將調用結果返回給服務消費方(Consumer),顯然這種方式是以冗餘服務爲基礎的,須要消耗更多的資源,可是可以知足高實時應用的需求。 html

1、Dubbo服務集羣容錯java

假設咱們使用的是單機模式的Dubbo服務,若是在服務提供方(Provider)發佈服務之後,服務消費方(Consumer)發出一次調用請求,剛好此次因爲網絡問題調用失敗,那麼咱們能夠配置服務消費方重試策略,可能消費方第二次重試調用是成功的(重試策略只須要配置便可,重試過程是透明的);可是,若是服務提供方發佈服務所在的節點發生故障,那麼消費方再怎麼重試調用都是失敗的,因此咱們須要採用集羣容錯模式,這樣若是單個服務節點因故障沒法提供服務,還能夠根據配置的集羣容錯模式,調用其餘可用的服務節點,這就提升了服務的可用性。
首先,根據Dubbo文檔,咱們引用文檔提供的一個架構圖以及各組件關係說明,以下所示:

上述各個組件之間的關係(引自Dubbo文檔)說明以下:web

  • 這裏的Invoker是Provider的一個可調用Service的抽象,Invoker封裝了Provider地址及Service接口信息。
  • Directory表明多個Invoker,能夠把它當作List,但與List不一樣的是,它的值多是動態變化的,好比註冊中心推送變動。
  • Cluster將Directory中的多個Invoker假裝成一個Invoker,對上層透明,假裝過程包含了容錯邏輯,調用失敗後,重試另外一個。
  • Router負責從多個Invoker中按路由規則選出子集,好比讀寫分離,應用隔離等。
  • LoadBalance負責從多個Invoker中選出具體的一個用於本次調用,選的過程包含了負載均衡算法,調用失敗後,須要重選。

咱們也簡單說明目前Dubbo支持的集羣容錯模式,每種模式適應特定的應用場景,能夠根據實際須要進行選擇。redis

Dubbo內置支持以下6種集羣模式:算法

 (1) Failover Cluster模式spring

配置值爲failover。這種模式是Dubbo集羣容錯默認的模式選擇,調用失敗時,會自動切換,從新嘗試調用其餘節點上可用的服務。對於一些冪等性操做可使用該模式,如讀操做,由於每次調用的反作用是相同的,因此能夠選擇自動切換並重試調用,對調用者徹底透明。能夠看到,若是重試調用必然會帶來響應端的延遲,若是出現大量的重試調用,可能說明咱們的服務提供方發佈的服務有問題,如網絡延遲嚴重、硬件設備須要升級、程序算法很是耗時,等等,這就須要仔細檢測排查了。
例如,能夠這樣顯式指定Failover模式,或者不配置則默認開啓Failover模式,配置示例以下:   apache

<dubbo:service interface="org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService" version="1.0.0"
     cluster="failover" retries="2" timeout="100" ref="chatRoomOnlineUserCounterService" protocol="dubbo" >
     <dubbo:method name="queryRoomUserCount" timeout="80" retries="2" />
</dubbo:service>

 

上述配置使用Failover Cluster模式,若是調用失敗一次,能夠再次重試2次調用,服務級別調用超時時間爲100ms,調用方法queryRoomUserCount的超時時間爲80ms,容許重試2次,最壞狀況調用花費時間160ms。若是該服務接口org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService還有其餘的方法可供調用,則其餘方法沒有顯式配置則會繼承使用dubbo:service配置的屬性值。api

(2) Failfast Cluster模式緩存

配置值爲failfast。這種模式稱爲快速失敗模式,調用只執行一次,失敗則當即報錯。這種模式適用於非冪等性操做,每次調用的反作用是不一樣的,如寫操做,好比交易系統咱們要下訂單,若是一次失敗就應該讓它失敗,一般由服務消費方控制是否從新發起下訂單操做請求(另外一個新的訂單)。tomcat

(3) Failsafe Cluster模式

置值爲failsafe。失敗安全模式,若是調用失敗, 則直接忽略失敗的調用,而是要記錄下失敗的調用到日誌文件,以便後續審計

(4) Failback Cluster模式

配置值爲failback。失敗自動恢復,後臺記錄失敗請求,定時重發。一般用於消息通知操做

(5) Forking Cluster模式

配置值爲forking。並行調用多個服務器,只要一個成功即返回。一般用於實時性要求較高的讀操做,但須要浪費更多服務資源。

(6) Broadcast Cluster模式

配置值爲broadcast。廣播調用全部提供者,逐個調用,任意一臺報錯則報錯(2.1.0開始支持)。一般用於通知全部提供者更新緩存或日誌等本地資源信息。
上面的6種模式均可以應用於生產環境,咱們能夠根據實際應用場景選擇合適的集羣容錯模式。若是咱們以爲Dubbo內置提供的幾種集羣容錯模式都不能知足應用須要,也能夠定製實現本身的集羣容錯模式,由於Dubbo框架給我提供的擴展的接口,只須要實現接口com.alibaba.dubbo.rpc.cluster.Cluster便可,接口定義以下所示:

   
@SPI(FailoverCluster.NAME)
public interface Cluster {
 
    /**
     * Merge the directory invokers to a virtual invoker.
     * @param <T>
     * @param directory
     * @return cluster invoker
     * @throws RpcException
     */
    @Adaptive
    <T> Invoker<T> join(Directory<T> directory) throws RpcException;
 
}
View Code

 

關於如何實現一個自定義的集羣容錯模式,能夠參考Dubbo源碼中內置支持的汲取你容錯模式的實現,6種模式對應的實現類以下所示:

  
com.alibaba.dubbo.rpc.cluster.support.FailoverCluster com.alibaba.dubbo.rpc.cluster.support.FailfastCluster com.alibaba.dubbo.rpc.cluster.support.FailsafeCluster com.alibaba.dubbo.rpc.cluster.support.FailbackCluster com.alibaba.dubbo.rpc.cluster.support.ForkingCluster com.alibaba.dubbo.rpc.cluster.support.AvailableCluster

 可能咱們初次接觸Dubbo時,不知道如何在實際開發過程當中使用Dubbo的集羣模式,後面咱們會以Failover Cluster模式爲例開發咱們的分佈式應用,再進行詳細的介紹。

2、Dubbo服務負載均衡

Dubbo框架內置提供負載均衡的功能以及擴展接口,咱們能夠透明地擴展一個服務或服務集羣,根據須要很是容易地增長/移除節點,提升服務的可伸縮性。

Dubbo框架內置提供了4種負載均衡策略,以下所示:

(1)Random LoadBalance:隨機策略,配置值爲random。能夠設置權重,有利於充分利用服務器的資源,高配的能夠設置權重大一些,低配的能夠稍微小一些

(2)RoundRobin LoadBalance:輪詢策略,配置值爲roundrobin

(3)LeastActive LoadBalance:配置值爲leastactive。根據請求調用的次數計數,處理請求更慢的節點會受到更少的請求

(4)ConsistentHash LoadBalance:一致性Hash策略,具體配置方法能夠參考Dubbo文檔。相同調用參數的請求會發送到同一個服務提供方節點上,若是某個節點發生故障沒法提供服務,則會基於一致性Hash算法映射到虛擬節點上(其餘服務提供方)

在實際使用中,只須要選擇合適的負載均衡策略值,配置便可,下面是上述四種負載均衡策略配置的示例:

  
<dubbo:service interface="org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService" version="1.0.0"
     cluster="failover" retries="2" timeout="100" loadbalance="random"
     ref="chatRoomOnlineUserCounterService" protocol="dubbo" >
     <dubbo:method name="queryRoomUserCount" timeout="80" retries="2" loadbalance="leastactive" />
</dubbo:service>

 

上述配置,也體現了Dubbo配置的繼承性特色,也就是dubbo:service元素配置了loadbalance=」random」,則該元素的子元素dubbo:method若是沒有指定負載均衡策略,則默認爲loadbalance=」random」,不然若是dubbo:method指定了loadbalance=」leastactive」,則使用子元素配置的負載均衡策略覆蓋了父元素指定的策略(這裏調用queryRoomUserCount方法使用leastactive負載均衡策略)。
固然,Dubbo框架也提供了實現自定義負載均衡策略的接口,能夠實現com.alibaba.dubbo.rpc.cluster.LoadBalance接口,接口定義以下所示:

 
/**
* LoadBalance. (SPI, Singleton, ThreadSafe)
*
* <a href="http://en.wikipedia.org/wiki/Load_balancing_(computing)">Load-Balancing</a>
*
* @see com.alibaba.dubbo.rpc.cluster.Cluster#join(Directory)
* @author qian.lei
* @author william.liangf
*/
@SPI(RandomLoadBalance.NAME)
public interface LoadBalance {
 
     /**
     * select one invoker in list.
     * @param invokers invokers.
     * @param url refer url
     * @param invocation invocation.
     * @return selected invoker.
     */
    @Adaptive("loadbalance")
     <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
 
}
View Code

 

如何實現一個自定義負載均衡策略,能夠參考Dubbo框架內置的實現,以下所示的3個實現類:

  
com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance com.alibaba.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance

Dubbo服務集羣容錯實踐

手機應用是以聊天室爲基礎的,咱們須要收集用戶的操做行爲,而後計算聊天室中在線人數,並實時在手機應用端顯示人數,整個系統的架構如圖所示:

上圖中,主要包括了兩大主要流程:日誌收集並實時處理流程、調用讀取實時計算結果流程,咱們使用基於Dubbo框架開發的服務來提供實時計算結果讀取聊天人數的功能。上圖中,實際上業務接口服務器集羣也能夠基於Dubbo框架構建服務,就看咱們想要構建什麼樣的系統來知足咱們的須要。
若是不使用註冊中心,服務消費方也可以直接調用服務提供方發佈的服務,這樣須要服務提供方將服務地址暴露給服務消費方,並且也沒法使用監控中心的功能,這種方式成爲直連。
若是咱們使用註冊中心,服務提供方將服務發佈到註冊中心,而服務消費方能夠經過註冊中心訂閱服務,接收服務提供方服務變動通知,這種方式能夠隱藏服務提供方的細節,包括服務器地址等敏感信息,而服務消費方只能經過註冊中心來獲取到已註冊的提供方服務,而不能直接跨過註冊中心與服務提供方直接鏈接。這種方式的好處是還可使用監控中心服務,可以對服務的調用狀況進行監控分析,還能使用Dubbo服務管理中心,方便管理服務,咱們在這裏使用的是這種方式,也推薦使用這種方式。使用註冊中心的Dubbo分佈式服務相關組件結構,以下圖所示:

下面,開發部署咱們的應用,經過以下4個步驟來完成:

  • 服務接口定義

服務接口將服務提供方(Provider)和服務消費方(Consumer)鏈接起來,服務提供方實現接口中定義的服務,即給出服務的實現,而服務消費方負責調用服務。咱們接口中給出了2個方法,一個是實時查詢獲取當前聊天室內人數,另外一個是查詢一天中某個/某些聊天室中在線人數峯值,接口定義以下所示:

  
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);
}

 

接口是服務提供方和服務消費方公共遵照的協議,通常狀況下是服務提供方將接口定義好後提供給服務消費方。

  • 服務提供方

服務提供方實現接口中定義的服務,其實現和普通的服務沒什麼區別,咱們的實現類爲ChatRoomOnlineUserCounterServiceImpl,代碼以下所示:

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;
     }
 
}
View Code

 


代碼中經過讀取Redis中數據來完成調用,邏輯比較簡單。對應的Maven POM依賴配置,以下所示:

  
<dependencies>
     <dependency>
          <groupId>org.shirdrn.dubbo</groupId>
          <artifactId>dubbo-api</artifactId>
          <version>0.0.1-SNAPSHOT</version>
     </dependency>
     <dependency>
          <groupId>org.shirdrn.dubbo</groupId>
          <artifactId>dubbo-commons</artifactId>
          <version>0.0.1-SNAPSHOT</version>
     </dependency>
     <dependency>
          <groupId>redis.clients</groupId>
          <artifactId>jedis</artifactId>
          <version>2.5.2</version>
     </dependency>
     <dependency>
          <groupId>org.apache.commons</groupId>
          <artifactId>commons-pool2</artifactId>
          <version>2.2</version>
     </dependency>
     <dependency>
          <groupId>org.jboss.netty</groupId>
          <artifactId>netty</artifactId>
          <version>3.2.7.Final</version>
     </dependency>
</dependencies>

有關對Dubbo框架的一些依賴,咱們單獨放到一個通用的Maven Module中(詳見後面「附錄:Dubbo使用Maven構建依賴配置」),這裏再也不多說。服務提供方實現,最關鍵的就是服務的配置,由於Dubbo基於Spring來管理配置和實例,因此經過配置能夠指定服務是不是分佈式服務,以及經過配置增長不少其它特性。咱們的配置文件爲provider-cluster.xml,內容以下所示:

 

<?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">
 
     <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
          <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
          <property name="ignoreResourceNotFound" value="true" />
          <property name="locations">
               <list>
                    <value>classpath*:jedis.properties</value>
               </list>
          </property>
     </bean>
     
     <dubbo:application name="chatroom-cluster-provider" />
     <dubbo:registry address="zookeeper://zk1:2181?backup=zk2:2181,zk3:2181" />
     
     <dubbo:protocol name="dubbo" port="20880" />
     
     <dubbo:service interface="org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService" version="1.0.0"
          cluster="failover" retries="2" timeout="1000" loadbalance="random" actives="100" executes="200"
          ref="chatRoomOnlineUserCounterService" protocol="dubbo" >
          <dubbo:method name="queryRoomUserCount" timeout="500" retries="2" loadbalance="roundrobin" actives="50" />
     </dubbo:service>
     
     <bean id="chatRoomOnlineUserCounterService" class="org.shirdrn.dubbo.provider.service.ChatRoomOnlineUserCounterServiceImpl" >
          <property name="jedisPool" ref="jedisPool" />
     </bean>
     
     <bean id="jedisPool" class="redis.clients.jedis.JedisPool" destroy-method="destroy">
          <constructor-arg index="0">
               <bean class="org.apache.commons.pool2.impl.GenericObjectPoolConfig">
                    <property name="maxTotal" value="${redis.pool.maxTotal}" />
                    <property name="maxIdle" value="${redis.pool.maxIdle}" />
                    <property name="minIdle" value="${redis.pool.minIdle}" />
                    <property name="maxWaitMillis" value="${redis.pool.maxWaitMillis}" />
                    <property name="testOnBorrow" value="${redis.pool.testOnBorrow}" />
                    <property name="testOnReturn" value="${redis.pool.testOnReturn}" />
                    <property name="testWhileIdle" value="true" />
               </bean>
          </constructor-arg>
          <constructor-arg index="1" value="${redis.host}" />
          <constructor-arg index="2" value="${redis.port}" />
          <constructor-arg index="3" value="${redis.timeout}" />
     </bean>
     
</beans>
View Code

 

 

 

上面配置中,使用dubbo協議,集羣容錯模式爲failover,服務級別負載均衡策略爲random,方法級別負載均衡策略爲roundrobin(它覆蓋了服務級別的配置內容),其餘一些配置內容能夠參考Dubbo文檔。咱們這裏是從Redis讀取數據,因此使用了Redis鏈接池。

啓動服務示例代碼以下所示:  

 

package org.shirdrn.dubbo.provider;
 
import org.shirdrn.dubbo.provider.common.DubboServer;
 
public class ChatRoomClusterServer {
 
     public static void main(String[] args) throws Exception {
          DubboServer.startServer("classpath:provider-cluster.xml");
     }
 
}

  

上面調用了DubboServer類的靜態方法startServer,以下所示:

 
public static void startServer(String config) {
     ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);
     try {
          context.start();
          System.in.read();
     } catch (IOException e) {
          e.printStackTrace();
     } finally {
          context.close();
     }
}

 

方法中主要是初始化Spring IoC容器,所有對象都交由容器來管理。

  • 服務消費方

服務消費方就容易了,只須要知道註冊中心地址,並引用服務提供方提供的接口,消費方調用服務實現以下所示:

  
package org.shirdrn.dubbo.consumer;
 
import java.util.Arrays;
import java.util.List;
 
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService;
import org.springframework.context.support.AbstractXmlApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
 
public class ChatRoomDubboConsumer {
 
     private static final Log LOG = LogFactory.getLog(ChatRoomDubboConsumer.class);
     
     public static void main(String[] args) throws Exception {
          AbstractXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:consumer.xml");
          try {
               context.start();
               ChatRoomOnlineUserCounterService chatRoomOnlineUserCounterService = (ChatRoomOnlineUserCounterService) context.getBean("chatRoomOnlineUserCounterService");        
               getMaxOnlineUserCount(chatRoomOnlineUserCounterService);             
               getRealtimeOnlineUserCount(chatRoomOnlineUserCounterService);             
               System.in.read();
          } finally {
               context.close();
          }
          
     }
 
     private static void getMaxOnlineUserCount(ChatRoomOnlineUserCounterService liveRoomOnlineUserCountService) {
          List<String> maxUserCounts = liveRoomOnlineUserCountService.getMaxOnlineUserCount(
                    Arrays.asList(new String[] {"1482178010" , "1408492761", "1430546839", "1412517075", "1435861734"}), "20150327", "yyyyMMdd");
          LOG.info("After getMaxOnlineUserCount invoked: maxUserCounts= " + maxUserCounts);
     }
 
     private static void getRealtimeOnlineUserCount(ChatRoomOnlineUserCounterService liveRoomOnlineUserCountService)
               throws InterruptedException {
          String rooms = "1482178010,1408492761,1430546839,1412517075,1435861734";
          String onlineUserCounts = liveRoomOnlineUserCountService.queryRoomUserCount(rooms);
          LOG.info("After queryRoomUserCount invoked: onlineUserCounts= " + onlineUserCounts);
     }
}
View Code

 

對應的配置文件爲consumer.xml,內容以下所示:

  
<?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"
     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">
 
     <dubbo:application name="chatroom-consumer" />
     <dubbo:registry address="zookeeper://zk1:2181?backup=zk2:2181,zk3:2181" />
     
     <dubbo:reference id="chatRoomOnlineUserCounterService" interface="org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService" version="1.0.0">
          <dubbo:method name="queryRoomUserCount" retries="2" />
     </dubbo:reference>
 
</beans>

 

也能夠根據須要配置dubbo:reference相關的屬性值,也能夠配置dubbo:method指定調用的方法的配置信息,詳細配置屬性能夠參考Dubbo官方文檔。

  • 部署與驗證

開發完成提供方服務後,在本地開發調試的時候能夠怎麼簡單怎麼作,若是是要部署到生產環境,則須要打包後進行部署,能夠參考下面的Maven POM配置:

  
<build>
     <plugins>
          <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-shade-plugin</artifactId>
               <version>1.4</version>
               <configuration>
                    <createDependencyReducedPom>true</createDependencyReducedPom>
               </configuration>
               <executions>
                    <execution>
                         <phase>package</phase>
                         <goals>
                              <goal>shade</goal>
                         </goals>
                         <configuration>
                              <transformers>
                                   <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
                                   <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                        <mainClass>org.shirdrn.dubbo.provider.ChatRoomClusterServer</mainClass>
                                   </transformer>
                              </transformers>
                         </configuration>
                    </execution>
               </executions>
          </plugin>
     </plugins>
</build>
View Code

 

這裏也給出Maven POM依賴的簡單配置:

 
<dependencies>
     <dependency>
          <groupId>org.shirdrn.dubbo</groupId>
          <artifactId>dubbo-api</artifactId>
          <version>0.0.1-SNAPSHOT</version>
     </dependency>
</dependencies>

 

咱們開發的服務應該是分佈式的,首先是經過配置內容來決定,例如設置集羣模式、設置負載均衡模式等,而後在部署的時候,能夠在多個節點上同一個服務,這樣多個服務都會註冊到Dubbo註冊中心,若是某個節點上的服務不可用了,能夠根據咱們配置的策略來選擇其餘節點上的可用服務,後面經過Dubbo服務管理中心和監控中心就能更加清楚明瞭。

Dubbo服務管理與監控

咱們須要在安裝好管理中心和監控中心之後,再將上面的開發的提供方服務部署到物理節點上,而後就可以經過管理中心和監控中心來查看對應的詳細狀況。

  • Dubbo服務管理中心

安裝Dubbo服務管理中心,須要選擇一個Web容器,咱們使用Tomcat服務器。首先下載Dubbo管理中心安裝文件dubbo-admin-2.5.3.war,或者直接從源碼構建獲得該WAR文件。這裏,咱們已經構建好對應的WAR文件,而後進行安裝,執行以下命令:

 
cd apache-tomcat-6.0.35
rm -rf webapps/ROOT
unzip ~/dubbo-admin-2.5.3.war -d webapps/ROOT

 

修改配置文件~/apache-tomcat-6.0.35/webapps/ROOT/WEB-INF/dubbo.properties,指定咱們的註冊中心地址以及登陸密碼,內容以下所示:

 
dubbo.registry.address=zookeeper://zk1:2181?backup=zk2:2181,zk3:2181
dubbo.admin.root.password=root
dubbo.admin.guest.password=guest

 

而後,根據須要修改~/apache-tomcat-6.0.35/conf/server.xml配置文件,主要是Tomcat HTTP 端口號(我這裏使用8083端口),完成後能夠直接啓動Tomcat服務器:

 
cd ~/apache-tomcat-6.0.35/
bin/catalina.sh start

 

而後訪問地址http://10.10.4.130:8083/便可,根據配置文件指定的root用戶密碼,就能夠登陸Dubbo管理控制檯。
咱們將上面開發的服務提供方服務,部署到2個獨立的節點上(192.168.14.1和10.10.4.125),而後能夠經過Dubbo管理中心查看對應服務的情況,如圖所示:

上圖中能夠看出,該服務有兩個獨立的節點能夠提供,由於配置的集羣模式爲failover,若是某個節點的服務發生故障沒法使用,則會自動透明地重試另外一個節點上的服務,這樣就不至於出現拒絕服務的狀況。若是想要查看提供方某個節點上的服務詳情,能夠點擊對應的IP:Port連接,示例如圖所示:

上圖能夠看到服務地址: 

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自帶的簡易監控中心文件dubbo-monitor-simple-2.5.3-assembly.tar.gz,能夠解壓縮之後,修改配置文件~/dubbo-monitor-simple-2.5.3/conf/dubbo.properties的內容,以下所示:

 
dubbo.container=log4j,spring,registry,jetty
dubbo.application.name=simple-monitor
dubbo.application.owner=
dubbo.registry.address=zookeeper://zk1:2181?backup=zk2:2181,zk3:2181
dubbo.protocol.port=7070
dubbo.jetty.port=8087
dubbo.jetty.directory=${user.home}/monitor
dubbo.charts.directory=${dubbo.jetty.directory}/charts
dubbo.statistics.directory=${user.home}/monitor/statistics
dubbo.log4j.file=logs/dubbo-monitor-simple.log
dubbo.log4j.level=WARN

 

而後啓動簡易監控中心,執行以下命令:

 
cd ~/dubbo-monitor-simple-2.5.3
bin/start.sh

 

這裏使用了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接口定義以下所示:

 
/**
* MonitorFactory. (SPI, Singleton, ThreadSafe)
*
* @author william.liangf
*/
@SPI("dubbo")
public interface MonitorFactory {
    
    /**
     * Create monitor.
     * @param url
     * @return monitor
     */
    @Adaptive("protocol")
    Monitor getMonitor(URL url);
 
}

 

Monitor接口定義以下所示:

 
/**
* Monitor. (SPI, Prototype, ThreadSafe)
*
* @see com.alibaba.dubbo.monitor.MonitorFactory#getMonitor(com.alibaba.dubbo.common.URL)
* @author william.liangf
*/
public interface Monitor extends Node, MonitorService {
 
}

 

具體定義內容能夠查看MonitorService接口,再也不累述。

總結

Dubbo還提供了其餘不少高級特性,如路由規則、參數回調、服務分組、服務降級等等,並且不少特性在給出內置實現的基礎上,還給出了擴展的接口,咱們能夠給出自定義的實現,很是方便並且強大。更多能夠參考Dubbo官網用戶手冊和開發手冊。  

參考:基於Dubbo框架構建分佈式服務

相關文章
相關標籤/搜索