1. 程式人生 > >dubbo是什麼,很好的介紹

dubbo是什麼,很好的介紹

Dubbo是Alibaba開源的分散式服務框架,我們可以非常容易地通過Dubbo來構建分散式服務,並根據自己實際業務應用場景來選擇合適的叢集容錯模式,這個對於很多應用都是迫切希望的,只需要通過簡單的配置就能夠實現分散式服務呼叫,也就是說服務提供方(Provider)釋出的服務可以天然就是叢集服務,比如,在實時性要求很高的應用場景下,可能希望來自消費方(Consumer)的呼叫響應時間最短,只需要選擇Dubbo的Forking Cluster模式配置,就可以對一個呼叫請求並行傳送到多臺對等的提供方(Provider)服務所在的節點上,只選擇最快一個返回響應的,然後將呼叫結果返回給服務消費方(Consumer),顯然這種方式是以冗餘服務為基礎的,需要消耗更多的資源,但是能夠滿足高實時應用的需求。 有關Dubbo服務框架的簡單使用,可以參考我的其他兩篇文章(《基於Dubbo的Hessian協議實現遠端呼叫》,《Dubbo實現RPC呼叫使用入門》,後面參考連結中已給出連結),這裡主要圍繞Dubbo分散式服務相關配置的使用來說明與實踐。

Dubbo服務叢集容錯

假設我們使用的是單機模式的Dubbo服務,如果在服務提供方(Provider)釋出服務以後,服務消費方(Consumer)發出一次呼叫請求,恰好這次由於網路問題呼叫失敗,那麼我們可以配置服務消費方重試策略,可能消費方第二次重試呼叫是成功的(重試策略只需要配置即可,重試過程是透明的);但是,如果服務提供方釋出服務所在的節點發生故障,那麼消費方再怎麼重試呼叫都是失敗的,所以我們需要採用叢集容錯模式,這樣如果單個服務節點因故障無法提供服務,還可以根據配置的叢集容錯模式,呼叫其他可用的服務節點,這就提高了服務的可用性。 首先,根據Dubbo文件,我們引用文件提供的一個架構圖以及各元件關係說明,如下所示:dubbo-cluster-architecture

上述各個元件之間的關係(引自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

2

3

4

<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配置的屬性值。

  • 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

02

03

04

05

06

07

08

09

10

11

12

13

14

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

}

關於如何實現一個自定義的叢集容錯模式,可以參考Dubbo原始碼中內建支援的汲取你容錯模式的實現,6種模式對應的實現類如下所示:

1

2

3

4

5

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模式為例開發我們的分散式應用,再進行詳細的介紹。

Dubbo服務負載均衡

Dubbo框架內建提供負載均衡的功能以及擴充套件介面,我們可以透明地擴充套件一個服務或服務叢集,根據需要非常容易地增加/移除節點,提高服務的可伸縮性。Dubbo框架內建提供了4種負載均衡策略,如下所示:

  • Random LoadBalance:隨機策略,配置值為random。可以設定權重,有利於充分利用伺服器的資源,高配的可以設定權重大一些,低配的可以稍微小一些
  • RoundRobin LoadBalance:輪詢策略,配置值為roundrobin。
  • LeastActive LoadBalance:配置值為leastactive。根據請求呼叫的次數計數,處理請求更慢的節點會受到更少的請求
  • ConsistentHash LoadBalance:一致性Hash策略,具體配置方法可以參考Dubbo文件。相同呼叫引數的請求會發送到同一個服務提供方節點上,如果某個節點發生故障無法提供服務,則會基於一致性Hash演算法對映到虛擬節點上(其他服務提供方)

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

1

2

3

4

5

<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介面,介面定義如下所示:

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

