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 > |