zuul配置请求拦截_SpringCloud整合Zuul源码分析

回顾

  1. Zuul是通过ZuulServletFilter或者 ZuulServlet接管我们的请求
  2. Zuul整个流程如下:ZuulServletFilter(ZuulServlet) -> ZuulRunner -> FilterProcessor -> ZuulFilter

目标

明确SpringMVC和Zuul框架是怎么配合的

引入Zuul的版本信息

<properties>
 <spring-cloud.version>Hoxton.RELEASE</spring-cloud.version>
</properties>

<dependencyManagement>
 <dependencies>
   <dependency>
     <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-dependencies</artifactId>
      <version>${spring-cloud.version}</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>
    
<dependencies>
 <dependency>
   <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
   </dependency>
</dependencies>

Zuul功能启用及配置的加载

Zuul的启用 - @EnableZuulProxy

// 引入断路器功能
@EnableCircuitBreaker
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
// 注入触发Zuul配置类的标记Bean
@Import(ZuulProxyMarkerConfiguration.class)
public @interface EnableZuulProxy {
}

ZuulProxyAutoConfiguration - Zuul自动配置Bean

// 此配置类不会被代理
@Configuration(proxyBeanMethods = false)
// 引入Ribbon相关配置
@Import({ RibbonCommandFactoryConfiguration.RestClientRibbonConfiguration.class,
  RibbonCommandFactoryConfiguration.OkHttpRibbonConfiguration.class,
  RibbonCommandFactoryConfiguration.HttpClientRibbonConfiguration.class,
  HttpClientConfiguration.class })
@ConditionalOnBean(ZuulProxyMarkerConfiguration.Marker.class)
public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration {

 // 省略部分代码。。。

 // 加载pre filters bean
 @Bean
 @ConditionalOnMissingBean(PreDecorationFilter.class)
 public PreDecorationFilter preDecorationFilter(RouteLocator routeLocator,
   ProxyRequestHelper proxyRequestHelper) {
  return new PreDecorationFilter(routeLocator,
    this.server.getServlet().getContextPath(), this.zuulProperties,
    proxyRequestHelper);
 }

 // 加载route filters bean
 @Bean
 @ConditionalOnMissingBean(RibbonRoutingFilter.class)
 public RibbonRoutingFilter ribbonRoutingFilter(ProxyRequestHelper helper,
   RibbonCommandFactory<?> ribbonCommandFactory) {
  RibbonRoutingFilter filter = new RibbonRoutingFilter(helper, ribbonCommandFactory,
    this.requestCustomizers);
  return filter;
 }

  // 加载route filters bean
 @Bean
 @ConditionalOnMissingBean({ SimpleHostRoutingFilter.class,
   CloseableHttpClient.class })
 public SimpleHostRoutingFilter simpleHostRoutingFilter(ProxyRequestHelper helper,
   ZuulProperties zuulProperties,
   ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
   ApacheHttpClientFactory httpClientFactory) {
  return new SimpleHostRoutingFilter(helper, zuulProperties,
    connectionManagerFactory, httpClientFactory);
 }
}

ZuulServerAutoConfiguration - Zuul自动配置Bean

@Configuration(proxyBeanMethods = false)
// 加载zuul的自定义properties配置
@EnableConfigurationProperties({ ZuulProperties.class })
// 加载前提:classpath下有类ZuulServlet和ZuulServletFilter
@ConditionalOnClass({ ZuulServlet.class, ZuulServletFilter.class })
@ConditionalOnBean(ZuulServerMarkerConfiguration.Marker.class)
public class ZuulServerAutoConfiguration {

 // 省略部分代码。。。

  // ZuulController是Controller的一个实现,负责将拦截的请求交给ZuulServlet处理
 @Bean
 public ZuulController zuulController() {
  return new ZuulController();
 }

