第六章 Zuul网关路由及源码分析笔记
前面所有的微服务都是通过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:[email protected]eureka1:7001/eureka,http://admin:admin@eureka2:7002/eureka,http://admin:[email protected]: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:[email protected]eureka1:7001/eureka,http://admin:admin@eureka2:7002/eureka,http://admin:[email protected]: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;
}
}