前面所有的微服務都是通過Eureka找到的,但是在很多開發中爲了規範微服務的使用,提供有一個處理控制器Zuul。Zuul其實是一個API網關,類似於設計模式裏面的Facade門面模式,他的存在就像是整個微服務的門面,所有的外部客戶端訪問都需要經過它來進行調度與過濾。
代碼Git地址:https://gitee.com/hankin_chj/springcloud-micro-service.git
一、zuul的基本使用
1、新建模塊springcloud-micro-zuul-gateway
1.1、springcloud-micro-zuul-gateway的pom文件如下
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
1.2、zuul-gateway修改application.yml文件
server:
port: 9501
eureka:
client: # 客戶端進行Eureka註冊的配置
service-url:
defaultZone: http://admin:admin@eureka1:7001/eureka,http://admin:admin@eureka2:7002/eureka,http://admin:admin@eureka3:7003/eureka
register-with-eureka: false
spring:
application:
name: springcloud-micro-zuul-gateway
1.3、zuul-gateway創建啓動類
@SpringBootApplication
@EnableZuulProxy
public class ZuulApp {
public static void main(String[] args) {
SpringApplication.run(ZuulApp.class,args);
}
}
1.4、運行啓動報錯:
發現啓動報錯,其實這是因爲zuul目前對springboot2.1.2及以上版本的支持並不好,這也是zuul最近一直被人詬病的地方,爲了解決這個問題只好降低springboot的版本至2.0.7及以下。
1.5、降低系統的springboot版本爲2.0.7.RELEASE
springcloud-micro-service修改父工程pom文件,降低springboot版本爲2.0.7.RELEASE
<!-- 進行SpringCloud依賴包的導入處理 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- SpringCloud離不開SpringBoot,所以必須要配置此依賴包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<!--<version>2.1.3.RELEASE</version>-->
<version>2.0.7.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
注意:
因爲zuul的使用問題降低了springboot的版本,這個使用要注意將數據庫的驅動名稱也做相應的修改:
# 配置MySQL的驅動程序類,注意springboot降低版本以後不能使用com.mysql.cj.jdbc.Driver
driver-class-name: com.mysql.jdbc.Driver
重新啓動ZuulApp:
正常訪問用戶服務:http://localhost:8090/user/get/1
使用zuul代理訪問用戶服務:http://localhost:9501/springcloud-micro-user/user/get/1
2、Zuul配置路由
前面以及簡單的使用了zuul,但你會發現訪問地址還必須知道程序的名稱,如果不知道這個名稱是無法訪問的,但如果讓用戶知道了這名稱,那麼使用zuul就是去它的實際意義的,我們可以通過名稱直接調用。既然是使用代理,那麼代理的功能就是不能讓用戶看到真實的操作,屏蔽真實的調用地址,這個時候就需要自己增加zuul的路由規則配置了。
2.1、zuul-gateway修改application.yml配置文件,增加路由配置
# 使用zuul代理
zuul:
routes:
springcloud-micro-user: /user-proxy/**
這個時候就可以通過/users-proxy 來訪問springcloud-micro-user服務
訪問地址:http://localhost:9501/user-proxy/user/get/1,可以正常返回結果。
2.2、忽略掉用戶服務的名稱
但是還會發現,雖然現在以及開啓了路由訪問的支持,但依然通過應用程序的名稱還是能訪問
訪問地址:http://localhost:9501/microcloud-provider-users/users/get/1
zuul-gateway修改application.yml文件,忽略掉用戶服務的名稱:
# 使用zuul代理
zuul:
routes:
springcloud-micro-user: /user-proxy/**
ignored-services: springcloud-micro-user
做完後,就可以進行代理的安全使用,但真實情況下,一般會有很多微服務,如果完全按照上面的配置方式會非常的麻煩,所有最加到的做法是可以採用一個通配符“*”的模式來統一完成。
2.3、zuul-gateway修改application.yml文件
# 使用zuul代理
zuul:
routes:
springcloud-micro-user: /user-proxy/**
#ignored-services: springcloud-micro-user
ignored-services: "*"
重新啓動訪問:http://localhost:9501/user-proxy/user/get/1,正常返回數據。
訪問:http://localhost:9501/springcloud-micro-user/user/get/1,則無法訪問。
2.4、zuul中另外一種配置方式
除開上面這一種訪問模式以外,在zuul中還有另外一種配置方式,修改application.yml文件:
zuul: # zuul的另外一隻配置方式
routes:
user.path: /user-proxy/**
user.serviceId: springcloud-micro-user
ignored-services:
"*"
其中在配置文件中出現的user其實是一個邏輯名稱,這個名稱主要作用是將path與serviceId綁定在一起。
2.5、zuul-gateway脫離eureka進行訪問
zuul-gateway模塊如果說不想通過eureka進行訪問,對於zuul來說也是可以實現的,但是在真實的開發環境中,基本不會使用,yml配置如下所示:
# zuul-gateway脫離eureka進行訪問
zuul:
routes:
user:
path: /user-proxy/**
serviceId: springcloud-micro-user
user2:
path: /user2-proxy/**
url: http://localhost:8090/
ignored-services:
"*"
重新啓動訪問:http://localhost:9501/user2-proxy/user/get/1,依然可以正常返回數據。
2.6、zuul-gateway模塊設置公共前綴prefix
zuul:
routes:
user:
path: /user-proxy/**
serviceId: springcloud-micro-user
user2:
path: /user2-proxy/**
url: http://localhost:8090/
ignored-services:
"*"
prefix: /chj-api
注意:一旦設置了公共前綴,所以的訪問路徑都要在前面加上前綴
分別通過兩個路由來訪問測試:
http://localhost:9501/chj-api/user-proxy/user/get/1
http://localhost:9501/chj-api/user2-proxy/user/get/1
都可以正常番薯數據。
3、zuul過濾訪問
其實zuul的功能本質上就是一個代理操作,類似於nginx,但是在真實的使用中,所有的微服務一點都有增加的認證信息,那麼就必須在其訪問之前追加認證的頭部操作,這樣的功能需要通過zuul的過去操作完成。
3.1、zuul增加產品微服務
zuul-gateway修改application.yml配置,增加產品微服務配置:
zuul:
routes:
user:
path: /user-proxy/**
serviceId: springcloud-micro-user
user2:
path: /user2-proxy/**
url: http://localhost:8090/
product:
path: /product-proxy/**
serviceId: springcloud-micro-product
ignored-services:
"*"
prefix: /chj-api
重啓訪問:http://localhost:9501/chj-api/product-proxy/prodcut/get/1
這樣直接訪問是訪問不到的,需要添加安全模塊,查看控制檯報錯信息如下:
com.netflix.zuul.exception.ZuulException: Forwarding error at
org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter.handleException(RibbonRoutingFilter.java:189) ~[spring-cloud-netflix-zuul-2.0.0.RELEASE.jar:2.0.0.RELEASE]
at org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter.forward(RibbonRoutingFilter.java:164) ~[spring-cloud-netflix-zuul-2.0.0.RELEASE.jar:2.0.0.RELEASE]
at org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter.run(RibbonRoutingFilter.java:112) ~[spring-cloud-netflix-zuul-2.0.0.RELEASE.jar:2.0.0.RELEASE]
at com.netflix.zuul.ZuulFilter.runFilter(ZuulFilter.java:117) ~[zuul-core-1.3.1.jar:1.3.1]
at com.netflix.zuul.FilterProcessor.processZuulFilter(FilterProcessor.java:193) ~[zuul-core-1.3.1.jar:1.3.1]
at com.netflix.zuul.FilterProcessor.runFilters(FilterProcessor.java:157) ~[zuul-core-1.3.1.jar:1.3.1]
3.2、zuul-gateway模塊追加過濾處理
增加過濾器主要是爲了攔截訪問路勁添加用戶安全校驗信息:
/**
* zuul過利器配置(主要針對用戶安全校驗)
*/
public class AuthorizedRequestFilter extends ZuulFilter {
@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 currentContext = RequestContext.getCurrentContext() ; // 獲取當前請求的上下文
String auth = "admin:admin"; // 認證的原始信息
byte[] encodedAuth = Base64.getEncoder().encode(auth.getBytes(Charset.forName("US-ASCII"))); // 進行一個加密的處理
String authHeader = "Basic " + new String(encodedAuth);
currentContext.addZuulRequestHeader("Authorization", authHeader);
return null;
}
}
其中filterType爲過濾的類型,在進行Zuul過濾的時候可以設置其過濾執行的位置,那麼此時有如下幾種類型:
public static final String ERROR_TYPE = "error";
public static final String POST_TYPE = "post";
public static final String PRE_TYPE = "pre";
public static final String ROUTE_TYPE = "route";
·pre:在請求發出之前執行過濾,如果要進行訪問,肯定在請求前設置頭信息;
·route:在進行路由請求的時候被調用;
·post:在路由之後發送請求信息的時候被調用;
·error:出現錯誤之後進行調用。
3.3、zuul-gateway模塊建立一個配置程序類
@Configuration
public class ZuulConfig {
@Bean
public AuthorizedRequestFilter getAuthorizedRequestFilter() {
return new AuthorizedRequestFilter() ;
}
}
這個時候再次訪問:
http://localhost:9501/chj-api/product-proxy/prodcut/get/1
http://localhost:9501/chj-api/user-proxy/user/get/1
http://localhost:9501/chj-api/user2-proxy/user/get/1
這兩個服務都能正常訪問了。
4、Zuul安全訪問
作爲所有接口的統一門面,zuul也是可以進行加密訪問的
4.1、增加安全訪問模塊
zuul-gateway修改pom文件,增加spring-boot-starter-security依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
4.2、增加用戶配置
zuul-gateway修改application.yml配置文件,增加用戶配置信息:
spring:
application:
name: springcloud-micro-zuul-gateway
security:
user:
name: admin
password: admin
4.3、啓動服務測試
再訪問http://localhost:9501/chj-api/user-proxy/user/get/1
這個時候就需要輸入用戶名密碼了。
二、Feign訪問Zuul
前面學習feign的時候確實已經知道,他其實是去eureka中獲取服務地址的,如果想使用feign來訪問zuul,首先就應該讓zuul註冊到eureka中。
1、zuul-gateway增加eureka相關配置
zuul-gateway模塊修改application.yml文件,添加如下配置信息:
eureka:
client: # 客戶端進行Eureka註冊的配置
service-url:
defaultZone: http://admin:admin@eureka1:7001/eureka,http://admin:admin@eureka2:7002/eureka,http://admin:admin@eureka3:7003/eureka
instance:
instance-id: microcloud-zuul-gateway
prefer-ip-address: true
lease-renewal-interval-in-seconds: 2 # 設置心跳的時間間隔(默認是30秒)
lease-expiration-duration-in-seconds: 5 # 如果現在超過了5秒的間隔(默認是90秒)
2、zuul代理系統所有接口服務訪問
springcloud-micro現在所有的服務都要通過zuul的代理進行訪問,需要在系統公共接口模塊增加接口。
2.1、service-feign公共接口模塊新增接口IZUUlClientService:
@FeignClient(name = "SPRINGCLOUD-MICRO-ZUUL-GATEWAY",configuration = FeignClientConfig.class,
fallbackFactory = IZUUlClientServiceallbackFactory.class)
public interface IZUUlClientService {
// ***********************產品服務模塊接口***********************
@RequestMapping("/chj-api/product-proxy/prodcut/get/{id}")
public Product getProduct(@PathVariable("id")long id);
@RequestMapping("/chj-api/product-proxy/prodcut/list")
public List<Product> listProduct() ;
@RequestMapping("/chj-api/product-proxy/prodcut/add")
public boolean addPorduct(Product product) ;
// ***********************產品服務模塊接口***********************
@RequestMapping("/chj-api/user-proxy/user/get/{name}")
public User getUser(@PathVariable("name")String name);
}
2.2、增加容錯處理實現
新增IZUUlClientServiceallbackFactory,在Zuul由於出現網絡問題失去聯繫後進行容錯處理
/**
* 在Zuul由於出現網絡問題失去聯繫後進行容錯處理
*/
@Component
public class IZUUlClientServiceallbackFactory implements FallbackFactory<IZUUlClientService> {
@Override
public IZUUlClientService create(Throwable throwable) {
return new IZUUlClientService() {
@Override
public Product getProduct(long id) {
Product product = new Product();
product.setProductId(999999L);
product.setProductName("feign-zuulName");
product.setProductDesc("feign-zuulDesc");
return product;
}
@Override
public List<Product> listProduct() {
return null;
}
@Override
public boolean addPorduct(Product product) {
return false;
}
@Override
public User getUser(String name) {
User user = new User();
user.setSex("F");
user.setAge(30);
user.setName("zuul-fllback:"+name);
return user;
}
};
}
}
2.3、服務消費者添加zuul代理接口訪問方法
consumer-hystrix模塊修改ConsumerProductController,增加方法getProductAndUser,訪問接口:
@RestController
@RequestMapping("/consumer")
public class ConsumerProductController {
@Resource
private IProductClientService iProductClientService;
@Resource
private IZUUlClientService izuUlClientService;
@RequestMapping("/product/get")
public Object getProduct(long id) {
return iProductClientService.getProduct(id);
}
@RequestMapping("/product/list")
public Object listProduct() {
return iProductClientService.listProduct();
}
@RequestMapping("/product/add")
public Object addPorduct(Product product) {
return iProductClientService.addPorduct(product);
}
// 通過zuul全局網關調用用戶和產品服務
@RequestMapping("/product/getProductAndUser")
public Object getProductAndUser(long id) {
Map<String,Object> result = new HashMap();
result.put("product",izuUlClientService.getProduct(id));
result.put("user",izuUlClientService.getUser(id+""));
return result;
}
}
2.4、啓動服務測試
依次啓動eureka,user服務,product服務,zuul服務與consumer-feign-hystrix服務。
1)在地址欄輸入:http://localhost/consumer/product/getProductAndUser?id=1
查詢結果如下:
{"product":{"productId":null,"productName":"java編程","productDesc":"springcloud"},
"user":{"name":"1","age":31,"sex":"boy"}}
2)關閉zuul服務繼續訪問:
http://localhost/consumer/product/getProductAndUser?id=1,返回結果:
{"product":{"productId":999999,"productName":"feign-zuulName","productDesc":"feign-zuulDesc"},
"user":{"name":"zuul-fllback:1","age":30,"sex":"F"}}
返回結果都是rollback的數據,說明服務降級已經開啓。
3、Zuul熔斷
zuul是一個代理服務,但如果被代理的服務突然斷了,這個時候zuul上面會有出錯信息,例如,停止product服務,訪問http://localhost:9501/enjoy-api/product-proxy/prodcut/get/1會報錯。
現在服務的調用方已經做了處理,不會出現這樣的錯誤信息,但一般來說,對於zuul本身代理方,也應該進行zuul的降級處理。
3.1、修改uul-gateway模塊建立fallback回退處理類
注意HttpHeaders包的引入import org.springframework.http.HttpHeaders;
@Component
public class ProviderFallback implements FallbackProvider {
@Override
public String getRoute() {
return "*";
}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return new ClientHttpResponse() {
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.set("Content-Type", "text/html; charset=UTF-8");
return headers;
}
@Override
public InputStream getBody() throws IOException {
// 響應體
return new ByteArrayInputStream("產品微服務不可用,請稍後再試。".getBytes());
}
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.BAD_REQUEST;
}
@Override
public int getRawStatusCode() throws IOException {
return HttpStatus.BAD_REQUEST.value();
}
@Override
public String getStatusText() throws IOException {
return HttpStatus.BAD_REQUEST.getReasonPhrase();
}
@Override
public void close() {
}
};
}
}
3.2、重新啓動服務
訪問:http://localhost/consumer/product/getProductAndUser?id=1,返回結果如下:
{"product":{"productId":null,"productName":"java編程","productDesc":"springcloud"},
"user":{"name":"1","age":31,"sex":"boy"}}
關閉product服務返回結果如下:
{"product":{"productId":999999,"productName":"feign-zuulName","productDesc":"feign-zuulDesc"},
"user":{"name":"1","age":31,"sex":"boy"}}
備註:
getRoute方法可以返回服務的ID,比如springcloud-micro-product,如果需要匹配全部適應 “*”。
二、zuul原理解析
第一步:引入依賴,在啓動類中添加@EnableZuulProxy,聲明這是一個Zuul代理。
第二步:註冊到Eureka Server啓動服務,訪問這個端口,url中帶上要請求的服務名。
1、啓動類中添加@EnableZuulProxy
1.引入依賴,在啓動類中添加@EnableZuulProxy,聲明這是一個Zuul代理。
2.註冊到Eureka Server啓動服務,訪問這個端口,url中帶上要請求的服務名。
1.1、zuul默認是支持開啓斷路器(hystrix)
@EnableZuulProxy註解默認引入了@EnableCircuitBreaker,所以zuul默認是支持開啓斷路器(hystrix)
@EnableCircuitBreaker //開啓斷路器(hystrix)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import({ZuulProxyMarkerConfiguration.class})
public @interface EnableZuulProxy {
}
1.2、查看ZuulProxyMarkerConfiguration源碼:
源碼位置:org.springframework.cloud.netflix.zuul.ZuulProxyMarkerConfiguration
@Configuration //讓ZuulProxyMarkerConfiguration 配置生效
public class ZuulProxyMarkerConfiguration {
public ZuulProxyMarkerConfiguration() {
}
//創建Marker 實例,讓ZuulProxyMarkerConfiguration 配置生效
@Bean
public ZuulProxyMarkerConfiguration.Marker zuulProxyMarkerBean() {
return new ZuulProxyMarkerConfiguration.Marker();
}
class Marker {
Marker() {
}
}
}
2、zuul默認啓動類加載配置分析
註冊到Eureka Server啓動服務,訪問這個端口,url中帶上要請求的服務名。
配置類入口:spring-cloud-netflix-zuul-2.0.0.RELEASE.jar!\META-INF\spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.zuul.ZuulServerAutoConfiguration,\
org.springframework.cloud.netflix.zuul.ZuulProxyAutoConfiguration
其中ZuulProxyAutoConfiguration的父類是ZuulServerAutoConfiguration
@Configuration
@Import({RestClientRibbonConfiguration.class, OkHttpRibbonConfiguration.class, HttpClientRibbonConfiguration.class, HttpClientConfiguration.class})
@ConditionalOnBean({Marker.class})
public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration {
2.1、分析方法zuulServlet
代碼位置:org.springframework.boot.web.servlet.ServletRegistrationBean
該方法向ServletRegistrationBean中註冊了一個ZuulServlet對象,而ZuulServlet又繼HttpServlet
//註冊一個zuulServlet
@Bean
@ConditionalOnMissingBean(
name = {"zuulServlet"}
)
public ServletRegistrationBean zuulServlet() {
ServletRegistrationBean<ZuulServlet> servlet = new ServletRegistrationBean(new ZuulServlet(),
new String[]{this.zuulProperties.getServletPattern()});
servlet.addInitParameter("buffer-requests", "false");
return servlet;
}
2.2、ZuulServlet.service方法分析
Zuulfilter有四種過濾方式:pre(前置)、route(路由定位)、post(執行後)、error(出錯)
執行流程如下圖所示:
執行代碼如下面:com.netflix.zuul.http.ZuulServlet.service方法
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
try {
this.init((HttpServletRequest)servletRequest, (HttpServletResponse)servletResponse);
RequestContext context = RequestContext.getCurrentContext();
context.setZuulEngineRan();
try {
this.preRoute(); //執行前置過濾器
} catch (ZuulException var13) {
this.error(var13);
this.postRoute(); //執行後置過濾
return;
}
try {
this.route();
} catch (ZuulException var12) {
this.error(var12);
this.postRoute();
return;
}
try {
this.postRoute();
} catch (ZuulException var11) {
this.error(var11);
}
} catch (Throwable var14) {
this.error(new ZuulException(var14, 500, "UNHANDLED_EXCEPTION_" + var14.getClass().getName()));
} finally {
RequestContext.getCurrentContext().unset();
}
}
2.3、FilterProcessor#runFilters方法分析:
不管是哪一種過濾器,最後都會落到com.netflix.zuul.FilterProcessor#runFilters方法上面
void postRoute() throws ZuulException {
this.zuulRunner.postRoute();
}
void route() throws ZuulException {
this.zuulRunner.route();
}
void preRoute() throws ZuulException {
this.zuulRunner.preRoute();
}
com.netflix.zuul.ZuulRunner代碼:
public void postRoute() throws ZuulException {
FilterProcessor.getInstance().postRoute();
}
public void route() throws ZuulException {
FilterProcessor.getInstance().route();
}
public void preRoute() throws ZuulException {
FilterProcessor.getInstance().preRoute();
}
最後方法處理都在com.netflix.zuul.FilterProcessor中,以方法postRoute爲例:
public void postRoute() throws ZuulException {
try {
this.runFilters("post");
} catch (ZuulException var2) {
throw var2;
} catch (Throwable var3) {
throw new ZuulException(var3, 500, "UNCAUGHT_EXCEPTION_IN_POST_FILTER_" + var3.getClass().getName());
}
}
FilterProcessor.runFilters()方法實現如下:
public Object runFilters(String sType) throws Throwable {
if (RequestContext.getCurrentContext().debugRouting()) {
Debug.addRoutingDebug("Invoking {" + sType + "} type filters");
}
boolean bResult = false;
//據filter類型,獲取排過序的filter
List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType);
if (list != null) {
for(int i = 0; i < list.size(); ++i) {
ZuulFilter zuulFilter = (ZuulFilter)list.get(i);
//處理filter執行
Object result = this.processZuulFilter(zuulFilter);
if (result != null && result instanceof Boolean) {
bResult |= (Boolean)result;
}
}
}
return bResult;
}
接下來查看FilterProcessor.processZuulFilter方法源碼:
public Object processZuulFilter(ZuulFilter filter) throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
boolean bDebug = ctx.debugRouting();
String metricPrefix = "zuul.filter-";
long execTime = 0L;
String filterName = "";
try {
long ltime = System.currentTimeMillis();
filterName = filter.getClass().getSimpleName();
RequestContext copy = null;
Object o = null;
Throwable t = null;
if (bDebug) {
Debug.addRoutingDebug("Filter " + filter.filterType() + " " + filter.filterOrder() + " " + filterName);
copy = ctx.copy();
}
//真正執行ZuulFilter內存方法
ZuulFilterResult result = filter.runFilter();
ExecutionStatus s = result.getStatus();
execTime = System.currentTimeMillis() - ltime;
switch(s) {
case FAILED:
t = result.getException();
ctx.addFilterExecutionSummary(filterName, ExecutionStatus.FAILED.name(), execTime);
break;
case SUCCESS:
o = result.getResult();
ctx.addFilterExecutionSummary(filterName, ExecutionStatus.SUCCESS.name(), execTime);
if (bDebug) {
Debug.addRoutingDebug("Filter {" + filterName + " TYPE:" + filter.filterType() + " ORDER:" + filter.filterOrder() + "} Execution time = " + execTime + "ms");
Debug.compareContextState(filterName, copy);
}
}
if (t != null) {
throw t;
} else {
this.usageNotifier.notify(filter, s);
return o;
}
} catch (Throwable var15) {
if (bDebug) {
Debug.addRoutingDebug("Running Filter failed " + filterName + " type:" + filter.filterType() +
" order:" + filter.filterOrder() + " " + var15.getMessage());
}
this.usageNotifier.notify(filter, ExecutionStatus.FAILED);
if (var15 instanceof ZuulException) {
throw (ZuulException)var15;
} else {
ZuulException ex = new ZuulException(var15, "Filter threw Exception", 500, filter.filterType()
+ ":" + filterName);
ctx.addFilterExecutionSummary(filterName, ExecutionStatus.FAILED.name(), execTime);
throw ex;
}
}
}
2.4、真正執行ZuulFilter的runFilter內存方法
com.netflix.zuul.ZuulFilter#runFilter
public ZuulFilterResult runFilter() {
ZuulFilterResult zr = new ZuulFilterResult();
if (!this.isFilterDisabled()) {
if (this.shouldFilter()) {
Tracer t = TracerFactory.instance().startMicroTracer("ZUUL::" + this.getClass().getSimpleName());
try {
//執行,run方法就是具體filter 實現的run方法
Object res = this.run();
zr = new ZuulFilterResult(res, ExecutionStatus.SUCCESS);
} catch (Throwable var7) {
t.setName("ZUUL::" + this.getClass().getSimpleName() + " failed");
zr = new ZuulFilterResult(ExecutionStatus.FAILED);
zr.setException(var7);
} finally {
t.stopAndLog();
}
} else {
zr = new ZuulFilterResult(ExecutionStatus.SKIPPED);
}
}
return zr;
}
通過查看源碼發現run方法是在IZuulFilter接口中定義的,而前面我們搭建zuul模塊的時候就實現了該接口的run方法:
public interface IZuulFilter {
boolean shouldFilter();
Object run() throws ZuulException;
}
2.5、最後回到了我們自定義的AuthorizedRequestFilter上面
/**
* zuul過利器配置(主要針對用戶安全校驗)
*/
public class AuthorizedRequestFilter extends ZuulFilter {
@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 currentContext = RequestContext.getCurrentContext() ; // 獲取當前請求的上下文
String auth = "admin:admin"; // 認證的原始信息
byte[] encodedAuth = Base64.getEncoder().encode(auth.getBytes(Charset.forName("US-ASCII"))); // 進行一個加密的處理
String authHeader = "Basic " + new String(encodedAuth);
currentContext.addZuulRequestHeader("Authorization", authHeader);
return null;
}
}