一.構建工程 1.引入依賴 2.創建主類 3.配置application.properties 這裡存在 api-a 和 api-b 兩個微服務應用, 當請求http://localhost:port/api-a/helloWorld, 會被路由轉發至 api-a 服務的 /helloWorld 接 ...
一.構建工程
1.引入依賴
<!--SpringBoot2.0以上版本需引入該依賴--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency>
2.創建主類
@SpringBootApplication @EnableDiscoveryClient @EnableZuulProxy @RestController public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
3.配置application.properties
zuul.routes.api-a.path=/api-a/** zuul.routes.api-a.service-id=api-a zuul.routes.api-b.path=/api-b/** zuul.routes.api-b.service-id=api-b
這裡存在 api-a 和 api-b 兩個微服務應用, 當請求http://localhost:port/api-a/helloWorld, 會被路由轉發至 api-a 服務的 /helloWorld 介面, 當請求http://localhost:port/api-b/helloWorld, 會被路由轉發至 api-b 服務的 /helloWorld 介面. 當請求 URL 符合配置規則時, 就會被轉發至 service-id 對應的微服務應用介面.
4.配置請求過濾
SpringCloud Zuul 還有另一個和核心功能: 請求過濾. Zuul 允許開發者在 API 網關上通過定義過濾器來實現對請求的攔截與過濾, 實現方法非常簡單, 只需繼承 ZuulFilter 抽象類並實現它定義的4個抽象函數就可以完成對請求的攔截和過濾.
public class MyGatewayFilter extends ZuulFilter {
/*4種過濾器類型,pre
:可以在請求被路由之前調用,route
:在路由請求時候被調用,
post
:在route和error過濾器之後被調用,error
:處理請求時發生錯誤時被調用*/
@Override
public String filterType() {
return "pre";
}
@Override public int filterOrder() { return 0; //優先順序為0,數字越大,優先順序越低 } @Override public boolean shouldFilter() { return true; // 是否執行該過濾器,此處為true,說明需要過濾 } @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); log.info(String.format("%s request to %s", request.getMethod(), request.getRequestURL().toString())); Object accessToken = request.getParameter("token"); //獲取token參數 if (accessToken == null) { log.warn("token is empty"); ctx.setSendZuulResponse(false); //過濾該請求, 不對其進行路由 ctx.setResponseStatusCode(401); //返回錯誤碼 ctx.setResponseBody("token is null!"); //返回錯誤內容 return null; //Zuul還未對返回數據做處理 }
return null; } }
創建過濾器後,它並不會直接生效, 我們還需為其創建具體的 Bean 才能啟動該過濾器.
@SpringBootApplication @EnableDiscoveryClient @EnableZuulProxy @RestController public class LixjApplication { public static void main(String[] args) { SpringApplication.run(LixjApplication.class, args); } @Bean public MyGatewayFilter myGatewayFilter(){ return new MyGatewayFilter(); } }
二. 路由詳解
1.路徑匹配規則
/api-a/? 可以匹配 /api-a/ 之後拼接一個任務字元的路徑 , 比如 /api-a/a , /api-a/b , /api-a/c
/api-a/* 可以匹配 /api-a/ 之後拼接任意字元的路徑, 比如 /api-a/a, /api-a/aaa, /api-a/bbb . 但它無法匹配 /api-a/a/b 這種多級目錄路徑
/api-a/** 可以匹配 /api-a/* 包含的內容之外, 還可以匹配形如 /api-a/a/b 的多級目錄路徑
2.路由匹配順序
隨著版本的迭代, 我們需要對一個服務做一些功能拆分, 將原屬於 api-a 服務的某些功能拆分到另一個全新的服務 api-a-part 中, 而這些拆分的外部調用 URL 路徑希望能夠符合規則 /api-a/part/** .
zuul.routes.api-a.path=/api-a/** zuul.routes.api-a.service-id=api-a zuul.routes.api-a-part.path=/api-a/part/** zuul.routes.api-a-part.service-id=api-a-part
在源碼中, 路由規則是通過 LinkedHashMap 保存的, 也就是說, 路由規則的保存時有序的, 而內容的載入是通過遍歷配置文件中路由規則依次加入的, 所以導致問題的根本原因是對配置文件中內容的讀取, 但上述properties配置無法保證路由規則載入順序, 我們需要使用 YML 文件來配置, 以實現有序的路由規則.
zuul: routes: api-a-part: path=/api-a/part/** service-id=api-a-part api-a: path=/api-a/** service-id=api-a
3.本地跳轉
zuul.routes.api-c.path=/api-c/** zuul.routes.api-c.url=forward:/api-c
以上配置使用了本地跳轉,當 url 符合 /api-c/** 規則時,會被網關轉發到 自己本身 服務的對應介面.