前言
介紹完分布式配置中心,結合前面的文章。我們已經有了一個微服務的框架了,可以對外提供api接口服務了。但現在試想一下,在微服務框架中,每個對外服務都是獨立部署的,對外的api或者服務地址都不是不盡相同的。對於內部而言,很簡單,通過注冊中心自動感知即可。但我們大部分情況下,服務都是提供給外部系統進行調用的,不可能同享一個注冊中心。同時一般上內部的微服務都是在內網的,和外界是不連通的。而且,就算我們每個微服務對外開放,對於調用者而言,調用不同的服務的地址或者參數也是不盡相同的,這樣就會造成消費者客戶端的復雜性,同時想想,可能微服務可能是不同的技術棧實現的,有的是
http
、rpc
或者websocket
等等,也會進一步加大客戶端的調用難度。所以,一般上都有會有個api網關,根據請求的url不同,路由到不同的服務上去,同時入口統一了,還能進行統一的身份鑒權、日志記錄、分流等操作。接下來,我們就來了解今天要講解的路由服務:zuul
。
一點知識
為什么要使用微服務網關
簡單來說,微服務網關是微服務架構中一個不可或缺的部分。通過服務網關統一向外系統提供REST API的過程中,除了具備服務路由、均衡負載功能之外,它還具備了權限控制等功能。
在未加入網關時,一般上會在服務外網架設一個負載均衡,如nginx等。此時,微服務的組成為:
此時,對於Open Service
而言可能需要提供權限控制等和業務無關的能力,這樣本身就破壞了微服務服務單一的原則。所以,一般上在Open Service
之上,還有一層服務提供諸如通用的權限校驗、參數校驗等功能,此服務就是網關了。之后,對於內部微服務而言,只需要關系各自微服務提供的業務功能即可,無需去關心其他非業務相關的功能。
API網關是什么
API網關可以提供一個單獨且統一的API入口用於訪問內部一個或多個API。簡單來說嘛就是一個統一入口,比如現在的支付寶或者微信的相關api服務一樣,都有一個統一的api地址,統一的請求參數,統一的鑒權。
客戶端和服務端直連的弊端
- 客戶端會對此請求不同的微服務,增加客戶端復雜性
- 存在跨域請求時,需要進行額外處理
- 認證服務,每個服務需要獨立認證
- UI端和微服務耦合
網關的優缺點
優點:
- 減少api請求次數
- 限流
- 緩存
- 統一認證
- 降低微服務的復雜度
- 支持混合通信協議(前端只和api通信,其他的由網關調用)
- ……
缺點:
- 網關需高可用,可能產生單點故障
- 管理復雜
網關的選擇
現在市場上有很多的網關可供選擇:
- Spring Cloud Zuul:本身基於
Netflix
開源的微服務網關,可以和Eureka
,Ribbon
,Hystrix
等組件配合使用。 - Kong : 基於OpenResty的 API 網關服務和網關服務管理層。
- Spring Cloud Gateway:是由
spring
官方基於Spring5.0
,Spring Boot2.0
,Project Reactor
等技術開發的網關,提供了一個構建在Spring Ecosystem
之上的API網關,旨在提供一種簡單而有效的途徑來發送API,並向他們提供交叉關注點,例如:安全性,監控/指標和彈性。目的是為了替換Spring Cloud Netfilx Zuul
的。
在Spring cloud
體系中,一般上選擇zuul
或者Gateway
。當然,也可以綜合自己的業務復雜性,自研一套或者改在一套符合自身業務發展的api網關的,最簡單做法是做個聚合api服務
,通過SpringBoot
構建對外的api接口,實現統一鑒權、參數校驗、權限控制等功能,說白了就是一個rest
服務。
Zuul介紹和使用
何為Zuul
Zuul
是Netflix
開源的微服務網關,可以和Eureka
、Ribbon
、Hystrix
等組件配合使用,Spring Cloud
對Zuul
進行了整合與增強,Zuul
的主要功能是路由轉發和過濾器。
Zuul
基於JVM的路由器和服務器端負載均衡器。同時,Zuul
的規則引擎允許規則和過濾器基本上用任何JVM語言編寫,內置支持Java
和Groovy
。這個功能,就可以實現動態路由的功能了。當需要添加某個新的對外服務時,一般上不停機更新是通過數據緩存配置或者使用Groovy
進行動態路由的添加的。
Zuul
的核心一系列的過濾器:
- 身份認證與安全:識別每個資源的驗證要求,並拒絕那些與要求不符的請求。
- 審查與監控:在邊緣位置追蹤有意義的數據和統計結果,從而帶來精確的生產視圖。
- 動態路由:動態地將請求路由到不同的后端集群。
- 壓力測試:逐漸增加指向集群的流量,以了解性能。
- 負載分配:為每一種負載類型分配對應容量,並啟用超出限定值的請求。
- 靜態響應處理:在邊緣位置直接建立部分相應,從而避免其轉發到內部集群。
加入Zuul
網關后:
Zuul實踐
創建工程:spring-cloud-zuul
。
這里直接加入了注冊中心進行服務化模式。
0.加入pom依賴。
<!-- zuul 依賴 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<!-- eureka client 依賴 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
注意:這里的Eureka
不是必須的。在沒有注冊中心的情況下,也是可以進行zuul使用的。
1.配置文件,配置注冊中心相關信息、路由規則等。
spring.application.name=zuul-server
server.port=8888
# 注冊中心地址 -此為單機模式
eureka.client.service-url.defaultZone=http://127.0.0.1:1000/eureka
# 啟用ip配置 這樣在注冊中心列表中看見的是以ip+端口呈現的
eureka.instance.prefer-ip-address=true
# 實例名稱 最后呈現地址:ip:15678
eureka.instance.instance-id=${spring.cloud.client.ip-address}:${server.port}
## 路由規則
## 傳統路由配置:不依賴服務發現。
## 所有以myapi開頭的url路由至http://127.0.0.1:2000/下
## 如http://127.0.0.1:8888/myapi/hello --> http://127.0.0.1:2000/hello
zuul.routes.myApi.path=/myapi/**
zuul.routes.myApi.url=http://127.0.0.1:2000
#forward模式 直接轉發至zuul提供的rest服務
zuul.routes.myDemo.path=/myDemo/**
zuul.routes.myDemo.url=forward:/demo
## 服務發現模式
# 路由地址
zuul.routes.myEureka.path=/eureka/**
#為具體服務的名稱
zuul.routes.myEureka.service-id=eureka-client
友情提示:
默認情況下:Zuul代理所有注冊到EurekaServer的微服務,路由規則:
http://ZUUL_HOST:ZUUL_PORT/微服務實例名(serverId)/**
轉發至serviceId對應的微服務。
如:http://127.0.0.1:8888/eureka-client/hello?name=oKong
最后就是轉發至:http://127.0.0.1:2000//hello?name=oKong
2.啟動類加入@EnableZuulProxy
注解,聲明一個Zuul代理。
/**
* zuul 示例
*
* @author oKong
*
*/
@SpringBootApplication
@EnableZuulProxy
@EnableDiscoveryClient
@Slf4j
public class SpringCloudZuulApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(SpringCloudZuulApplication.class, args);
log.info("spring-cloud-zuul啟動!");
}
}
3.編寫一個控制類,測試forward
功能。
/**
* zuul 內部提供對外服務示例
* @author oKong
*
*/
@RestController
@RequestMapping("/demo")
public class DemoController {
@GetMapping("/hello")
public String hello(String name) {
return "hi," + name + ",this is zuul api! ";
}
}
4.啟動spring-cloud-eureka-server
和spring-cloud-eureka-client
服務,之后再啟動spring-cloud-zuul
服務。
此時,我們來訪問zuul內部服務:http://127.0.0.1:8888/demo/hello?name=oKong
然后訪問:http://127.0.0.1:8888/myDemo/hello?name=oKong 效果和上面直接方式是一樣的,最后都是訪問/demo/**
api地址的。
訪問:http://127.0.0.1:8888/myapi/hello?name=oKong 和 http://127.0.0.1:8888/eureka/hello?name=oKong 最后效果都是一樣的,都是訪問http://127.0.0.1:2000/hello?name=oKong 。
直接以默認形式訪問:http://127.0.0.1:8888/eureka-client/hello?name=oKong 最后效果也是一樣的。
其實,從控制台輸出日志也是可以一窺究竟的:
路由規則說明
簡單說明下關於路由規則的一些說明:
傳統路由配置:不依賴服務發現,如nginx
- 單例實例配置:通過
zuul.routes.<route>.path
和zuul.routes.<route>.url
參數對的方式來配置
# 傳統路由配置
zuul.routes.server-provide.path=/server-provide/**
zuul.routes.server-provide.url=http://127.0.0.1:1001/
- 多實例配置:通過一組
zuul.routes.<route>.path
與zuul.routes.<route>.serviceId
參數對的方式配置
# 多實例
zuul.routes.server-provide.path=/user-service/**
zuul.routes.server-provide.serviceId=user-service
ribbon.eureka.enabled=false
server-provide.ribbon.listOfServers=http://127.0.0.1:1001/,http://127.0.0.1:1001/
服務路由配置:依賴服務發現,結合Eureka
-
默認規則:http://ZUUL_HOST:ZUUL_PORT/微服務實例名(serverId)/** ,轉發至serviceId對應的微服務。
-
自定義路由規則:通過一組
zuul.routes.<route>.path
與zuul.routes.<route>.serviceId
參數對的方式配置
# 自定義路由
zuul.routes.server-provide.path=/server-api/**
zuul.routes.server-provide.serviceId=server-provide
比如:
而且,要注意,這些過濾器是path
進行最佳路徑匹配的,所以,一般上在一些歷史系統上,我們會在最后后面加上一個路徑/**的匹配規則,以保證歷史api可以使用,做到最大兼容性,避免類似404的異常。
zuul.routes.legacy.path=/**
參考資料
總結
本章節主要簡單介紹了關於
Zuul
的一些簡單使用以及一些路由規則的簡單說明。開頭也說過了,Zuul
的核心是一系列的過濾器。介於篇幅問題,關於過濾器的介紹、自定義過濾器、異常處理、熔斷降級等放在下一章節來講解。而相關的其他配置,大家可以去官網進行查看,或者自行搜索下,也可以查看下:org.springframework.cloud.netflix.zuul.filters.ZuulProperties
類,相關配置信息都在里面了。
最后
目前互聯網上大佬都有分享
SpringCloud
系列教程,內容可能會類似,望多多包涵了。原創不易,碼字不易,還希望大家多多支持。若文中有錯誤之處,還望提出,謝謝。
老生常談
- 個人QQ:
499452441
- 微信公眾號:
lqdevOps
個人博客:http://blog.lqdev.cn
源碼示例:https://github.com/xie19900123/spring-cloud-learning
原文地址:http://blog.lqdev.cn/2018/10/14/SpringCloud/chapter-nine/