SpringBoot系列之整合Dubbo示例教程
一、分散式基本理論
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工程:
參考Dubbo官方例子
<properties>
<spring-boot.version>2.2.1.RELEASE</spring-boot.version>
<dubbo.version>2.7.5</dubbo.version>
<curator.version>2.12.0</curator.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>${dubbo.version}</version>
</dependency>
<!-- Zookeeper dependencies -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
我用的Springboot版本是2.2.1,dubbo starter版本是2.7.5,啟動之後,發現報錯
Caused by: java.lang.: org.apache.curClassNotFoundExceptionator.framework.CuratorFrame
找不到對應的類,看起來是缺少jar了?通過網上資料搜尋再加上如下配置即可:
<!-- https://mvnrepository.com/artifact/org.apache.curator/curator-framework -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>${curator.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.curator/curator-recipes -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>${curator.version}</version>
</dependency>
curator是Zookeeper配置需要的
maven配置好之後,就可以進行dubbo配置:
#server.port=7010
dubbo.application.name=user-service-provider
dubbo.registry.address=127.0.0.1:2181
dubbo.registry.protocol=zookeeper
dubbo.protocol.name=dubbo
dubbo.protocol.port=20882
dubbo.monitor.protocol=registry
#dubbo.scan.base-packages=com.example.springboot.dubbo
使用者服務類:
package com.example.springboot.dubbo.service.impl;
import com.example.spring.dubbo.bean.UserAddress;
import com.example.spring.dubbo.service.UserService;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Service//暴露服務
@Component
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);
}
}
Springboot的啟動類要加上@EnableDubbo註解:
package com.example.springboot.dubbo;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* <pre>
* Springboot啟動類
* </pre>
*
* @author nicky
* <pre>
* 修改記錄
* 修改後版本: 修改人: 修改日期: 2020年01月05日 修改內容:
* </pre>
*/
@EnableDubbo(scanBasePackages="com.example.springboot.dubbo.service.impl")
@SpringBootApplication
public class UserServiceProviderApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceProviderApplication.class, args);
}
}
啟動Springboot類,然後在監控平臺是可以看到服務註冊成功的
檢視服務介面的詳細資訊:
4.4、服務消費者工程
然後服務已經註冊了,現在建立一個消費者工程order-service-comsumer
- maven加上配置
<properties>
<spring-boot.version>2.2.1.RELEASE</spring-boot.version>
<dubbo.version>2.7.5</dubbo.version>
<curator.version>2.12.0</curator.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>${dubbo.version}</version>
</dependency>
<!-- Zookeeper dependencies -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.curator/curator-framework -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>${curator.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.curator/curator-recipes -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>${curator.version}</version>
</dependency>
<dependency>
<groupId>com.example.springboot</groupId>
<artifactId>shop-api-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
- 消費者配置檔案:
server.port=8081
dubbo.application.name=order-service-consumer
dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.monitor.protocol=registry
- 訂單服務類:
package com.example.springboot.dubbo.service.impl;
import com.example.spring.dubbo.bean.UserAddress;
import com.example.spring.dubbo.service.OrderService;
import com.example.spring.dubbo.service.UserService;
import org.apache.dubbo.config.annotation.Reference;
import org.apache.dubbo.config.annotation.Service;
import java.util.List;
/**
* <pre>
* 訂單服務
* </pre>
*
* @author nicky
* <pre>
* 修改記錄
* 修改後版本: 修改人: 修改日期: 2020年01月05日 修改內容:
* </pre>
*/
@Service
public class OrderServiceImpl implements OrderService{
@Reference(loadbalance="random",timeout=1000) //dubbo直連
UserService userService;
@Override
public List<UserAddress> initOrder(String userId) {
//1、查詢使用者的收貨地址
List<UserAddress> addressList = userService.getUserAddressList(userId);
return addressList;
}
}
- Springboot啟動
package com.example.springboot.dubbo;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* <pre>
* Springboot啟動類
* </pre>
*
* @author nicky
* <pre>
* 修改記錄
* 修改後版本: 修改人: 修改日期: 2020年01月05日 修改內容:
* </pre>
*/
@EnableDubbo(scanBasePackages="com.example.springboot.dubbo.service.impl")
@SpringBootApplication
public class OrderServiceProviderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceProviderApplication.class, args);
}
}
寫一個Controller類進行測試:
package com.example.springboot.dubbo.controller;
import com.example.spring.dubbo.bean.UserAddress;
import com.example.spring.dubbo.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
@Controller
public class OrderController {
@Autowired
OrderService orderService;
@ResponseBody
@RequestMapping("/initOrder")
public List<UserAddress> initOrder(@RequestParam("uid")String userId) {
return orderService.initOrder(userId);
}
}
在監控平臺看,消費者啟動也是可以的
程式碼例子下載:github下載鏈