  // ZuulHandlerMapping负责路由匹配
 @Bean
 public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes,
   ZuulController zuulController) {
  ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController);
  mapping.setErrorController(this.errorController);
  mapping.setCorsConfigurations(getCorsConfigurations());
  return mapping;
 }

  // 默认加载ZuulServlet
 @Bean
 @ConditionalOnMissingBean(name = "zuulServlet")
 @ConditionalOnProperty(name = "zuul.use-filter", havingValue = "false",
   matchIfMissing = true)
 public ServletRegistrationBean zuulServlet() {
  ServletRegistrationBean<ZuulServlet> servlet = new ServletRegistrationBean<>(
    new ZuulServlet(), this.zuulProperties.getServletPattern());
  // The whole point of exposing this servlet is to provide a route that doesn't
  // buffer requests.
  servlet.addInitParameter("buffer-requests", "false");
  return servlet;
 }

  // 当配置zuul.use-filter=true,加载zuulServletFilter, 表示用filter来拦截请求
 @Bean
 @ConditionalOnMissingBean(name = "zuulServletFilter")
 @ConditionalOnProperty(name = "zuul.use-filter", havingValue = "true",
   matchIfMissing = false)
 public FilterRegistrationBean zuulServletFilter() {
  final FilterRegistrationBean<ZuulServletFilter> filterRegistration = new FilterRegistrationBean<>();
  filterRegistration.setUrlPatterns(
    Collections.singleton(this.zuulProperties.getServletPattern()));
  filterRegistration.setFilter(new ZuulServletFilter());
  filterRegistration.setOrder(Ordered.LOWEST_PRECEDENCE);
  // The whole point of exposing this servlet is to provide a route that doesn't
  // buffer requests.
  filterRegistration.addInitParameter("buffer-requests", "false");
  return filterRegistration;
 }

 // 在Zuul各阶段filter处理过程中捕获异常,SendErrorFilter会forward "/error" 
 @Bean
 public SendErrorFilter sendErrorFilter() {
  return new SendErrorFilter();
 }

 @Configuration(proxyBeanMethods = false)
 protected static class ZuulFilterConfiguration {

    // 注入Spring容器中的ZuulFilter类型所有的实现类,包括内置和自定义的Filter,内置的有10个
  @Autowired
  private Map<String, ZuulFilter> filters;

    // 注册ZuulFilter到FilterRegistry中
  @Bean
  public ZuulFilterInitializer zuulFilterInitializer(CounterFactory counterFactory,
    TracerFactory tracerFactory) {
   FilterLoader filterLoader = FilterLoader.getInstance();
   FilterRegistry filterRegistry = FilterRegistry.instance();
   return new ZuulFilterInitializer(this.filters, counterFactory, tracerFactory,
     filterLoader, filterRegistry);
  }
 }
}

以上两个类,加载了Zuul的相关配置类:

  • 拦截请求:
    • 和SpringMVC结合的Bean:ZuulControllerZuulHandlerMapping
    • 通过Web Filter拦截请求Bean:ZuulServletFilter
  • Zuul流程需要的Bean:
    • 自带的ZuulFilter,有10个下面会一一介绍
    • 监控相关
    • ZuulFilter的容器:FilterRegistry

默认的ZuulFilters

Pre Filter

  1. ServletDetectionFilter order = -3
    作用:判断请求是否是由DispatcherServlet or ZuulServlet传来的,并把判断结果以键值对的形式放在RequestContext
  2. Servlet30WrapperFilter order = -2
    作用:包装request,兼容servlet3.0
  3. FormBodyWrapperFilter order = -1
    作用:包装表单数据并为下游服务重新编码
  4. DebugFilter order = 1
    作用:如果debug请求,那么会在RequestContext中标记为debug请求和routing
  5. PreDecorationFilter = 5
    作用:请求路由和zuul路由配置进行匹配,并设置与代理相关的头部信息

Route Filter

  1. RibbonRoutingFilter order = 10
    作用:使用Ribbon、Hytrix和可插拔的httpClient发送请求,serviceId、是否重试以及负载均衡策略在相关联的RequestContext获取
  2. SimpleHostRoutingFilter order = 100
    作用:用HttpClient发送请求到预定的URLs,URLs通过RequestContext#getRouteHost()获取
  3. SendForwardFilter order = 500
    作用:用RequestDispatcherforwards请求,转发的地址是RequestContextFilterConstants#FORWARD_TO_KEY对应value

Post Filter

  1. SendResponseFilter order = 1000
    作用:写 代理的请求得到的响应 到 当前响应

