1. 網關服務所謂何
在微服務架構風格中,一個大應用被拆分成爲了多個小的服務系統提供出來,這些小的系統他們可以自成體系,也就是說這些小系統可以擁有自己的數據庫,框架甚至語言等,這些小系統通常以提供 RestFul 風格的接口來被 H5, Android, IOS 以及第三方應用程序調用。
但是在UI上進行展示的時候,我們通常需要在一個界面上展示很多數據,這些數據可能來自於不同的微服務中。如果想通過一個統一的入口來請求不同的服務,那麼給應用專門建立一個網關(Gateway)服務就顯得非常有必要了。下圖展示了一個擁有網關服務的微服務架構設計。
微服務中的網關服務同其他服務一樣,被微服務的註冊中心統一發現並管理,網關服務一般負責處理負載均衡、緩存、路由、訪問控制、服務代理、監控、日誌等。本文主要實現網關服務的路由轉發和過濾功能。
2. Zuul來過濾且分發
2.1 NetFlix Zuul
Zuul is the front door for all requests from devices and websites to the backend of the Netflix streaming application. As an edge service application, Zuul is built to enable dynamic routing, monitoring, resiliency, and security.
以上內容摘自NetFlix Zuul官方Git Wiki。這段話高度概括了Zuul的作用,大概意思是Zuul是所有從客戶端發往服務端的請求的“守門人”,可以提供動態路由、監控、彈性、安全等邊緣服務。
Zuul uses a range of different types of filters that enables us to quickly and nimbly apply functionality to our edge service. These filters help us perform the following functions:
- Authentication and Security - identifying authentication requirements for each resource and rejecting requests that do not satisfy them.
- Insights and Monitoring - tracking meaningful data and statistics at the edge in order to give us an accurate view of production.
- Dynamic Routing - dynamically routing requests to different backend clusters as needed.
- Stress Testing - gradually increasing the traffic to a cluster in order to gauge performance.
- Load Shedding - allocating capacity for each type of request and dropping requests that go over the limit.
- Static Response handling - building some responses directly at the edge instead of forwarding them to an internal cluster
- Multiregion Resiliency - routing requests across AWS regions in order to diversify our ELB usage and move our edge closer to our members
2.2 Spring Cloud Zuul
Spring Cloud has created an embedded Zuul proxy to ease the development of a common use case where a UI application wants to make proxy calls to one or more back end services. This feature is useful for a user interface to proxy to the back end services it requires, avoiding the need to manage CORS and authentication concerns independently for all the back ends.
以上內容摘自Spring Cloud官方文檔。大致意思是Spring Cloud內嵌了Zuul以適應一個代理調用多個不同後端服務的通用場景,使得在開發過程中不用關心跨域資源共享和安全認證等細節。
3. 實際動手初體驗
3.1 搭建Eureka微服務框架
內嵌在Spring Cloud中的Zuul Proxy需要依賴Eureka的服務發現功能,所以萬里長征第一步是搭建Spring Eureka微服務框架。
- 新建項目(即project)demo-parent,在它下面新建模塊(即module)demo-eureka-server作爲服務註冊中心、demo-gateway作爲網關服務、demo-web作爲一個普通服務。
- 將demo-eureka-server設置爲註冊中心,並把demo-gateway和demo-web註冊到demo-eureka-server上面。
- 在demo-eureka-server的pom文件中添加依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
- 在demo-eureka-server啓動類DemoEurekaServerApplication前面添加@EnableEurekaServer註解表明這個模塊是Eureka服務註冊中心
- 在demo-eureka-server的application.yml文件中添加配置
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
fetch-registry: false
register-with-eureka: false
service-url:
default-zone: http://${eureka.instance.hostname}:${server.port}/eureka/
spring:
application:
name: demo-eureka-server
- 在demo-gateway和demo-web的pom文件中添加依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- 在demo-gateway和demo-web啓動類前面添加@EnableEurekaClient註解表明這個模塊可以被註冊中心發現
- 在demo-gateway的application.yml文件中添加配置
server:
port: 8767
eureka:
client:
service-url:
default-zone: http://localhost:8761/eureka/
spring:
application:
name: demo-gateway
- 在demo-web的application.yml文件中添加與demo-gateway類似的配置,不過要注意區分端口號,設置demo-web的端口號爲8768
- 此時,分別運行三個模塊的啓動類,可以看到demo-gateway和demo-web已經被註冊到了demo-eureka-server上面
3.2 配置Gateway服務
- 將demo-gateway設置爲網關服務,能夠將請求進行過濾並且分發到不同的服務上
- 在demo-gateway的pom文件中添加依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
- 在demo-gateway啓動類上面添加@EnableZuulProxy註解表明這個模塊是網關服務模塊
- 在demo-gateway的application.yml文件中添加配置
zuul:
routes:
web:
path: /web/**
serviceId: demo-web
這裏採用了手動映射路由的方式,意思是當請求路徑包含“/web”的時候,當前請求會被轉發到demo-web服務上。如果不想手動映射,可以在pom文件引入spring-boot-starter-actuator依賴,再在application.yml添加端點暴露的有關配置(這裏不再詳細說明),瀏覽器訪問http://localhost:8767/actuator/routes就可以看到demo-gateway配置的各服務路徑映射關係。"/web/**":"demo-web"
是我們手動配置的映射關係,"/demo-web/**":"demo-web"
是自動配置的映射關係,也就是說http://localhost:8767/web/hi和http://localhost:8767/demo-web/hi都會去請求demo-web服務上的hi方法。
2. 實現demo-gateway的過濾功能
目前demo-gateway已經具備了分發路由的功能,如果想在請求方法之前進行一些驗證,需要繼承ZuulFilter類並重寫有關方法。
@Component
public class CommonAccessPreFilter extends ZuulFilter {
private static Logger logger = LoggerFactory.getLogger(CommonAccessPreFilter.class);
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
logger.info(String.format("%s >>> %s", request.getMethod(), request.getRequestURL().toString()));
String id = request.getParameter("id");
if (id == null)
logger.warn("id is null");
else
logger.info("id = " + id);
return null;
}
}
filterType方法定義過濾器的類型。Zuul中定義了四種不同類型的過濾器,分別是pre(在請求被路由之前調用)、route(在請求路由時調用)、error(處理請求時發生錯誤時被調用)、post(在route和error之後被調用)。本文使用pre過濾器,即在路由之前做一些驗證。
filterOrder方法定義過濾器的執行順序,數值越大,優先級越低。
shouldFilter方法是過濾器的開關,返回true時過濾器纔會生效。
run方法裏面是具體的過濾邏輯。本文舉例驗證請求參數id是否爲空,針對id是否爲null可以個性化定製不同的邏輯。
下圖是分別請求http://localhost:8767/web/hi?id=1和http://localhost:8767/web/hi時過濾器打印在控制檯的日誌。
4. 腳下足跡永留存
完整項目源碼已上傳至個人公開GitHub倉庫