Dubbo服務註冊與發現
目錄
- 一、分散式基本理論
- 1.1、分散式基本定義
- 1.2 架構發展演變
- 1.3、RPC簡介
- 二、Dubbo理論簡介
- 三、Dubbo環境搭建
- 3.1 Zookeeper搭建
- 3.2 Dubbo管理頁面搭建
- 四、Dubbo服務註冊發現例子
- 4.1、業務場景
- 4.2、api工程建立
- 4.3、服務提供者工程
- 4.4、服務消費者工程
@
一、分散式基本理論
1.1、分散式基本定義
《分散式系統原理與範型》定義:
“分散式系統是若干獨立計算機的集合,這些計算機對於使用者來說就像單個相關係統”
分散式系統(distributed system)是建立在網路之上的軟體系統。
1.2 架構發展演變
架構的發展是由最初的單一應用架構構建的,一般就是ORM框架方便資料庫操作。
不過隨著系統越來越複雜,單一應用架構會變得難以維護,所以架構逐漸演變出了垂直應用架構,所謂垂直應用架構其實就是安裝業務模板進行拆分,比如可以安裝業務將一個電商系統分為訂單模組,使用者資訊管理模組,商品管理模組等等,這時候MVC框架就派上用場,MVC框架可以協助系統更好的按業務拆分,不過業務拆分後雖然是比單一應用架構更好維護了。
不過隨著系統越來約複雜,發現很多共用的模組很難複用起來,這時候分散式服務架構登場了,分散式架構是將一些核心業務抽取出來,作為獨立的服務,逐漸形成穩定的服務中心,當應用需要時,就去服務中心調服務就可以,而實現這種服務註冊的肯定是RPC框架了。
當服務越來越多,容量的評估,小服務資源的浪費等問題逐漸顯現,此時需增加一個排程中心基於訪問壓力實時管理叢集容量,提高叢集利用率,這時候就需要流動計算架構(SOA)[ Service Oriented Architecture],用於提高機器利用率的資源排程,SOA是一個治理中心,綜上所述,到目前,軟體系統架構演變經歷了:單一應用架構->垂直應用架構->分散式應用架構->流動計算架構,下面Dubbo官網的圖片可以很好的描述
1.3、RPC簡介
RPC概念
RPC【Remote Procedure Call】是指遠端過程呼叫,是一種程序間通訊方式,他是一種技術的思想,而不是規範。它允許程式呼叫另一個地址空間(通常是共享網路的另一臺機器上)的過程或函式,而不用程式設計師顯式編碼這個遠端呼叫的細節。
RPC核心模組
RPC有兩個核心模組:通訊和序列化
二、Dubbo理論簡介
Apache Dubbo (incubating) |ˈdʌbəʊ| 是一款高效能、輕量級的開源Java RPC框架,它提供了三大核心能力:面向介面的遠端方法呼叫,智慧容錯和負載均衡,以及服務自動註冊和發現。
官網:
http://dubbo.apache.org/
Dubbo的服務治理:
Dubbo原理圖片,圖片來自Dubbo官網:
Dubbo角色:
Provider:暴露服務的服務提供者
Container:服務執行的容器
Consumer:呼叫遠端服務的消費者
Registry:服務註冊和發現的註冊中心
Minitor:統計服務呼叫次數和時間的監控中心
呼叫過程:
下面根據我的理解說明一下
0:伺服器容器負責啟動、載入、執行服務提供者
1:服務提供者在啟動後就可以向註冊中心暴露服務
2:服務消費者在啟動後就可以向註冊中心訂閱想要的服務
3:註冊中心向服務消費者返回服務呼叫列表
4:服務消費者基於軟負載均衡演算法呼叫服務提供者的服務,這個服務提供者有可能是一個服務提供者列表,呼叫那個服務提供者就是根據負載均衡來呼叫了
5:服務提供者和服務消費者定時將儲存在記憶體中的服務呼叫次數和服務呼叫時間推送給監控中心
三、Dubbo環境搭建
3.1 Zookeeper搭建
搭建Zookeeper,首先是搭建分散式架構的註冊中心Zookeeper,當然也可以用Redis等等來做服務註冊中心,不過本部落格只介紹Zookeeper的,因為沒有linux伺服器,所以只介紹window版的搭建
1、下載Zookeeper:
網址 https://archive.apache.org/dist/zookeeper/zookeeper-3.4.13/- 2、解壓Zookeeper
解壓Zookeeper之後,執行bin目錄裡的zkServer.cmd,發現報錯了,提示找不到配置檔案,所以需要繼續步驟3 3、配置Zookeeper
因為Zookeeper的conf資料夾下面只提供zoo_sample.cfg檔案,需要自己修改命名為zoo.cfg
對於配置檔案需要注意:
dataDir=./ 臨時資料儲存的目錄(可寫相對路徑)
clientPort=2181 zookeeper的埠號
- 4、使用zkCli.cmd測試
修改配置檔案後,重新啟動zkServer.cmd,啟動bin目錄下面的zkCli.cmd,很顯然這是個客戶端程式,注意zkServer.cmd是服務端程式,必須啟動
ok,簡單在zkCli.cmd敲幾個命令測試一下:
ls /:列出zookeeper根下儲存的所有節點
create –e /testNode 12345678:建立一個testNode節點,值為12345678
get /testNode:獲取/testNode節點的值
3.2 Dubbo管理頁面搭建
搭建了服務註冊中心後,就需要搭建Dubbo-admin了,最近看了一下,dubbo的Github專案已經進行了更新,管理平臺已經做了比較大的改動,而我學習的時候,平臺是比較簡單的,所以本dubbo-admin搭建是以舊版master的為準,不過以學習為目的的,只需要知道具體原理和操作技巧就可以
- 下載dubbo-admin
去下載一下dubbo-admin,可以找主幹master分支的,找到dubbo-admin,git clone到本地
https://github.com/apache/incubator-dubbo-ops
因為我搭建時候(ps:不是部落格寫作時間),dubbo還沒做比較大改動,所以我以比較舊的版本為例子,現在新的具體參考dubbo官方的教程,本部落格只是做記錄
- 修改dubbo-admin
修改 src\main\resources\application.properties 指定zookeeper地址
- Maven package dubbo-admin
mvn clean package -Dmaven.test.skip=true
- 執行dubbo-admin的jar
maven打包之後,就去target裡找到jar,然後cmd執行
java -jar dubbo-admin-0.0.1-SNAPSHOT.jar
執行成功之後,訪問: http://127.0.0.1:7001,輸入預設的賬號密碼root/root,登入成功
四、Dubbo服務註冊發現例子
經典例子:
4.1、業務場景
某個電商系統,訂單服務需要呼叫使用者服務獲取某個使用者的所有地址;
我們現在 需要建立兩個服務模組進行測試
模組 | 功能 |
---|---|
訂單服務模組 | 建立訂單等 |
使用者服務模組 | 查詢使用者地址等 |
- 測試預期結果:
訂單服務web模組在A伺服器,使用者服務模組在B伺服器,A可以遠端呼叫B的功能
4.2、api工程建立
建立工程:
建議將服務介面,服務模型,服務異常等均放在 API 包中,因為服務模型及異常也是 API 的一部分,同時,這樣做也符合分包原則:重用釋出等價原則(REP),共同重用原則(CRP)。
建立一個API工程,將實體類和介面都放在api工程
maven新建一個shop-api-common工程:
使用者地址DTO類:
package com.test.dubbo.bean;
import java.io.Serializable;
public class UserAddress implements Serializable {
private Integer id;
private String userAddress; //使用者地址
private String userId; //使用者id
private String consignee; //收貨人
private String phoneNum; //電話號碼
private String isDefault; //是否為預設地址 Y-是 N-否
public UserAddress() {
super();
}
public UserAddress(Integer id, String userAddress, String userId, String consignee, String phoneNum,
String isDefault) {
super();
this.id = id;
this.userAddress = userAddress;
this.userId = userId;
this.consignee = consignee;
this.phoneNum = phoneNum;
this.isDefault = isDefault;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserAddress() {
return userAddress;
}
public void setUserAddress(String userAddress) {
this.userAddress = userAddress;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getConsignee() {
return consignee;
}
public void setConsignee(String consignee) {
this.consignee = consignee;
}
public String getPhoneNum() {
return phoneNum;
}
public void setPhoneNum(String phoneNum) {
this.phoneNum = phoneNum;
}
public String getIsDefault() {
return isDefault;
}
public void setIsDefault(String isDefault) {
this.isDefault = isDefault;
}
}
使用者資訊服務介面:
package com.test.dubbo.service;
import java.util.List;
import com.test.dubbo.bean.UserAddress;
/**
* 使用者服務
*/
public interface UserService {
/**
* 按照使用者id返回所有的收貨地址
* @param userId
* @return
*/
public List<UserAddress> getUserAddressList(String userId);
}
訂單資訊服務介面:
package com.test.dubbo.service;
import java.util.List;
import com.test.dubbo.bean.UserAddress;
public interface OrderService {
/**
* 初始化訂單
* @param userId
*/
public List<UserAddress> initOrder(String userId);
}
ok,建立好api工程
4.3、服務提供者工程
要實現服務提供,配置檔案主要需要配置如下:
Dubbo提供者載入過程(Dubbo容器的啟動):
Spring載入xml配置之後暴露服務的過程:
Exporter方法主要是開啟socket的監聽,接收客戶的請求
ok,理解了上面的理論知識後,繼續建立一個user-service-provider工程:
- maven配置:
<!-- 引入dubbo -->
<!-- https://mvnrepository.com/artifact/com.alibaba/dubbo -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.2</version>
</dependency>
<!-- 註冊中心使用的是zookeeper,引入操作zookeeper的客戶端端 -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.12.0</version>
</dependency>
- resources加一個提供者的配置檔案provider.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 1、指定當前服務/應用的名字(同樣的服務名字相同,不要和別的服務同名) -->
<dubbo:application name="user-service-provider"></dubbo:application>
<!-- 2、指定註冊中心的位置 -->
<!-- <dubbo:registry address="zookeeper://127.0.0.1:2181"></dubbo:registry> -->
<dubbo:registry protocol="zookeeper" address="127.0.0.1:2181"></dubbo:registry>
<!-- 3、指定通訊規則(通訊協議 通訊埠) -->
<dubbo:protocol name="dubbo" port="20882"></dubbo:protocol>
<!-- 4、暴露服務 ref:指向服務的真正的實現物件 -->
<dubbo:service interface="com.test.dubbo.service.UserService"
ref="userServiceImpl01" timeout="1000" version="1.0.0">
<dubbo:method name="getUserAddressList" timeout="1000"></dubbo:method>
</dubbo:service>
<!--統一設定服務提供方的規則 -->
<dubbo:provider timeout="1000"></dubbo:provider>
<!-- 服務的實現 -->
<bean id="userServiceImpl01" class="com.test.dubbo.service.impl.UserServiceImpl"></bean>
<!-- 連線監控中心 -->
<dubbo:monitor protocol="registry"></dubbo:monitor>
</beans>
- 加個業務實現類:
package com.test.dubbo.service.impl;
import java.util.Arrays;
import java.util.List;
import com.test.dubbo.bean.UserAddress;
import com.test.dubbo.service.UserService;
public class UserServiceImpl implements UserService {
//@Override
public List<UserAddress> getUserAddressList(String userId) {
UserAddress address1 = new UserAddress(1, "北京市昌平區", "1", "李老師", "010-56253825", "Y");
UserAddress address2 = new UserAddress(2, "深圳市寶安區", "1", "王老師", "010-56253825", "N");
return Arrays.asList(address1,address2);
}
}
- 啟動服務提供者,註冊到Zookeeper:
啟動服務提供者有兩種方法,一種是IOC啟動:
package com.test.dubbo;
import java.io.IOException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApplication {
public static void main(String[] args) throws IOException {
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("provider.xml");
ioc.start();
System.in.read();
}
}
另外一種是調dubbo的Main類,啟動:
dubbo的main函式:
com.alibaba.dubbo.container.Main
com.alibaba.dubbo.container.spring.SpringContainer
服務註冊成功,可以去dubbo-admin看
檢視服務介面的詳細資訊:
4.4、服務消費者工程
然後服務已經註冊了,現在建立一個消費者工程order-service-comsumer
- maven加上配置
<!-- 引入dubbo -->
<!-- https://mvnrepository.com/artifact/com.alibaba/dubbo -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.2</version>
</dependency>
<!-- 註冊中心使用的是zookeeper,引入操作zookeeper的客戶端端 -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.12.0</version>
</dependency>
- 消費者配置檔案:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<context:component-scan base-package="com.test.dubbo.service.impl"></context:component-scan>
<dubbo:application name="order-service-consumer"></dubbo:application>
<dubbo:registry address="zookeeper://127.0.0.1:2181"></dubbo:registry>
<!-- 配置本地存根-->
<!--宣告需要呼叫的遠端服務的介面;生成遠端服務代理 -->
<!--
1)、精確優先 (方法級優先,介面級次之,全域性配置再次之)
2)、消費者設定優先(如果級別一樣,則消費方優先,提供方次之)
-->
<!-- timeout="0" 預設是1000ms-->
<!-- retries="":重試次數,不包含第一次呼叫,0代表不重試-->
<!-- 冪等(設定重試次數)【查詢、刪除、修改】、非冪等(不能設定重試次數)【新增】 -->
<dubbo:reference interface="com.test.dubbo.service.UserService"
id="userService" timeout="5000" retries="3" version="*">
<!-- <dubbo:method name="getUserAddressList" timeout="1000"></dubbo:method> -->
</dubbo:reference>
<!-- 配置當前消費者的統一規則:所有的服務都不檢查 -->
<dubbo:consumer check="false" timeout="5000"></dubbo:consumer>
<dubbo:monitor protocol="registry"></dubbo:monitor>
<!-- <dubbo:monitor address="127.0.0.1:7070"></dubbo:monitor> -->
</beans>
- 訂單服務類:
package com.test.dubbo.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.test.dubbo.bean.UserAddress;
import com.test.dubbo.service.OrderService;
import com.test.dubbo.service.UserService;
/**
* 讓服務消費者去註冊中心訂閱服務提供者的服務地址
*/
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
UserService userService;
@Override
public List<UserAddress> initOrder(String userId) {
System.out.println("使用者id:"+userId);
//查詢使用者的收貨地址
List<UserAddress> addressList = userService.getUserAddressList(userId);
for (UserAddress userAddress : addressList) {
System.out.println(userAddress.getUserAddress());
}
return addressList;
}
}
- IOC啟動
同樣也是可以ioc啟動,或者Dubbo提供的Main類啟動:
package com.test.dubbo;
import java.io.IOException;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.test.dubbo.service.OrderService;
public class MainApplication {
@SuppressWarnings("resource")
public static void main(String[] args) throws IOException {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("consumer.xml");
OrderService orderService = applicationContext.getBean(OrderService.class);
orderService.initOrder("1");
System.out.println("呼叫成功....");
System.in.read();
}
}
呼叫成功,console列印:
使用者id:1
北京市昌平區
深圳市寶安區
呼叫成功....