Dubbo想要個閘道器怎麼辦?試試整合Spring Cloud Gateway
阿新 • • 發佈:2020-06-28
![mark](https://img2020.cnblogs.com/other/1769816/202006/1769816-20200628094613340-740080500.png)
## 一、背景
在微服務架構中 **API閘道器** 非常重要,閘道器作為全域性流量入口並不單單是一個反向路由,更多的是把各個邊緣服務(Web層)的各種共性需求抽取出來放在一個公共的“服務”(閘道器)中實現,例如安全認證、許可權控制、限流熔斷、監控、跨域處理、聚合API文件等公共功能。
在以 **Dubbo** 框架體系來構建的微服務架構下想要增加API閘道器,如果不想自研開發的情況下在目前的開源社群中幾乎沒有找到支援dubbo協議的主流閘道器,但是 Spring Cloud 體系下卻有兩個非常熱門的開源API閘道器可以選擇;本文主要介紹如何通過 `Nacos` 整合 `Spring Cloud Gateway` 與 `Dubbo 服務`。
## 二、傳統 dubbo 架構
dubbo屬於rpc呼叫,所以必須提供一個web層的服務作為http入口給客戶端呼叫,並在上面提供安全認證等基礎功能,而web層前面對接Nginx等反向代理用於統一入口和負載均衡。
>web層一般是根據業務模組來切分的,用於聚合某個業務模組所依賴的各個service服務
![file](https://img2020.cnblogs.com/other/1769816/202006/1769816-20200628094613561-603034254.png)
**PS**:我們能否把上圖中的web層全部整合在一起成為一個API閘道器呢?(不建議這樣做)
因為這樣的web層並沒有實現 **泛化呼叫** 必須引入所有dubbo服務的api依賴,會使得閘道器變得非常不穩定,任何服務的介面變更都需要修改閘道器中的api依賴!
## 三、整合 Srping Cloud Gateway 閘道器
下面就開始聊聊直接拿熱門的 `Srping Cloud Gateway` 來作為dubbo架構體系的閘道器是否可行,首先該API閘道器是屬於 Spring Cloud 體系下的元件之一,要整合dubbo的話需要解決以下問題:
1. 打通註冊中心:spring cloud gateway 需要通過註冊中心發現下游服務,而 dubbo 也需要通過註冊中心實現服務的註冊與發現,如果兩者的註冊中心不能打通的話就會變成雙註冊中心架構就非常複雜了!
2. 協議轉換: gateway 使用http傳輸協議呼叫下游服務,而dubbo服務預設使用的是tcp傳輸協議
>上面提到的第一個問題“打通註冊中心”其實已經不是問題了,目前dubbo支援 `Zookeeper` 與 `Nacos` 兩個註冊中心,而 Spring Cloud 自從把 `@EnableEurekaClient` 改為 `@EnableDiscoveryClient` 之後已經基本上支援所有主流的註冊中心了,本文將使用 `Nacos` 作為註冊中心打通兩者
### 3.1. 方式一
把傳統dubbo架構中的 `Nginx` 替換為 `Spring Cloud Gateway` ,並把 `安全認證` 等共性功能前移至閘道器處實現
![mark](https://img2020.cnblogs.com/other/1769816/202006/1769816-20200628094613871-941364658.png)
> 由於web層服務本身提供的就是http介面,所以閘道器層無需作協議轉換,但是由於 `安全認證` 前移至網關了需要通過網路隔離的手段防止被繞過閘道器直接請求後面的web層
### 3.2. 方式二
dubbo服務本身修改或新增 `rest` 傳輸協議的支援,這樣閘道器就可以通過http傳輸協議與dubbo服務通訊了
> rest傳輸協議:基於標準的Java REST API——JAX-RS 2.0(Java API for RESTful Web Services的簡寫)實現的REST呼叫支援
![mark](https://img2020.cnblogs.com/other/1769816/202006/1769816-20200628094614039-872204925.png)
> 目前版本的dubbo已經支援dubbo、rest、rmi、hessian、http、webservice、thrift、redis等10種傳輸協議了,並且還支援同一個服務同時定義多種協議,例如配置 protocol = { "dubbo", "rest" } 則該服務同時支援 `dubbo` 與 `rest` 兩種傳輸協議
### 3.3. 總結
**方式一** 對比 **方式二** 多了一層web服務所以多了一次網路呼叫開銷,但是優點是各自的職責明確單一,web層可以作為聚合層用於聚合多個service服務的結果經過融合加工一併返回給前端,所以這種架構下能大大減少服務的 **迴圈依賴**
## 四、程式碼實踐
### 依賴環境
* lombok
* jdk 1.8
* Nacos 1.3
* Spring Boot 2.2.8.RELEASE
* Spring Cloud Hoxton.SR5
* Spring Cloud Alibaba 2.2.1.RELEASE
在根目錄的 `pom.xml` 中定義全域性的依賴版本
```xml
```
### 4.1. 建立dubbo-api工程
分別定義兩個api介面
DubboService 使用dubbo協議的服務
```java
public interface DubboService {
String test(String param);
}
```
RestService 使用rest協議的服務
```java
public interface RestService {
String test(String param);
}
```
### 4.2. 建立web-dubbo工程
使用 **方式一** 整合對接閘道器,這裡為了簡化在同一個服務下只使用邏輯分層定義controller層與service層,並沒有做服務拆分
#### 4.2.1. 建立配置
定義 spring boot 配置
```yml
server:
port: 8081
spring:
application:
name: zlt-web-dubbo
main:
allow-bean-definition-overriding: true
cloud:
nacos:
server-addr: 192.168.28.130:8848
username: nacos
password: nacos
```
> `server.port`:配置應用伺服器暴露的埠
>
> `spring.cloud.nacos`:配置 spring cloud 的註冊中心相關引數,nacos 的配置需要改為自己環境所對應
定義 dubbo 配置
```yml
dubbo:
scan:
base-packages: org.zlt.service
protocols:
dubbo:
name: dubbo
port: -1
registry:
address: spring-cloud://localhost
consumer:
timeout: 5000
check: false
retries: 0
cloud:
subscribed-services:
```
> `dubbo.scan.base-packages`:指定 Dubbo 服務實現類的掃描基準包
>
> `dubbo.protocols`:服務暴露的協議配置,其中子屬性 `name` 為協議名稱,`port` 為協議埠( -1 表示自增埠,從 20880 開始)
>
> `dubbo.registry.address`:Dubbo 服務註冊中心配置,其中子屬性 `address` 的值 "spring-cloud://localhost",說明掛載到 Spring Cloud 註冊中心
#### 4.2.2. 建立DubboService的實現類
通過 `protocol = "dubbo"` 指定使用 `dubbo協議` 定義服務
```java
@Service(protocol = "dubbo")
public class DubboServiceImpl implements DubboService {
@Override
public String test(String param) {
return "dubbo service: " + param;
}
}
```
#### 4.2.3. 建立Controller類
使用 `Spring Boot` 的 `@RestController` 註解定義web服務
```java
@RestController
public class WebController {
@Autowired
private DubboService dubboService;
@GetMapping("/test/{p}")
public String test(@PathVariable("p") String param) {
return dubboService.test(param);
}
}
```
### 4.3. 建立rest-dubbo工程
使用 **方式二** 整合對接閘道器,由於該服務是通過dubbo來建立rest服務,所以並不需要使用 Spring Boot 內建應用服務
#### 4.3.1. 建立配置
定義 spring boot 配置
```yml
spring:
application:
name: zlt-rest-dubbo
main:
allow-bean-definition-overriding: true
cloud:
nacos:
server-addr: 192.168.28.130:8848
username: nacos
password: nacos
```
> 因為不使用 Spring Boot 內建的應用服務所以這裡並不需要指定 `server.port`
定義 dubbo 配置
```yml
dubbo:
scan:
base-packages: org.zlt.service
protocols:
dubbo:
name: dubbo
port: -1
rest:
name: rest
port: 8080
server: netty
registry:
address: spring-cloud://localhost
consumer:
timeout: 5000
check: false
retries: 0
cloud:
subscribed-services:
```
> `dubbo.protocols`:配置兩種協議,其中rest協議定義 8080 埠並使用 netty 作為應用伺服器
#### 4.3.2. 建立RestService的實現類
通過 `protocol = "rest"` 指定使用 `rest協議` 定義服務
```java
@Service(protocol = "rest")
@Path("/")
public class RestServiceImpl implements RestService {
@Override
@Path("test/{p}")
@GET
public String test(@PathParam("p") String param) {
return "rest service: " + param;
}
}
```
### 4.4. 建立Spring Cloud Gateway工程
定義 spring boot 配置
```yml
server:
port: 9900
spring:
application:
name: sc-gateway
main:
allow-bean-definition-overriding: true
cloud:
nacos:
server-addr: 192.168.28.130:8848
username: nacos
password: nacos
```
> `server.port`:定義閘道器埠為 9090
定義閘道器配置
```yml
spring:
cloud:
gateway:
discovery:
locator:
lowerCaseServiceId: true
enabled: true
routes:
- id: web
uri: lb://zlt-web-dubbo
predicates:
- Path=/api-web/**
filters:
- StripPrefix=1
- id: rest
uri: lb://zlt-rest-dubbo
predicates:
- Path=/api-rest/**
filters:
- StripPrefix=1
```
分別定義兩個路由策略:
* 路徑 `/api-web/` 為請求 `web-dubbo` 工程
* 路徑 `/api-rest/` 為請求 `rest-dubbo` 工程
### 4.5. 測試
分別啟動:Nacos、sc-gateway、web-dubbo、rest-dubbo 工程,通過閘道器的以下兩個介面分別測試兩種整合方式
1. http://127.0.0.1:9900/api-web/test/abc :請求 `web-dubbo` 工程測試整合方式一
2. http://127.0.0.1:9900/api-rest/test/abc :請求 `rest-dubbo` 工程測試整合方式二
## 五、demo下載
ide需要安裝 `lombok` 外掛
[https://github.com/zlt2000/dubboSpringCloud](https://github.com/zlt2000/dubboSpringCloud)
**掃碼關注有驚喜!**
![file](https://img2020.cnblogs.com/other/1769816/202006/1769816-20200628094614300-1106718