[TOC]
服務網關是微服務架構中一個不可或缺的部分。通過服務網關統一向外系統提供`REST API`的過程中,除了具備服務路由、均衡負載功能之外,它還具備了權限控制等功能。`Spring Cloud` `Netflix`中的Zuul就擔任了這樣的一個角色,為微服務架構提供了前門保護的作用,同時將權限控制這些較重的非業務邏輯內容遷移到服務路由層面,使得服務集群主體能夠具備更高的可復用性和可測試性。
路由在微服務體系結構的一個組成部分。例如,/可以映射到您的Web應用程序,`/api/users`映射到用戶服務,并將`/api/shop`映射到商店服務。`Zuul`是`Netflix`的基于`JVM`的路由器和服務器端負載均衡器。
**Netflix使用Zuul進行以下操作:**
- 認證
- 洞察
- 壓力測試
- 金絲雀測試
- 動態路由
- 服務遷移
- 負載脫落
- 安全
- 靜態響應處理
- 主動/主動流量管理
`Zuul`的規則引擎允許基本上寫任何JVM語言編寫規則和過濾器,內置`Java`和`Groovy`。
# 什么是服務網關
**服務網關 = 路由轉發 + 過濾器**
1、路由轉發:接收一切外界請求,轉發到后端的微服務上去;
2、過濾器:在服務網關中可以完成一系列的橫切功能,例如權限校驗、限流以及監控等,這些都可以通過過濾器完成(其實路由轉發也是通過過濾器實現的)。
# 為什么需要服務網關
**上述所說的橫切功能(以權限校驗為例)可以寫在三個位置:**
- 每個服務自己實現一遍
- 寫到一個公共的服務中,然后其他所有服務都依賴這個服務
- 寫到服務網關的前置過濾器中,所有請求過來進行權限校驗
**第一種,缺點太明顯,基本不用;**
**第二種,相較于第一點好很多,代碼開發不會冗余,但是有兩個缺點:**
- 由于每個服務引入了這個公共服務,那么相當于在每個服務中都引入了相同的權限校驗的代碼,使得每個服務的jar包大小無故增加了一些,尤其是對于使用docker鏡像進行部署的場景,jar越小越好;
- 由于每個服務都引入了這個公共服務,那么我們后續升級這個服務可能就比較困難,而且公共服務的功能越多,升級就越難,而且假設我們改變了公共服務中的權限校驗的方式,想讓所有的服務都去使用新的權限校驗方式,我們就需要將之前所有的服務都重新引包,編譯部署。
**而服務網關恰好可以解決這樣的問題:**
- 將權限校驗的邏輯寫在網關的過濾器中,后端服務不需要關注權限校驗的代碼,所以服務的jar包中也不會引入權限校驗的邏輯,不會增加jar包大小;
- 如果想修改權限校驗的邏輯,只需要修改網關中的權限校驗過濾器即可,而不需要升級所有已存在的微服務。
**所以,需要服務網關!!!**
# 服務網關技術選型
![服務網關][11]
**引入服務網關后的微服務架構如上,總體包含三部分:服務網關、open-service和service。**
**1、總體流程:**
- 服務網關、open-service和service啟動時注冊到注冊中心上去;
- 用戶請求時直接請求網關,網關做智能路由轉發(包括服務發現,負載均衡)到open-service,這其中包含權限校驗、監控、限流等操作
- open-service聚合內部service響應,返回給網關,網關再返回給用戶
**2、引入網關的注意點**
- 增加了網關,多了一層轉發(原本用戶請求直接訪問open-service即可),性能會下降一些(但是下降不大,通常,網關機器性能會很好,而且網關與open-service的訪問通常是內網訪問,速度很快);
- 網關的單點問題:在整個網絡調用過程中,一定會有一個單點,可能是網關、nginx、dns服務器等。防止網關單點,可以在網關層前邊再掛一臺nginx,nginx的性能極高,基本不會掛,這樣之后,網關服務就可以不斷的添加機器。但是這樣一個請求就轉發了兩次,所以最好的方式是網關單點服務部署在一臺牛逼的機器上(通過壓測來估算機器的配置),而且nginx與zuul的性能比較,根據國外的一個哥們兒做的實驗來看,其實相差不大,zuul是netflix開源的一個用來做網關的開源框架;
- 網關要盡量輕。
**3、服務網關基本功能**
- 智能路由:接收外部一切請求,并轉發到后端的對外服務open-service上去;
- 注意:我們只轉發外部請求,服務之間的請求不走網關,這就表示全鏈路追蹤、內部服務API監控、內部服務之間調用的容錯、智能路由不能在網關完成;當然,也可以將所有的服務調用都走網關,那么幾乎所有的功能都可以集成到網關中,但是這樣的話,網關的壓力會很大,不堪重負。
- 權限校驗:只校驗用戶向open-service服務的請求,不校驗服務內部的請求。服務內部的請求有必要校驗嗎?
- API監控:只監控經過網關的請求,以及網關本身的一些性能指標(例如,gc等);
- 限流:與監控配合,進行限流操作;
- API日志統一收集:類似于一個aspect切面,記錄接口的進入和出去時的相關日志
- 。。。后續補充
**4、技術選型**
筆者準備自建一個輕量級的服務網關,技術選型如下:
- 開發語言:java + groovy,groovy的好處是網關服務不需要重啟就可以動態的添加filter來實現一些功能;
- 微服務基礎框架:springboot;
- 網關基礎組件:netflix zuul;
- 服務注冊中心:consul;
- 權限校驗:jwt;
- API監控:prometheus + grafana;
- API統一日志收集:logback + ELK;
- 壓力測試:Jmeter;
- 。。。后續補充
- 在后續的介紹中,會逐漸介紹各個知識點,并完成一個輕量級的服務網關!!!
# Spring Cloud Zuul
# 簡單使用
**新建項目** `spring-cloud-zuul-service`
## 添加依賴
```xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
```
## 開啟服務注冊
在程序的啟動類 `ZuulApplication` 通過 `@EnableZuulProxy` 開啟 Zuul 服務網關
```java
package io.ymq.example.zuul;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@EnableZuulProxy
@SpringBootApplication
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
```
## 添加配置
配置文件 `application.yml`
```sh
spring:
application:
name: zuul-service
server:
port: 9000
zuul:
routes:
blog:
path: /baidu/**
url: https://www.baidu.com
```
## 測試訪問
**配置說明:**
瀏覽器訪問:[http://127.0.0.1:9000/baidu](http://127.0.0.1:9000/baidu) 重定向到百度
# 服務轉發
## 準備工作
我們先拿之前兩篇文章,構建的兩個微服務代碼為基礎,進行下面的操作
**建議先閱讀以下兩篇文章**
[Spring Cloud(四) 服務提供者 Eureka + 服務消費者 Feign ](http://www.ymq.io/2017/12/06/spring-cloud-feign/)
[Spring Cloud(三) 服務提供者 Eureka + 服務消費者(rest + Ribbon)](http://www.ymq.io/2017/12/05/spring-cloud-ribbon-rest/)
[http://www.ymq.io/2017/12/06/spring-cloud-feign/](http://www.ymq.io/2017/12/06/spring-cloud-feign/)
[http://www.ymq.io/2017/12/05/spring-cloud-ribbon-rest/](http://www.ymq.io/2017/12/05/spring-cloud-ribbon-rest/)
## Eureka Service
**導入第三篇文章中的項目:作為服務注冊中心**
`spring-cloud-eureka-service`
## Eureka Provider
**導入第三篇文章中的項目:作為服務的提供者**
`spring-cloud-eureka-provider-1`
`spring-cloud-eureka-provider-2`
`spring-cloud-eureka-provider-3`
## 添加依賴
項目繼續改造,添加依賴
```xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
```
## 修改配置
配置文件 `application.yml`
```sh
spring:
application:
name: zuul-service
server:
port: 9000
#zuul:
# routes:
# blog:
# path: /baidu/**
# url: http://www.baidu.com
eureka:
client:
serviceUrl:
defaultZone: https://localhost:8761/eureka/
zuul:
routes:
api:
path: /**
serviceId: eureka-provider
```
**配置說明:**
瀏覽器訪問:[http://127.0.0.1:9000/](http://127.0.0.1:9000/) ,Zuul 會去 Eureka 服務注冊中心,找到`eureka-provider`服務以均衡負載的方式訪問
## 測試服務
依次啟動項目:
`spring-cloud-eureka-service`
`spring-cloud-eureka-provider-1`
`spring-cloud-eureka-provider-2`
`spring-cloud-eureka-provider-3`
`spring-cloud-zuul-service`
啟動該工程后,訪問服務注冊中心,查看服務是否都已注冊成功:[http://localhost:8761/](http://localhost:8761/)
![查看各個服務注冊狀態][33]
**瀏覽器訪問**
訪問:[http://127.0.0.1:9000/](http://127.0.0.1:9000/) ,Zuul 會去 Eureka 服務注冊中心,找到`eureka-provider`服務以均衡負載的方式訪問
**在命令窗口`curl http://localhost:9000/`,發現一切正常**
或者瀏覽器`get` 請求`http://localhost:9000/` F5 刷新
![測試轉發服務][44]
# 路由規則
**網關的默認路由規則**
`Spring cloud zuul` 默認情況下,`Zuul`會代理所有注冊到`Eureka Server`的微服務,并且`Zuul`的路由規則如下:`[http://ZUUL_HOST:ZUUL_PORT/]()` 微服務在`Eureka`上的`serviceId/**`會被轉發到`serviceId`對應的微服務。
我們注釋 `spring-cloud-zuul-service`項目中關于路由的配置:
```sh
#zuul:
# routes:
# api:
# path: /**
# serviceId: eureka-provider
```
**瀏覽器訪問**
訪問:[http://127.0.0.1:9000/eureka-provider/](http://127.0.0.1:9000/eureka-provider/) ,Zuul 會去 Eureka 服務注冊中心,找到`eureka-provider`服務以均衡負載的方式訪問
**在命令窗口`curl http://127.0.0.1:9000/eureka-provider/`,發現一切正常**
或者瀏覽器`get` 請求`http://127.0.0.1:9000/eureka-provider/` F5 刷新
![測試轉發服務][55]
# ZuulFilter
在下一章,會深入介紹 Zuul 高級功能使用,`ZuulFilter` ,支持下鵬磊,關注下屏幕下方的微信公眾號
# 源碼下載
**GitHub:**[https://github.com/souyunku/spring-cloud-examples/tree/master/spring-cloud-zuul](https://github.com/souyunku/spring-cloud-examples/tree/master/spring-cloud-zuul)
**碼云:**[https://gitee.com/souyunku/spring-cloud-examples/tree/master/spring-cloud-zuul](https://gitee.com/souyunku/spring-cloud-examples/tree/master/spring-cloud-zuul)
[11]: https://www.souyunku.com/images/2017/SpringCloud/zuul/11.png
[22]: https://www.souyunku.com/images/2017/SpringCloud/zuul/22.png
[33]: https://www.souyunku.com/images/2017/SpringCloud/zuul/33.png
[44]: https://www.souyunku.com/images/2017/SpringCloud/zuul/44.png
[55]: https://www.souyunku.com/images/2017/SpringCloud/zuul/55.png
- Spring Cloud(一)服務的注冊與發現(Eureka)
- Spring Cloud(二)Consul 服務治理實現
- Spring Cloud(三)服務提供者 Eureka + 服務消費者(rest + Ribbon)
- Spring Cloud(四)服務提供者 Eureka + 服務消費者 Feign
- Spring Cloud(五)斷路器監控(Hystrix Dashboard)
- Spring Cloud(六)服務網關 zuul 快速入門
- Spring Cloud(七)服務網關 Zuul Filter 使用
- Spring Cloud(八)高可用的分布式配置中心 Spring Cloud Config
- Spring Cloud(九)高可用的分布式配置中心 Spring Cloud Config 集成 Eureka 服務
- Spring Cloud(十)高可用的分布式配置中心 Spring Cloud Config 中使用 Refresh
- Spring Cloud(十一)高可用的分布式配置中心 Spring Cloud Bus 消息總線集成(RabbitMQ)