從0開始學微服務:11 服務發佈和引用的實踐

從0開始學微服務:11 服務發佈和引用的實踐

在專欄第 4 期,我給你講解了服務發佈和引用常見的三種方式:Restful API、XML 配置以及 IDL 文件。今天我將以 XML 配置方式為例,給你講解服務發佈和引用的具體實踐以及可能會遇到的問題.

首先我們一起來看下 XML 配置方式,服務發佈和引用的具體流程是什麼樣的。

XML 配置方式的服務發佈和引用流程

1. 服務提供者定義接口

服務提供者發佈服務之前首先要定義接口,聲明接口名、傳遞參數以及返回值類型,然後把接口打包成 JAR 包發佈出去。

比如下面這段代碼,聲明瞭接口 UserLastStatusService,包含兩個方法 getLastStatusId 和 getLastStatusIds,傳遞參數一個是 long 值、一個是 long 數組,返回值一個是 long 值、一個是 map。

package com.weibo.api.common.status.service;
public interface UserLastStatusService {
* @param uids
* @return
*/
public long getLastStatusId(long uid);
/**
*
* @param uids
* @return

*/
public Map<long> getLastStatusIds(long[] uids);
}
/<long>

2. 服務提供者發佈接口

服務提供者發佈的接口是通過在服務發佈配置文件中定義接口來實現的。

下面我以一個具體的服務發佈配置文件 user-last-status.xml 來給你講解,它定義了要發佈的接口 userLastStatusLocalService,對外暴露的協議是 Motan 協議,端口是 8882。並且針對兩個方法 getLastStatusId 和 getLastStatusIds,通過 requestTimeout="300" 單獨定義了超時時間是 300ms,通過 retries="0" 單獨定義了調用失敗後重試次數為 0,也就是不重試。


<beans> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
">
<service> requestTimeout="50" retries="2" interface="com.weibo.api.common.status.service.UserLastStatusService"
basicService="serviceBasicConfig" export="motan:8882">
<method> retries="0" />
<method> retries="0" />
/<method>/<method>/<service>
/<beans>

然後服務發佈者在進程啟動的時候,會加載配置文件 user-last-status.xml,把接口對外暴露出去。

3. 服務消費者引用接口

服務消費者引用接口是通過在服務引用配置文件中定義要引用的接口,並把包含接口定義的 JAR 包引入到代碼依賴中。

下面我再以一個具體的服務引用配置文件 user-last-status-client.xml 來給你講解,它定義服務消費者引用了接口 commonUserLastStatusService,接口通信協議是 Motan。


<beans> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
">
<protocol>
<basicreferer> protocol="motan" />

<referer> basicReferer="userLastStatusServiceClientBasicConfig" />
/<referer>/<basicreferer>/<beans>

然後服務消費者在進程啟動時,會加載配置文件 user-last-status-client.xml 來完成服務引用。

上面所講的服務發佈和引用流程看似比較簡單,但在實際使用過程中,還是有很多坑的,比如在實際項目中經常會遇到這個問題:一個服務包含了多個接口,可能有上行接口也可能有下行接口,每個接口都有超時控制以及是否重試等配置,如果有多個服務消費者引用這個服務,是不是每個服務消費者都必須在服務引用配置文件中定義?

你可以先思考一下這個問題,聯繫自己的實踐經驗,是否有理想的解決方案呢?

服務發佈和引用的那些坑

根據我的項目經驗,在一個服務被多個服務消費者引用的情況下,由於業務經驗的參差不齊,可能不同的服務消費者對服務的認知水平不一,比如某個服務可能調用超時了,最好可以重試來提供調用成功率。但可能有的服務消費者會忽視這一點,並沒有在服務引用配置文件中配置接口調用超時重試的次數,因此最好是可以在服務發佈的配置文件中預定義好類似超時重試次數,即使服務消費者沒有在服務引用配置文件中定義,也能繼承服務提供者的定義。這就是下面要講的服務發佈預定義配置。

1. 服務發佈預定義配置

以下面的服務發佈配置文件 server.xml 為例,它提供了一個服務 contentSliceRPCService,並且明確了其中三個方法的調用超時時間為 500ms 以及超時重試次數為 3。

<service> basicService="serviceBasicConfig" export="motan:8882" >
<method> retries="3" />
<method> retries="3" />

<method> retries="3" />
/<method>/<method>/<method>/<service>

假設服務引用的配置文件 client.xml 的內容如下,那麼服務消費者就會默認繼承服務發佈配置文件中設置的方法調用的超時時間以及超時重試次數。

