環境:
jdk1.8;spring boot2.0.3;spring cloud(Finchley.RELEASE版本);Maven3.3
摘要說明:
前面幾章我們實作了:
服務中心提供服務注冊和發現;
實作消費者對提供方的負載均衡調用;
實作服務調用時的熔斷機制使服務更加健全及其監控;
但消費者如何給外部調用且對消費者的叢集如何做到負載均衡及權限管理?
故抽象出服務網關這個概念:
服務網關是微服務架構中一個不可或缺的部分。通過服務網關統一向外系統提供REST API的過程中,除了具備服務路由、均衡負載功能之外,它應還具備了權限控制等功能。
Spring Cloud Zuul:為微服務架構提供了前門保護的作用,同時将權限控制這些較重的非業務邏輯内容遷移到服務路由層面,使得服務叢集主體能夠具備更高的可複用性和可測試性。
步驟:
1.建立zuul(Zuul服務路由)子項目
通過
SPRING INITIALIZR
工具選擇Cloud Routing:Zuul子產品建構turbine子項目引入依賴(pom.xnl):
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>pers.cc</groupId>
<artifactId>springCloud</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>zuul</artifactId>
<name>zuul</name>
<description>zuul(服務網關)</description>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
</project>
2.配置zuul
使用@EnableZuulProxy注解開啟zuul的服務網關配置:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Bean;
import pers.cc.springCloud.zuul.filter.AccessTokenFilter;
import pers.cc.springCloud.zuul.filter.AccessTokenFilter1;
@SpringBootApplication
@EnableZuulProxy
@EnableDiscoveryClient
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
// 配置過濾器
@Bean
public AccessTokenFilter accessTokenFilter() {
return new AccessTokenFilter();
}
// 配置多個過濾器
@Bean
public AccessTokenFilter1 accessTokenFilter1() {
return new AccessTokenFilter1();
}
}
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
// 配置過濾器
@Bean
public AccessTokenFilter accessTokenFilter() {
return new AccessTokenFilter();
}
// 配置多個過濾器
@Bean
public AccessTokenFilter1 accessTokenFilter1() {
return new AccessTokenFilter1();
}
}
配置application.properties:
#指定服務名和端口
spring.application.name=zuul
server.port=8001
#注冊到注冊中心
eureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/
#單例配置
zuul.routes.hystrix.path=/hystrix/**
zuul.routes.hystrix.url=http://localhost:5001/
#叢集配置
zuul.routes.ribbon.path=/ribbon/**
zuul.routes.ribbon.serviceId=ribbon-consumer
#若服務名稱和通配url一緻則可省略成以下配置
zuul.routes.feign-consumer=/feign-consumer/**
3.開發zuul
a.路由配置
通過在application.properties進行規則配置達到配置路由的效果:
1.單例配置,即消費者隻部署一個服務時:
zuul.routes.XX.path=通配位址
zuul.routes.XX.url=服務指向
2.叢集配置(自定義path),即後端服務多個且自定義path:
zuul.routes.XX.path=通配位址
zuul.routes.XX.serviceId=服務名稱
3.叢集配置(預設path),即後端服務多個且預設path:
zuul.routes.XX=/XX/**
b.過濾器開發
過濾器主要是用來進行權限管理;即通過過濾器來進行請求的攔截和過濾;過濾器中可以進行鑒權、簽名校驗、權限驗證等
1)實作過濾器,繼承實作ZuulFilter,實作其四個方法:
Object run():主過濾邏輯處理
boolean shouldFilter():判斷該過濾器是否需要被執行
int filterOrder():過濾器的執行順序
String filterType():過濾器類型:pre-可以在請求被路由之前調用、route-在路由請求時候被調用、 post-在route和error過濾器之後被調用、error-處理請求時發生錯誤時被調用
建立AccessTokenFilter設定需要添加參數accessToken,執行順序為1;
import javax.servlet.http.HttpServletRequest;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
public class AccessTokenFilter extends ZuulFilter {
// run:過濾器的具體邏輯。
// 通過ctx.setSendZuulResponse(false)令zuul過濾該請求,不對其進行路由,
// 然後通過ctx.setResponseStatusCode(401)設定了其傳回的錯誤碼,
// 也可以進一步優化比如,通過ctx.setResponseBody(body)對傳回body内容進行編輯等。
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
Object accessToken = request.getParameter("accessToken");
System.out.println("accessToken:" + accessToken);
if (accessToken == null) {
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
return null;
}
return null;
}
// shouldFilter:判斷該過濾器是否需要被執行。
// true表示該過濾器對所有請求都會生效。
// 實際運用中我們可以利用該函數來指定過濾器的有效範圍。
@Override
public boolean shouldFilter() {
return true;
}
// filterOrder:過濾器的執行順序。當請求在一個階段中存在多個過濾器時,需要根據該方法傳回的值來依次執行。
@Override
public int filterOrder() {
return 0;
}
// filterType:過濾器的類型,它決定過濾器在請求的哪個生命周期中執行。
// pre:可以在請求被路由之前調用
// route:在路由請求時候被調用
// post:在route和error過濾器之後被調用
// error:處理請求時發生錯誤時被調用
@Override
public String filterType() {
return "pre";
}
}
建立AccessTokenFilter1設定需要添加參數accessToken且參數不能等于zuul,執行順序為2;
import javax.servlet.http.HttpServletRequest;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
public class AccessTokenFilter1 extends ZuulFilter {
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
Object accessToken = request.getParameter("accessToken");
System.out.println("accessToken1:" + accessToken);
if (accessToken == null || "zuul".equals(accessToken)) {
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
return null;
}
return null;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public int filterOrder() {
return 2;
}
@Override
public String filterType() {
return "pre";
}
}
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
Object accessToken = request.getParameter("accessToken");
System.out.println("accessToken1:" + accessToken);
if (accessToken == null || "zuul".equals(accessToken)) {
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
return null;
}
return null;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public int filterOrder() {
return 2;
}
@Override
public String filterType() {
return "pre";
}
}
2)在ZuulApplication裡挂載過濾器:
// 配置過濾器
@Bean
public AccessTokenFilter accessTokenFilter() {
return new AccessTokenFilter();
}
// 配置多個過濾器
@Bean
public AccessTokenFilter1 accessTokenFilter1() {
return new AccessTokenFilter1();
}
4.測試zuul
先後啟動服務:
服務注冊中心(eurekaServer):eureka-server(1001)
服務提供者(eurekaDiscovery):eureka-client(2001)
服務消費者(ribbon):ribbon-consumer(3001)
服務消費者(ribbon):ribbon-consumer(3002)
服務消費者(feign):feign-consumer(4001)
服務消費者(feign):feign-consumer(4002)
服務消費者(hystrix):hystrix-consumer(5001)
服務網關(zuul):zuul(8001)
分别測試上述配置
http://localhost:8001/hystrix/test --》傳回401異常且兩個過濾器都執行過
http://localhost:8001/hystrix/test?accessToken=11 --》正常傳回
http://localhost:8001/ribbon/test?accessToken=11 --》正常傳回
http://localhost:8001/feign-consumer/test?accessToken=11 --》正常傳回
http://localhost:8001/feign-consumer/test?accessToken=zuul --》傳回401異常
5.源碼位址
github位址:https://github.com/cc6688211/springCloud.git