Error Filter

  1. SendErrorFilter order = 0
    作用:如果RequestContext#getThrowable() 不为空,默认将请求转发到 /error

SpringMVC怎么把请求转发给Zuul?

从配置类分析

从上述配置可以看下几个重要的配置类源码:

ZuulController

public class ZuulController extends ServletWrappingController {

 public ZuulController() {
    // 设置Servlet的类型
  setServletClass(ZuulServlet.class);
  setServletName("zuul");
  setSupportedMethods((String[]) null); // Allow all
 }

 @Override
 public ModelAndView handleRequest(HttpServletRequest request,
   HttpServletResponse response) throws Exception {
  try {
   // We don't care about the other features of the base class, just want to
   // handle the request
   return super.handleRequestInternal(request, response);
  }
  finally {
   // @see com.netflix.zuul.context.ContextLifecycleFilter.doFilter
   RequestContext.getCurrentContext().unset();
  }
 }

}

ServletWrappingController

public class ServletWrappingController extends AbstractController implements BeanNameAware, InitializingBean, DisposableBean {
  // 省略代码。。
  
 @Override
  public void afterPropertiesSet() throws Exception {
   if (this.servletClass == null) {
    throw new IllegalArgumentException("'servletClass' is required");
   }
   if (this.servletName == null) {
    this.servletName = this.beanName;
   }
      // 通过反射 初始化servlet
   this.servletInstance = ReflectionUtils.accessibleConstructor(this.servletClass).newInstance();
   this.servletInstance.init(new DelegatingServletConfig());
  }

  // 通过servlet实例处理请求
  @Override
  protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
  throws Exception {
   Assert.state(this.servletInstance != null, "No Servlet instance");
   this.servletInstance.service(request, response);
   return null;
  }
}

ZuulHandlerMapping

public class ZuulHandlerMapping extends AbstractUrlHandlerMapping {
   private final ZuulController zuul;
   private volatile boolean dirty = true;

  // 根据寻找路由处理器
 @Override
 protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
  if (this.errorController != null && urlPath.equals(this.errorController.getErrorPath())) {
   return null;
  }
    // 如果属于配置的忽视路由,则返回null
  if (isIgnoredPath(urlPath, this.routeLocator.getIgnoredPaths())) return null;
  RequestContext ctx = RequestContext.getCurrentContext();
  if (ctx.containsKey("forward.to")) {
   return null;
  }
  if (this.dirty) {
      // dirty默认为true,第一次会触发注册处理器到Spring容器中
      // 或者发送zuul路由刷新事件,设置dirty为true,见ZuulRefreshListener
   synchronized (this) {
    if (this.dirty) {
     registerHandlers();
     this.dirty = false;
    }
   }
  }
    // 交给Spring查找路由对应的handler
  return super.lookupHandler(urlPath, request);
 }

  // 注册配置路由对应的处理器
 private void registerHandlers() {
  Collection<Route> routes = this.routeLocator.getRoutes();
  if (routes.isEmpty()) {
   this.logger.warn("No routes found from RouteLocator");
  }
  else {
   for (Route route : routes) {
        // 在Spring容器中注册zuul路由配置对应ZuulController处理器
    registerHandler(route.getFullPath(), this.zuul);
   }
  }
 }

}

以上配置类:

  • ZuulController:它是ServletWrappingController的 子类,其属性包含了ZuulServlet
  • ZuulHandlerMapping:它是AbstractUrlHandlerMapping的子类
  • ZuulServlet:由上一篇知道它是Zuul流程的入口之一
回顾SpingMVC对于请求的处理流程
  1. 客户端请求交给SpringMVC的DispatcherServlet统一处理
  2. 通过已经注册的HandlerMapping, 根据请求路由找到处理器执行链HandlerExecutionChain,包括请求各个拦截器HandlerInterceptor和请求处理器handler
  3. 找到请求处理器对应的适配器HandlerAdapter
  4. 执行已注册的各拦截器的preHandle方法
  5. 调用处理器处理请求,返回模型数据以及视图ModelAndView
  6. 执行已注册的各拦截器的postHandle方法
  7. 根据给定的ModelAndView进行渲染
  8. 响应客户端

结合SpingMVC对于请求的处理流程可以猜到,当请求给到SpringMVC的DispatcherServlet后,如果该路由是需要Zuul拦截的请求,那么会匹配到ZuulHandlerMapping,从而找到处理器ZuulController,之后在处理的时候,会交给ZuulServlet,后面的流程见上一篇文章。

Debug验证

zuul拦截配置:

# zuul
# 是否启用ZuulServletFilter
# zuul.use-filter=true
ribbon.ConnectTimeout = 30000
ribbon.ReadTimeout = 30000
ribbon.eureka.enabled = false

management.endpoints.web.exposure.include = *
zuul.routes.test.path = /test/**
zuul.routes.test.stripPrefix = false
test.ribbon.listOfServers = ${service.test}
service.test=http://127.0.0.1:8081/t/test

请求:curl -v http://127.0.0.1:8080/test

图示过程:

a85f49d1931c5dbecfb3da520538f5d6.png
匹配ZuulController

9cb13d57ac9e183ad1767e17e49ca0e6.png
执行ZuulServlet

结果显示:猜想是正确的。 大致流程:DispatcherServlet -> ZuulController -> ZuulServlet -> 执行各阶段ZuulFilters

cca6aae2caef50f0ba14da271a44a729.png
ZuulServlet接管流程图

ZuulServletFilter - 另一种拦截请求流程

配置

 // 在类ZuulServerAutoConfiguration中加载

 @Bean
 @ConditionalOnMissingBean(name = "zuulServletFilter")
 @ConditionalOnProperty(name = "zuul.use-filter", havingValue = "true",
   matchIfMissing = false)
 public FilterRegistrationBean zuulServletFilter() {
  final FilterRegistrationBean<ZuulServletFilter> filterRegistration = new FilterRegistrationBean<>();
    // URL匹配规则: /zuul
  filterRegistration.setUrlPatterns(
    Collections.singleton(this.zuulProperties.getServletPattern()));
  filterRegistration.setFilter(new ZuulServletFilter());
  filterRegistration.setOrder(Ordered.LOWEST_PRECEDENCE);
  filterRegistration.addInitParameter("buffer-requests", "false");
  return filterRegistration;
 }

ZuulServletFilter的URL匹配规则是/zuul, 而且如果要是使得ZuulServletFilterBean加载,必须在配置文件中,添加:zuul.use-filter=true,如图:

# 是否启用filter拦截
zuul.use-filter=true
zuul.routes.test.path = /zuul/test/**
zuul.routes.test.stripPrefix = false
test.ribbon.listOfServers = ${service.test}
service.test=http://127.0.0.1:8081

ZuulServletFilter源码

public class ZuulServletFilter extends com.netflix.zuul.filters.ZuulServletFilter {
 @Override
 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
   FilterChain filterChain) throws IOException, ServletException {
  RequestContext context = RequestContext.getCurrentContext();
  context.setZuulEngineRan();
  super.doFilter(servletRequest, servletResponse, filterChain);
 }
}

源码很简单,在请求上下文添加了一个标志位zuulEngineRan为true。并执行父类com.netflix.zuul.filters.ZuulServletFilterdoFilter方法,进而进入了Zuul的核心流程当中,后面的流程我们已经熟悉了。

其中要注意下,com.netflix.zuul.filters.ZuulServletFilter虽然是Filter,但是并没有在其doFilter方法中调用FilterChaindoFilter方法,我们可以回想下,如果是我们自己写FIlter,一定会调用。之所以ZuulServletFilte没有这么做,是因为它要接管请求,并不要Servlet来处理。

38e2262f8927bfd425ce41705ffc1261.png
已注册的ZuulServletFilter

大致流程如图:

49c605e348e5525ebf55ad7810aeefea.png
ZuulServletFilter接管流程图

总结

Zuul和Spring结合并接管请求主要有两种方式:

  • 在Spring容器中通过注册请求处理器ZuulController和路由处理器的映射ZuulHandlerMapping,做到请求的拦截,并内置了一些ZuulFIlter保证请求的处理。
  • 通过注册ZuulServletFilter,使用Filter方式接管请求,注意默认的路径匹配及生效配置
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spring Cloud Zuul是一个基于Netflix Zuul的微服务网关,它提供了动态路由、监控、弹性、安全等功能。在使用Spring Cloud Zuul时,需要进行一些配置,包括路由配置、过滤器配置、负载均衡配置等。具体配置可以参考Spring Cloud官方文档。 ### 回答2: Spring Cloud Zuul是基于Netflix Zuul的一种微服务网关解决方案。它提供了一种动态路由、监控、弹性、安全性、负载均衡等功能,可以向外暴露API,以及将请求转发给底层的微服务。 Zuul可以进行横向扩展,只需添加更多的Zuul实例即可轻松实现。Zuul和Spring Cloud配合使用,可以提供了完整的微服务架构解决方案。 在配置Zuul时,需要注意以下几个方面: 1.创建Zuul应用程序: 可以通过使用Spring Initializr轻松创建新的Zuul应用程序。在创建过程中,只需选择相关选项即可。例如,在创建Spring Boot应用程序时,可以勾选Zuul选项,即可为您创建一个包含Zuul配置的应用程序。 2.配置Zuul路由: 要配置路由,需要编辑application.yml文件。在该文件中,可以为每个服务配置一个路由规则。例如,如果要将请求转发到特定的微服务,则可以配置以下路由规则: ``` zuul: routes: users: /myusers/** ``` 在这个例子中,我们设置了名为“users”的服务,它使用“/myusers/**”作为前缀路径。这意味着所有以“/myusers”开头的请求都将转发到名为“users”的服务。 3.配置Zuul过滤器: Zuul还提供了过滤器,可以用来进行请求和响应处理。使用Zuul过滤器可以实现以下功能: 认证和安全性 审计和日志记录 传输压缩 动态路由 在Zuul中,过滤器是完全可配置的,可以使用Java代码来实现自定义过滤器。 4.配置Zuul负载均衡: Zuul还提供了负载均衡的功能,它可以将请求分发到多个可用的服务实例中。Zuul可以与Spring Cloud中的Eureka服务发现和注册系统结合使用,从而可以找到运行在各个地方的服务的实例。通过使用Eureka,Zuul可以轻松地访问可用的服务,并使用负载均衡算法将请求分发到不同的实例中。 综上所述,Zuul是一种强大的微服务网关解决方案,可以通过动态路由、监控、弹性、安全性、负载均衡等功能提供API以及代理到底层微服务。要配置Zuul,需要创建应用程序、配置路由、配置过滤器和配置负载均衡等。 ### 回答3: Spring Cloud Zuul是Spring Cloud微服务架构中的一个API网关服务,主要用于统一管理服务路由和访问控制。下面我将介绍如何进行Spring Cloud Zuul配置。 1. 添加依赖 在pom.xml中添加如下依赖: ```xml <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> ``` 2. 配置Zuul路由规则 在配置文件中添加如下配置: ```yaml zuul: routes: app1: path: /app1/** serviceId: service1 app2: path: /app2/** serviceId: service2 ``` 上述配置表示,以`/app1`开头的请求将路由到名为`service1`的微服务,以`/app2`开头的请求将路由到名为`service2`的微服务。 3. 配置Zuul过滤器 在配置文件中添加如下配置: ```yaml zuul: routes: app1: path: /app1/** serviceId: service1 app2: path: /app2/** serviceId: service2 filters: pre-filter: pre: add-header-filter: name: X-Header-Param value: zuul-proxy ``` 上述配置表示,添加一个名为`pre-filter`的前置过滤器,在请求被路由之前将在请求头中添加一个名为X-Header-Param的参数,并赋值为`zuul-proxy`。 4. 集成Ribbon负载均衡 在配置文件中添加如下配置: ```yaml service1: ribbon: listOfServers: http://localhost:8081, http://localhost:8082 service2: ribbon: eureka: enabled: true ``` 上述配置表示,对于名为`service1`的微服务,采用指定的服务器列表进行负载均衡;对于名为`service2`的微服务,则启用Eureka进行负载均衡。 在完成以上配置之后,就可以通过Zuul来进行微服务的路由和访问控制了,通过配置过滤器和负载均衡,可以对请求进行更加灵活和高效的处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值