第六章 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网关路由及源码分析笔记

发现启动报错,其实这是因为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

 

第六章 Zuul网关路由及源码分析笔记

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(出错)

执行流程如下图所示:

 

第六章 Zuul网关路由及源码分析笔记

执行代码如下面: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;
    }
}