概念
wiki對於SOA定義如下:
A service-oriented architecture (SOA) is a design pattern in which application components provide services to other components via a communications protocol, typically over a network. The principles of service-orientation are independent of any vendor, product or technology
從定義上看,可以總結出SOA軟件架構模式的幾個特點:
- 面向服務划分系統--將龐大的業務系統拆分成高內聚的服務單元,每個單元對外提供服務服務能力,服務與服務之間通過相互協作共同實現業務價值
- 松耦合---SOA框架中可以應用多種技術,服務消費方不依賴於服務提供者的技術實現(比如Java服務提供方,Python服務消費者)。雙方可以通過thrift, proto-buffer或者消息隊列等框架來實現消息的互通。
- 系統的可靠性依賴外部網絡特質---傳統的單進程系統拆分成多進程系統之間的相互協作,進程之間通過RPC進行通信,增加了網絡開銷。
SOA系統中,最基礎的單元是服務,那么什么是服務呢?
Service Is a logical representation of a repeatable business activity that has a specified outcome (e.g., check customer credit, provide weather data, consolidate drilling reports). 從定義上看,服務是對業務活動的邏輯表達。服務能力通常使用API接口來進行抽象,形成所謂的"契約",外部模塊通過遵循契約來獲得相應的能力。
概念說完了,那么來聊聊如何去構建一個SOA框架。構建SOA框架需要考慮下面幾個要點
- 服務注冊/發現
- 負載均衡:使用合理的框架或是算法實現流量均勻的負載到集群節點上
- Heatbeat
- 服務監控(Metric, 熔斷機制(比如: 過去一分鍾,http調用失敗率超過60%,判定服務不可用,移出或打標簽))
- RPC框架
服務注冊/發現
服務注冊/發現是實現SOA的重中之重,負載均衡、Heatbeat都是基於這一基礎實現的。我們可以通過使用zookeeper來實現服務的注冊和發現。zookeeper wiki給出的定義如下:
ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services.
從定義上看zookeeper能夠:
- 作為配置信息的存儲服務器
- 命名服務
- 分布式的協調服務
除此之外zookeeper具有其他一些特點:
- 奇數個服務節點(和leader選舉算法有關)
- 提供多種語言的API
- 技術比較成熟
- 使用linux文件存儲結構,樹狀組織
- 使用watch機制push數據節點變更消息
主要使用zookeeper的命名服務去實現服務中心的功能,服務注冊/發現的架構如下所示:
在此架構中有三種角色: 服務提供者,服務注冊中心,服務消費者:
- 服務提供者注冊自己的服務,注冊信息包含系統信息,服務名稱,服務的ip和端口號,服務請求的url, 服務的權重等
- 注冊中心提供注冊服務的中心存儲,和向服務消費者push服務變更通知
- 服務消費者在啟動時獲取所需服務注冊信息(根據系統名稱+服務名稱),將服務注冊信息緩存在本地,監聽服務信息的變更,更新本地的緩存。服務消費者根據本地緩存的服務提供者信息負載,來轉發請求。對服務提供方提供心跳檢測。
心跳檢測
心跳是檢驗服務是否可用必不可少的服務,如果出現問題,將該服務提供者從該服務的提供者列表中移除;反之,則加入到服務的提供者列表中。Heatbeat實現原理比較簡單,啟動后台線程定期的向provier發送http請求,多次連續失敗將Provider從調度列表中移出。
負載均衡
負載可以通過兩種方式實現,一種是通過硬件分流,簡單方便,不過成本較高;
另一種方式是采用軟負載,軟負載的兩種方式:
a,中心控制-軟負載服務器。全局視角,可以得出全局最優解。但是有單點問題存在。方式
b,客戶端控制—客戶端自己選擇特定的service的provider,通過收集provider相關的信息,按照可選的一系列選擇算法,進行工作。好處是更加貼近consumer,能夠做出針對於本機的個性化選擇;問題是,每個選擇都是針對一個consumer進行的,consumer之間互相不知情,容易導致選擇沖突(eg,兩個provider a和b,如果在某一特定時刻,所有的consumer都指定了a,導致a的服務質量較差,所有的consumer感知到這一情況,按照一般算法都會將下一次的請求發給b,此時,所有的請求都積壓在b端,導致b的服務質量較差;然后,下一次又會同時指向a。造成了網絡的震盪和服務資源的浪費)
下面介紹一種簡單的輪詢算法,JAVA實現如下所示:
ServiceInstanceManager---維護ServiceInstance實例類
private final AtomicInteger counter = new AtomicInteger(0);
public String getUrl(){
List list = this.manager.getAvailableProviders();
if(list != null && !list.isEmpty()) {
int tmpCount = this.counter.getAndIncrement();
int index = tmpCount % list.size();
index = index >= 0 ? index:index + list.size();
String url = (String)list.get(index);
logger.debug("Service({}), invoke counter({}), url({})!", new Object[]{this.service, Integer.valueOf(tmpCount), url});
return url;
} else {
throw new LoadBalanceException(String.format("Service(%s) has no available provider!", new Object[]{this.service}));
}
}
RPC
RPC—Remote Procedure Call Protocol,是應用實現進程間調用的一種常用手段。通過指定服務對外的IP地址和端口id,本地計算機能夠訪問到遠端機器的資源。常用的RPC框架包括Java RMI, thrift, Google protobuf等。用戶在選擇不同的RPC框架可以從序列化,性能,語言支持幾個方面去考慮,比如Java RMI只能在java生態圈中使用,無法對接其他語言提供的RPC服務,而thrift在語言支持方面就相當全面,通過編寫thrift描述接口文檔,可以實現不同程序之間的調用。