/**

* LoadBalance. (SPI, Singleton, ThreadSafe)

*

*

* @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;

}

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

1

2

3

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

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

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

Dubbo服務叢集容錯實踐

手機應用是以聊天室為基礎的,我們需要收集使用者的操作行為,然後計算聊天室中線上人數,並實時在手機應用端顯示人數,整個系統的架構如圖所示:dubbo-services-architecture 上圖中,主要包括了兩大主要流程:日誌收集並實時處理流程、呼叫讀取實時計算結果流程,我們使用基於Dubbo框架開發的服務來提供實時計算結果讀取聊天人數的功能。上圖中,實際上業務介面伺服器叢集也可以基於Dubbo框架構建服務,就看我們想要構建什麼樣的系統來滿足我們的需要。 如果不使用註冊中心,服務消費方也能夠直接呼叫服務提供方釋出的服務,這樣需要服務提供方將服務地址暴露給服務消費方,而且也無法使用監控中心的功能,這種方式成為直連。 如果我們使用註冊中心,服務提供方將服務釋出到註冊中心,而服務消費方可以通過註冊中心訂閱服務,接收服務提供方服務變更通知,這種方式可以隱藏服務提供方的細節,包括伺服器地址等敏感資訊,而服務消費方只能通過註冊中心來獲取到已註冊的提供方服務,而不能直接跨過註冊中心與服務提供方直接連線。這種方式的好處是還可以使用監控中心服務,能夠對服務的呼叫情況進行監控分析,還能使用Dubbo服務管理中心,方便管理服務,我們在這裡使用的是這種方式,也推薦使用這種方式。使用註冊中心的Dubbo分散式服務相關元件結構,如下圖所示:dubbo-services-internal-architecture

下面,開發部署我們的應用,通過如下4個步驟來完成:

  • 服務介面定義

服務介面將服務提供方(Provider)和服務消費方(Consumer)連線起來,服務提供方實現介面中定義的服務,即給出服務的實現,而服務消費方負責呼叫服務。我們介面中給出了2個方法,一個是實時查詢獲取當前聊天室內人數,另一個是查詢一天中某個/某些聊天室中線上人數峰值,介面定義如下所示:

01

02

03

04

05

06

07

08

09

10

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,程式碼如下所示:

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

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;

}

}

程式碼中通過讀取Redis中資料來完成呼叫,邏輯比較簡單。對應的Maven POM依賴配置,如下所示:

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

<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,內容如下所示:

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

<?xml version="1.0" encoding="UTF-8"?>

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

上面配置中,使用dubbo協議,叢集容錯模式為failover,服務級別負載均衡策略為random,方法級別負載均衡策略為roundrobin(它覆蓋了服務級別的配置內容),其他一些配置內容可以參考Dubbo文件。我們這裡是從Redis讀取資料,所以使用了Redis連線池。 啟動服務示例程式碼如下所示:

01

02

03

04

05

06

07

08

09

10

11

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,如下所示:

01

02

03

04

05

06

07

08

09

10

11

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容器,全部物件都交由容器來管理。

  • 服務消費方

服務消費方就容易了,只需要知道註冊中心地址,並引用服務提供方提供的介面,消費方呼叫服務實現如下所示:

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

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

}

}

對應的配置檔案為consumer.xml,內容如下所示:

也可以根據需要配置dubbo:reference相關的屬性值,也可以配置dubbo:method指定呼叫的方法的配置資訊,詳細配置屬性可以參考Dubbo官方文件。

  • 部署與驗證

開發完成提供方服務後,在本地開發除錯的時候可以怎麼簡單怎麼做,如果是要部署到生產環境,則需要打包後進行部署,可以參考下面的Maven POM配置:

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

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

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

1

2

3

4

5

6

7

<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檔案,然後進行安裝,執行如下命令:

1

2

3

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,指定我們的註冊中心地址以及登入密碼,內容如下所示:

1

2

3

dubbo.admin.root.password=root

dubbo.admin.guest.password=guest

然後,根據需要修改~/apache-tomcat-6.0.35/conf/server.xml配置檔案,主要是Tomcat HTTP 埠號(我這裡使用8083埠),完成後可以直接啟動Tomcat伺服器:

1

2

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管理中心檢視對應服務的狀況,如圖所示:dubbo-admin-providers 上圖中可以看出,該服務有兩個獨立的節點可以提供,因為配置的叢集模式為failover,如果某個節點的服務發生故障無法使用,則會自動透明地重試另一個節點上的服務,這樣就不至於出現拒絕服務的情況。如果想要檢視提供方某個節點上的服務詳情,可以點選對應的IP:Port連結,示例如圖所示:dubbo-admin-providers-detail 上圖可以看到服務地址:

如果我們直接暴露該地址也是可以的,不過這種直連的方式對服務消費方不是透明的,如果以後IP地址更換,也會影響呼叫方,所以最好是通過註冊中心來隱蔽服務地址。同一個服務所部署在的多個節點上,也就對應對應著多個服務地址。另外,也可以對已經發布的服務進行控制,如修改訪問控制、負載均衡相關配置內容等,可以通過上圖中“消費者”檢視服務消費方呼叫服務的情況,如圖所示:dubbo-admin-consumers 也在管理控制檯可以對消費方進行管理控制。

  • Dubbo監控中心

Dubbo監控中心是以Dubbo服務的形式釋出到註冊中心,和普通的服務時一樣的。例如,我這裡下載了Dubbo自帶的簡易監控中心檔案dubbo-monitor-simple-2.5.3-assembly.tar.gz,可以解壓縮以後,修改配置檔案~/dubbo-monitor-simple-2.5.3/conf/dubbo.properties的內容,如下所示:

01

02

03

04

05

06

07

08

09

10

11

dubbo.container=log4j,spring,registry,jetty

dubbo.application.name=simple-monitor

dubbo.application.owner=

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

然後啟動簡易監控中心,執行如下命令:

1

2

cd ~/dubbo-monitor-simple-2.5.3

bin/start.sh

這裡使用了Jetty Web容器,訪問地址http://10.10.4.130:8087/就可以檢視監控中心,Applications選項卡頁面包含了服務提供方和消費方的基本資訊,如圖所示:dubbo-monitor-applications 上圖主要列出了所有提供方釋出的服務、消費方呼叫、服務依賴關係等內容。 接著,檢視Services選項卡頁面,包含了服務提供方提供的服務列表,如圖所示:dubbo-monitor-services 點選上圖中Providers連結就能看到服務提供方的基本資訊,包括服務地址等,如圖所示:dubbo-monitor-providers 點選上圖中Consumers連結就能看到服務消費方的基本資訊,包括服務地址等,如圖所示:dubbo-monitor-consumers 由於上面是Dubbo自帶的一個簡易監控中心,可能所展現的內容並不能滿足我們的需要,所以可以根據需要開發自己的監控中心。Dubbo也提供了監控中心的擴充套件介面,如果想要實現自己的監控中心,可以實現介面com.alibaba.dubbo.monitor.MonitorFactory和com.alibaba.dubbo.monitor.Monitor,其中MonitorFactory介面定義如下所示:

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

/**

* 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介面定義如下所示:

1

2

3

4

5

6

7

8

9

/**

* 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使用Maven構建依賴配置

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

<properties>

<spring.version>3.2.8.RELEASE</spring.version>

<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

</properties>

<dependencies>

<dependency>

<groupId>com.alibaba</groupId>

<artifactId>dubbo</artifactId>

<version>2.5.3</version>

<exclusions>

<exclusion>

<groupId>org.springframework</groupId>

<artifactId>spring</artifactId>

</exclusion>

<exclusion>

<groupId>org.apache.zookeeper</groupId>

<artifactId>zookeeper</artifactId>

</exclusion>

<exclusion>

<groupId>org.jboss.netty</groupId>

<artifactId>netty</artifactId>

</exclusion>

</exclusions>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-core</artifactId>

<version>${spring.version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-beans</artifactId>

<version>${spring.version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-context</artifactId>

<version>${spring.version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-context-support</artifactId>

<version>${spring.version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-web</artifactId>

<version>${spring.version}</version>

</dependency>

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-api</artifactId>

<version>1.6.2</version>

</dependency>

<dependency>

<groupId>log4j</groupId>

<artifactId>log4j</artifactId>

<version>1.2.16</version>

</dependency>

<dependency>

<groupId>org.javassist</groupId>

<artifactId>javassist</artifactId>

<version>3.15.0-GA</version>

</dependency>

<dependency>

<groupId>com.alibaba</groupId>

<artifactId>hessian-lite</artifactId>

<version>3.2.1-fixed-2</version>

</dependency>

<dependency>

<groupId>com.alibaba</groupId>

<artifactId>fastjson</artifactId>

<version<