Spring cloud適應於雲端服務,也適用於企業信息化SOA建設。spring boot也是restful微服務開發的利器。但對於內網服務,即服務與服務之間的調用,spring並無去刻意封裝,也許他們認爲已經沒有必要了,由於已經有了thrift、ice等強大的框架。java
若是是用spring boot自己提供的restful服務做爲服務與服務之間的調用,效率低不少,thrift的效率大概是restful的100-1000倍左右。本篇既是基於spring boot框架,結合thrift和zookeeper實現的一個簡單微服務框架,服務與服務之間使用thrift通訊(thrift既是通訊方式也是數據壓縮方式)。git
本demo一共包括三個工程:spring
cloud-thrift-server:服務提供方服務器
cloud-thrift-interface:接口及傳輸對象定義restful
cloud-thrift-client:服務調用方負載均衡
開源代碼地址:http://git.oschina.net/zhou666/spring-cloud-7simple框架
1)創建thrift接口定義文檔dom
namespace java cloud.simple.serviceide
struct UserDto {微服務
1: i32 id
2: string username
}
service UserService {
UserDto getUser()
}
接口定義完後,使用thrift命令生成對應的java文件,主要生成兩個文件,分別是UserService.java和UserDto.java,把這兩個文件放入cloud-thrift-interface工程,由於客戶端也須要這個接口定義。
2)實現thrift服務註冊
在服務的提供端須要實現接口,而且還要把實現類註冊到thrift服務器。
UserService.Processor processor = new UserService.Processor(
new UserServiceImpl());
TServer server = new TThreadPoolServer(new TThreadPoolServer.Args(
tServerTransport()).processor(processor));
UserServiceImpl就是接口實現類,將其註冊金Tserver。
註冊完服務後,須要啓動Tserver,很顯然這個須要在線程裏啓動。
executor.execute(new Runnable() {
@Override
public void run() {
tServer().serve();
}
});
3) 使用zookeeper進行服務名稱註冊
上面是註冊具體的服務執行類,這一步是將服務的實例註冊進zookeeper,這樣才能實現負載均衡。讓客戶端能夠根據服務實例列表選擇服務來執行。固然這裏只須要註冊服務所在服務器的IP便可,由於客戶端只要知道IP,也就知道訪問那個IP下的該服務。
String servicePath = "/"+serviceName ;// 根節點路徑
ZkClient zkClient = new ZkClient(serverList);
boolean rootExists = zkClient.exists(servicePath);
if (!rootExists) {
zkClient.createPersistent(servicePath);
}
InetAddress addr = null;
try {
addr = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
String ip = addr.getHostAddress().toString();
String serviceInstance = System.nanoTime() +"-"+ ip;
// 註冊當前服務
zkClient.createEphemeral(servicePath + "/" + serviceInstance);
System.out.println("提供的服務爲:" + servicePath + "/" + serviceInstance);
要注意這裏使用zkClient.createEphemeral創建臨時節點,若是這臺服務器宕機,這個臨時節點是會被清除的,這樣客戶端在訪問時就不會再選擇該服務器上的服務。
4) 客戶端更新服務列表
客戶端須要能及時的監聽服務列表的變化並做出負載均衡,咱們用以下方式監聽服務列表的變化:
// 註冊事件監聽
zkClient.subscribeChildChanges(servicePath, new IZkChildListener() {
// @Override
public void handleChildChange(String parentPath,
List<String> currentChilds) throws Exception {
// 實例(path)列表:當某個服務實例宕機,實例列表內會減去該實例
for (String instanceName : currentChilds) {
// 沒有該服務,創建該服務
if (!serviceMap.containsKey(instanceName)) {
serviceMap.put(instanceName,createUserService(instanceName));
}
}
for (Map.Entry<String, UserService.Client> entry : serviceMap.entrySet()) {
// 該服務已被移除
if (!currentChilds.contains(entry.getKey())) {
serviceMap.remove(entry.getKey());
}
}
System.out.println(parentPath + "事件觸發");
}
});
有了服務列表,客戶端在調用服務的時候就能夠採用負載均衡的方式了,在這裏使用最簡單的隨機方式:
public UserService.Client getBalanceUserService(){
Map<String, UserService.Client> serviceMap =ZooKeeperConfig.serviceMap;
//以負載均衡的方式獲取服務實例
for (Map.Entry<String, UserService.Client> entry : serviceMap.entrySet()) {
System.out.println("可供選擇服務:"+entry.getKey());
}
int rand=new Random().nextInt(serviceMap.size());
String[] mkeys = serviceMap.keySet().toArray(new String[serviceMap.size()]);
return serviceMap.get(mkeys[rand]);
}
本文結束,具體參見代碼,另外,以前還不瞭解thrift和zookeeper的朋友,不要被他們嚇到,其實他們是很輕量級的技術,很容易上手,這也許就是他們流行的緣由。