<referer>
/<referer>

通過服務發佈預定義配置可以解決多個服務消費者引用服務可能帶來的配置複雜的問題,這樣是不是最優的解決方案呢?

實際上我還遇到過另外一種極端情況,一個服務提供者發佈的服務有上百個方法,並且每個方法都有各自的超時時間、重試次數等信息。服務消費者引用服務時,完全繼承了服務發佈預定義的各項配置。這種情況下,服務提供者所發佈服務的詳細配置信息都需要存儲在註冊中心中,這樣服務消費者才能在實際引用時從服務發佈預定義配置中繼承各種配置。

這裡就存在一種風險,當服務提供者發生節點變更,尤其是在網絡頻繁抖動的情況下,所有的服務消費者都會從註冊中心拉取最新的服務節點信息,就包括了服務發佈配置中預定的各項接口信息,這個信息不加限制的話可能達到 1M 以上,如果同時有上百個服務消費者從註冊中心拉取服務節點信息,在註冊中心機器部署為百兆帶寬的情況下,很有可能會導致網絡帶寬打滿的情況發生。

面對這種情況,最好的辦法是把服務發佈端的詳細服務配置信息轉移到服務引用端,這樣的話註冊中心中就不需要存儲服務提供者發佈的詳細服務配置信息了。這就是下面要講的服務引用定義配置。

2. 服務引用定義配置

以下面的服務發佈配置文件為例,它詳細定義了服務 userInfoService 的各個方法的配置信息,比如超時時間和重試次數等。

<service>
<method>
<method>
<method>
<method>
<method>
<method>
<method>
<method>
<method>
<method>
<method>
<method>
<method>
<method>
<method>
<method>
/<service>

可以像下面一樣,把服務 userInfoService 的詳細配置信息轉移到服務引用配置文件中。

<referer>
<method>
<method>
<method>
<method>
<method>
<method>
<method>
<method>
<method>
<method>
<method>
<method>
<method>
<method>
<method>
<method>
/<referer>

這樣的話,服務發佈配置文件可以簡化為下面這段代碼,是不是信息精簡了許多。

<service>
/<service>

在進行類似的服務詳細信息配置,由服務發佈配置文件遷移到服務引用配置文件的過程時,尤其要注意遷移步驟問題,這就是接下來我要給你講的服務配置升級問題。

3. 服務配置升級

實際項目中,我就經歷過一次服務配置升級的過程。由於引用服務的服務消費者眾多,並且涉及多個部門,升級步驟就顯得異常重要,通常可以按照下面步驟操作。

  • 各個服務消費者在服務引用配置文件中添加服務詳細信息。
  • 服務提供者升級兩臺服務器,在服務發佈配置文件中刪除服務詳細信息,並觀察是否所有的服務消費者引用時都包含服務詳細信息。
  • 如果都包含,說明所有服務消費者均完成升級,那麼服務提供者就可以刪除服務發佈配置中的服務詳細信息。
  • 如果有不包含服務詳細信息的服務消費者,排查出相應的業務方進行升級,直至所有業務方完成升級。

總結

今天我給你介紹了 XML 配置方式的服務發佈和引用的具體流程,簡單來說就是服務提供者定義好接口,並且在服務發佈配置文件中配置要發佈的接口名,在進程啟動時加載服務發佈配置文件就可以對外提供服務了。而服務消費者通過在服務引用配置文件中定義相同的接口名,並且在服務引用配置文件中配置要引用的接口名,在進程啟動時加載服務引用配置文件就可以引用服務了。

在業務具體實踐過程中可能會遇到引用服務的服務消費者眾多,對業務的敏感度參差不齊的問題,所以在服務發佈的時候,最好預定義好接口的各種配置。在服務規模不大,業務比較簡單的時候,這樣做比較合適。但是對於複雜業務,雖然服務發佈時預定義好接口的各種配置,但在引用的服務消費者眾多且同時訪問的時候,可能會引起網絡風暴。這種情況下,比較保險的方式是,把接口的各種配置放在服務引用配置文件裡。

在進行服務配置升級過程時,要考慮好步驟,在所有服務消費者完成升級之前,服務提供者還不能把服務的詳細信息去掉,否則可能會導致沒有升級的服務消費者引用異常。

思考題

如果你在實際項目中採用過 XML 配置的服務發佈和應用方式,是否還遇到過其他問題?你是如何解決的呢?

歡迎你在留言區寫下自己的思考,與我一起討論。


分享到:


相關文章: