SpringCloud 学习笔记系列 05----Zuul 网关

本文深入探讨Zuul网关的功能与配置,包括路由、动态路由、服务迁移、负载均衡等特性,以及如何实现认证、审查与监控、压力测试、金丝雀测试等操作。同时,介绍了Zuul的规则引擎、跨域配置、熔断策略、超时设置、文件上传处理、查询字符串编码等高级功能。
摘要由CSDN通过智能技术生成

Zuul 网关

  • 路由在微服务体系结构中的一个组成部分;Zuul是Netflix的基于JVM的路由和服务器端负载均衡器。
  • 可以使用Zuul进行一下操作:
    • Authentication(认证)
      • 识别面向各类资源的验证要求并拒绝那些与要求不符的请求。
    • Insights(审查与监控)
      • 在边缘位置追踪有意义数据及统计结果,从而为我们带来准确的生产状态结论。
    • Stress Testing(压力测试)
      • 逐渐增加指向集群的负载流量,从而计算性能水平。
    • Canary Testing(金丝雀测试)
    • Dynamic Routing(动态路由)
      • 以动态方式根据需要将请求路由至不同后端集群处。
    • Service Migration(服务迁移)
    • Load Shedding(负载均衡)
    • Security(安全)
    • Static Response handling(静态响应处理)
      • 在边缘位置直接建立部分响应,从而避免其流入内部集群。
    • Automatic/Active traffic management(自动/手动流量管理)
  • Zuul的规则引擎允许基本上写任何JVM语言编写规则和过滤器,内置Java和Groovy。
  • 注意:
    • 配置属性zuul.max.host.connections已被两个新属性zuul.host.maxTotalConnections和zuul.host.maxPerRouteConnections替换,分别默认为200和20。
    • 所有路由的默认Hystrix隔离模式(ExecutionIsolationStrategy)为SEMAPHORE。如果首选隔离模式,则zuul.ribbonIsolationStrategy可以更改为THREAD。
Zuul网关配置
  • 配置跨域

    • 默认情况下,Zuul将所有跨源请求路由到服务。如果你想让Zuul来处理这些请求,可以提供定制的WebMvcConfigurer bean:
    •   @Bean
        public WebMvcConfigurer corsConfigurer() {
            return new WebMvcConfigurer() {
                public void addCorsMappings(CorsRegistry registry) {
                    registry.addMapping("/path-1/**")
                            .allowedOrigins("http://allowed-origin.com")
                            .allowedMethods("GET", "POST");
                }
            };
        }
      
    • allowedOrigins,allowedMethods,allowedHeaders,exposedHeaders,allowCredentials and maxAge via this configuration.
  • pom.xml

    •   <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
      
  • application.yml

    •   spring:
          application:
            name: hyq-zuul-server
          profiles:
            active: instance1
      
        # setting up spring eureka service discovery
        eureka:
          instance:
            prefer-ip-address: true
            instance-id: ${spring.application.name}:${server.port}
      
        # 路由配置:实际开发过程中放到config配置中心
        zuul:
          routes:
             # 别名
            eureka-client:
              # 通配符说明:
              #   ?: 匹配任意单字符 eg:/client/? 匹配 /client/a  /client/b
              #   *: 匹配任意数量字符 eg: /client/* 匹配 /client/user
              #  **: 匹配任意数量字符,支持多久目录 eg: /client/** 匹配 /client/user/info
              path: /client/**
              serviceId: HYQ-EUREKA-CLIENT  #如果是/ribbon/**路径下的请求,则跳转到service-ribbon
            eureka-server:
              path: /server/**
              serviceId: HYQ-HSYTRIX-CLIENT  #如果是/feign/**路径下的请求,则跳转到service-feign
          # 使用zuul.ignored-services配置需要忽略的服务,多个用逗号分隔
          ignored-services: '*' #只代理配置的微服务
          ignored-patterns: /client/** # 忽略所有包含client/** 的路径
        # 暴露 routes 接口:默认暴露health,info
        # 查看以及管理Zuul的路由:http://localhost:4001/actuator/routes
        management:
          endpoints:
              web:
                exposure:
                  include: routes
      
        ---
        spring:
          profiles: instance1
      
        server.port: 4001
      
        eureka:
          client:
            serviceUrl:
        #      defaultZone: http://localhost:8081/eureka/,http://localhost:8082/eureka/,http://localhost:8083/eureka/
              defaultZone: http://admin:admin@localhost:8081/eureka/
      
        # 配默认熔断超时策略
        # zuul配置了熔断fallback的话,熔断超时也要配置,
        # 不然如果你配置的ribbon超时时间大于熔断的超时,那么会先走熔断,相当于你配的ribbon超时就不生效了。
        hystrix:
          command:
            default:
              execution:
                isolation:
                  thread:
                    timeoutInMilliseconds: 60000
      
        # ribbon超时时间设置
        # zuul.host.connect-timeout-millis, zuul.host.socket-timeout-millis这两个配置,
        # 这两个和上面的ribbon都是配超时的。区别在于,如果路由方式是serviceId的方式,那么ribbon的生效,
        # 如果是url的方式,则zuul.host开头的生效。(此处重要!使用serviceId路由和url路由是不一样的超时策略)
        ribbon:
          ReadTimeout: 60000
          ConnectTimeout: 60000
      
        # 如果要指定具体某个服务配置超时时间: 就将default改为对应的 server_id即可 eg:
        # hystrix.command.user_server.execution.isolation.thread.timeoutInMilliseconds: 6000
        # user_server.ribbon.ReadTimeout: 6000
      
  • zuul.routes.eureka-client.url: http://localhost:6002/eureka-client,http://localhost:6003/feign-client

    • 这些简单的URL路由不会被执行为HystrixCommand,也不能使用Ribbon对多个URL进行负载平衡。为此,请指定service-route并为serviceId配置Ribbon客户端
    • 方法一:指定服务路由并为serviceId配置一个Ribbon客户端
    •   zuul:
          routes:
            users:
              path: /myusers/**
              serviceId: users
        # 在Ribbon中禁用Eureka使用
        ribbon:
          eureka:
            enabled: false
      
        users:
          ribbon:
            listOfServers: example.com,google.com
      
    • 方法二:使用静态服务器列表指定serviceId
    •   zuul:
          routes:
            echo:
              path: /myusers/**
              serviceId: myusers-service
              stripPrefix: true
      
        hystrix:
          command:
            myusers-service:
              execution:
                isolation:
                  thread:
                    timeoutInMilliseconds: ...
      
        myusers-service:
          ribbon:
            NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
            listOfServers: http://example1.com,http://example2.com
            ConnectTimeout: 1000
            ReadTimeout: 3000
            MaxTotalHttpConnections: 500
            MaxConnectionsPerHost: 100
      
      
  • Zuul Http客户端

    • zuul使用的默认HTTP客户端现在由Apache HTTP Client支持,而不是不推荐使用的Ribbon RestClient。
    • 要分别使用RestClient或使用okhttp3.OkHttpClient集合ribbon.restclient.enabled=true或ribbon.okhttp.enabled=true。
  • Cookie和敏感标题

    • application.yml
    •    zuul:
          routes:
            users:
              path: /myusers/**
              sensitiveHeaders: Cookie,Set-Cookie,Authorization
              url: https://downstream
      
    • sensitiveHeaders是一个黑名单,默认值不为空,所以要使Zuul发送所有标题(“被忽略”除外),您必须将其显式设置为空列表。如果您要将Cookie或授权标头传递到后端,这是必要的。
    • 除了每个路由的敏感标头,您还可以为与下游服务交互期间应该丢弃的值(请求和响应)设置全局值为zuul.ignoredHeaders。默认情况下,如果Spring安全性不在类路径上,则它们是空的,否则它们被初始化为由Spring Security指定的一组众所周知的“安全性”头(例如涉及缓存)。在这种情况下的假设是下游服务可能也添加这些头,我们希望代理的值。为了不丢弃这些众所周知的安全标头,只要Spring安全性在类路径上,您可以将zuul.ignoreSecurityHeaders设置为false。如果您禁用Spring安全性中的HTTP安全性响应头,并希望由下游服务提供的值,这可能很有用
  • 通过Zuul上传文件

    • @EnableZuulProxy您可以使用代理路径上传文件,只要文件很小,它就应该工作。
    • 对于大文件,有一个替代路径绕过“/ zuul / *”中的Spring DispatcherServlet(以避免多部分处理)。也就是说,如果zuul.routes.customers=/customers/**则可以将大文件发送到“/ zuul / customers / *”。servlet路径通过zuul.servletPath进行外部化。
    • application.yml
    •   hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 60000
        ribbon:
          ConnectTimeout: 3000
          ReadTimeout: 60000
      
  • 查询字符串编码

    • 要强制查询字符串的原始编码,可以将特殊标志传递给ZuulProperties,以便查询字符串与HttpServletRequest::getQueryString方法相同:
    • application.yml
    •   zuul:
          forceOriginalQueryStringEncoding: true
      
    • 此特殊标志仅适用于SimpleHostRoutingFilter,您可以使用RequestContext.getCurrentContext().setRequestQueryParams(someOverriddenParameters)轻松覆盖查询参数,因为查询字符串现在直接在原始的HttpServletRequest上获取。
Zuul提供Hystrix回退
  • 当Zuul中给定路由的电路跳闸时,您可以通过创建类型为ZuulFallbackProvider的bean来提供回退响应。在这个bean中,您需要指定回退的路由ID,并提供返回的ClientHttpResponse作为后备。
  •   package com.hyq.zuulserver.config;
    
      import com.netflix.zuul.context.RequestContext;
      import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
      import org.springframework.http.HttpHeaders;
      import org.springframework.http.HttpStatus;
      import org.springframework.http.MediaType;
      import org.springframework.http.client.ClientHttpResponse;
      import org.springframework.stereotype.Component;
    
      import javax.servlet.http.HttpServletRequest;
      import java.io.ByteArrayInputStream;
      import java.io.IOException;
      import java.io.InputStream;
    
      /**
       * Hystrix熔断
       */
      @Component
      public class ZuulFallBackProvider implements FallbackProvider {
          /**
           * 需要熔断的  路由: 如果是全部路由即:return "*" 或者 return null;
           * @return
           */
          @Override
          public String getRoute() {
      //        return "HYQ-HSYTRIX-CLIENT";
              return "*";
          }
    
          @Override
          public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
              return new ClientHttpResponse() {
                  /**
                   * 响应 Header
                   * @return Header
                   */
                  @Override
                  public HttpHeaders getHeaders() {
                      HttpHeaders headers = new HttpHeaders();
                      headers.setContentType(MediaType.APPLICATION_JSON);
                      return headers;
                  }
    
                  /**
                   * 响应体:Body
                   * @return Body
                   * @throws IOException 异常
                   */
                  @Override
                  public InputStream getBody() throws IOException {
                      RequestContext context = RequestContext.getCurrentContext();
                      HttpServletRequest request = context.getRequest();
                     String resp =  request.getRequestURI()+"=="+request.getMethod()+"=="+request.getRequestedSessionId();
                      return new ByteArrayInputStream(resp.getBytes());
                  }
    
                  /**
                   * 状态码
                   * @return 状态码
                   * @throws IOException 异常
                   */
                  @Override
                  public HttpStatus getStatusCode() throws IOException {
                      return HttpStatus.OK;
                  }
                  /**
                   * 状态码
                   * @return 状态码
                   * @throws IOException 异常
                   */
                  @Override
                  public int getRawStatusCode() throws IOException {
                      return 200;
                  }
                  /**
                   * 状态码
                   * @return 状态码
                   * @throws IOException 异常
                   */
                  @Override
                  public String getStatusText() throws IOException {
                      return "OK";
                  }
    
                  @Override
                  public void close() {
    
                  }
              };
          }
      }
    
    
Filter 过滤器
  • 代码示例1:

    •   package com.hyq.zuulserver.config;
        
        import com.netflix.zuul.ZuulFilter;
        import com.netflix.zuul.context.RequestContext;
        import com.netflix.zuul.exception.ZuulException;
        import org.springframework.stereotype.Component;
        
        import javax.servlet.http.HttpServletRequest;
        import javax.servlet.http.HttpServletResponse;
        
        import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
        import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.SEND_ERROR_FILTER_ORDER;
        
        /**
         * 过滤器:
         */
        @Component
        public class ApiGatewayFilter extends ZuulFilter {
            /**
             * 过滤器的类型:它决定过滤器在请求的哪个生命周期中执行
             * 按类型对筛选器进行分类。Zuul中的标准类型是“pre”,用于预路由过滤,
             * *“route”用于路由到一个原点,“post”用于路由后过滤器,“error”用于错误处理。
             * *我们还支持静态响应的“static”类型,参见StaticResponseFilter。
             * *通过调用FilterProcessor.runFilters(type)来创建或添加和运行任何filterType
             *
             * @return 枚举值:pre, route, post, error,static
             */
            @Override
            public String filterType() {
                return PRE_TYPE;
            }
        
            /**
             * 过滤器执行的顺序:当请求在一个阶段中存在多个过滤器时,需要根据该方法返回的值来依次执行
             *
             * @return 优先级, 0是最高优先级即最先执行
             */
            @Override
            public int filterOrder() {
                return SEND_ERROR_FILTER_ORDER;
            }
        
            /**
             * 判断该过滤器是否需要被执行
             *
             * @return 写逻辑,是否需要执行过滤。true会执行run函数,false不执行run函数
             */
            @Override
            public boolean shouldFilter() {
                return true;
            }
        
            /**
             * 过滤器的具体逻辑。
             *
             * @return 结果
             * @throws ZuulException 异常
             */
            @Override
            public Object run() throws ZuulException {
                System.out.println("Zuul Filter Is Working...");
                RequestContext ctx = RequestContext.getCurrentContext();
                HttpServletRequest request = ctx.getRequest();
                System.out.println("Zuul LOG: " + request.getMethod() + "====" + request.getRequestURI());
                return null;
            }
        
        }
      
      
  • 代码示例2:

    •   package com.hyq.zuulserver.config;
        
        import com.netflix.zuul.ZuulFilter;
        import com.netflix.zuul.context.RequestContext;
        import org.springframework.stereotype.Component;
        
        import javax.servlet.http.HttpServletResponse;
        import java.util.UUID;
        
        import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.POST_TYPE;
        import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.SEND_RESPONSE_FILTER_ORDER;
        
        /**
         * 后置过滤器(post 过滤器):过滤器添加了一个随机UUID作为X-Sample头文件:
         */
        @Component
        public class MyResponseHeaderFilter extends ZuulFilter {
            @Override
            public String filterType() {
                return POST_TYPE;
            }
        
            @Override
            public int filterOrder() {
                return SEND_RESPONSE_FILTER_ORDER - 1;
            }
        
            @Override
            public boolean shouldFilter() {
                return true;
            }
        
            @Override
            public Object run() {
                RequestContext context = RequestContext.getCurrentContext();
                HttpServletResponse servletResponse = context.getResponse();
                servletResponse.addHeader("X-Sample", UUID.randomUUID().toString());
                return null;
            }
        }
      
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值