欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring Cloud之服务网关Zuul

程序员文章站 2022-06-13 21:23:40
...

一.Zuul的简单介绍

1.zuul是什么
zuul 是netflix开源的一个API Gateway 服务器, 本质上是一个web servlet应用。Zuul是基于jvm路由和服务的负载均衡器,专为微服务而生的,认证,鉴权,限流,动态路由,监控,弹性,安全,负载均衡,协作单点压测。Zuul相当于外部环境和web网站后端所有请求的前门。
2.zuul的工作原理
zuul的核心是一系列的过滤器,大部分功能都是在过滤器中完成的。
(1) PRE:这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
(2) ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。
(3) POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
(4) ERROR:在其他阶段发生错误时执行该过滤器。
为什么需要zuul?
在微服务中,服务于服务之间的调用是很复杂的并且是调用的服务很多,如果服务与服务之间打交道的话又麻烦,还可能会导致系统问题。所以我们就设置一个网关,如果想调用服务的话就必须要经过这个网关,这个网关在进行请求的分发。前提是网关必须具备这些条件。

  1. 稳定性,高可用
  2. 安全性
  3. 性能和并发
  4. 扩展性

二.zuul的使用

1.添加依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>

2.配置文件
Spring Cloud之服务网关Zuul
3,启动类上添加注解

@EnableZuulProxy

4,访问路径
可以访问eureka上注册的所有服务,只不过要带上要访问的服务名称。

5,自定义路由,因为路由需要进行自定义,所以我们还要实现动态刷新

zuul:
  routes:
  #/myProduct/list -> /product/list自定义路由
#    myProduct:
#      path: /myProduct/**
#      serviceId: product
#简单的写法
    product: /myProduct/**
#排除某些路由
  ignored-patterns:
    - /product/list
    - /myProduct/list

6,动态路由和动态刷新配置
远端git配置,我这里配置了动态刷新
Apigateway.yml

spring:
  cloud:
    bus:
      refresh:
        enabled: true
  rabbitmq:
    host: 10.64.1.127
    port: 5672
    username: guest
    password: guest
zuul:
  routes:
  #忽略全部服务的敏感头(全部服务都可以获取token)
  sensitive-headers: 
  #/myProduct/list -> /product/list自定义路由
#    myProduct:
#      path: /myProduct/**
#      serviceId: product
#简单的写法
    product: /myProduct/**
#排除某些路由
  ignored-patterns:
    - /product/list
# - /myProduct/list

代码扫描,如果使用自动刷新配置必须要有这个。
Spring Cloud之服务网关Zuul

过滤器

Spring Cloud之服务网关Zuul
限流
限流就是很多请求来的时候,zuul可以把他们给挡回去。
限流是zuul的其中一个功能,限流是在请求被转发之前,即使有权限拦截的情况下,依然要把限流放到权限拦截的前边。
在限流的时候会有一个令牌桶算法,以固定的速度去令牌桶放令牌,然后获取的时候在令牌桶获取令牌,如果获取到的话就进行下一步,获取不到的话就进行拦截。
谷歌用RateLimiter封装了这种令牌桶的算法。
直接定义一个过滤器

@Component
public class RateLimitFilter extends ZuulFilter {
	//设置100个令牌
    private static final RateLimiter RATE_LIMITER = RateLimiter.create(100);

    @Override
    public String filterType() {
        return PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return SERVLET_DETECTION_FILTER_ORDER-1;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        if (!RATE_LIMITER.tryAcquire()){
            throw new RateLimitException();
        }
        return null;
    }
}

下面是FilterConstants的部分源码,里面定义了请求类型和请求的数字,数字越小,优先级越高。

public static final int DEBUG_FILTER_ORDER = 1;
    public static final int FORM_BODY_WRAPPER_FILTER_ORDER = -1;
    public static final int PRE_DECORATION_FILTER_ORDER = 5;
    public static final int RIBBON_ROUTING_FILTER_ORDER = 10;
    public static final int SEND_ERROR_FILTER_ORDER = 0;
    public static final int SEND_FORWARD_FILTER_ORDER = 500;
    public static final int SEND_RESPONSE_FILTER_ORDER = 1000;
    public static final int SIMPLE_HOST_ROUTING_FILTER_ORDER = 100;
    public static final int SERVLET_30_WRAPPER_FILTER_ORDER = -2;
    public static final int SERVLET_DETECTION_FILTER_ORDER = -3;
    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";

添加报头

@Component
public class addResponseHeaderFilter 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() throws ZuulException {
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletResponse response = requestContext.getResponse();
        response.setHeader("X-Foo", UUID.randomUUID().toString());
        return null;
    }
}

验证token

@Component
public class TokenFilter extends ZuulFilter {
    //验证过滤器,就是设置权限的过滤器
    @Override
    public String filterType() {
        return PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return PRE_DECORATION_FILTER_ORDER - 1;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();

        //这个只是模拟加上token
        String token = request.getParameter("token");
        if (StringUtils.isEmpty(token)){
            requestContext.setSendZuulResponse(false);
            requestContext.setResponseStatusCode(HttpStatus.SC_UNAUTHORIZED);
        }
        return null;
    }
}

权限拦截验证

@Component
public class AuthBuyerFilter extends ZuulFilter {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    //验证过滤器,就是设置权限的过滤器
    @Override
    public String filterType() {
        return PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return PRE_DECORATION_FILTER_ORDER - 1;
    }

    @Override
    public boolean shouldFilter() {
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();
        if ("/order/order/create".equals(request.getRequestURI())){
            return true;
        }
        return false;
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();
        /**
         * order/create创建订单只能买家访问(cookie中有openid)
         */

        Cookie cookie = CookieUtil.get(request,"openid");
        if (cookie == null||StringUtils.isEmpty(cookie.getValue())){
            requestContext.setSendZuulResponse(false);
            requestContext.setResponseStatusCode(HttpStatus.SC_UNAUTHORIZED);
        }
        return null;
    }
}

跨域
一般跨域有两种解决方式,一种是咋子类或者方法上添加@CrossOrigin注解,这个是基于方法的,放在哪个方法上,哪个方法生效。一种是增加CorsFilter过滤器,看下面代码。

@Component
public class CorsConfig {

    @Bean
    public CorsFilter corsFilter(){
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();

        final CorsConfiguration configuration = new CorsConfiguration();

        configuration.setAllowCredentials(true);
        configuration.setAllowedOrigins(Arrays.asList("*"));    //http://www.a.com
        configuration.setAllowedHeaders(Arrays.asList("*"));
        configuration.setAllowedMethods(Arrays.asList("*"));
        //在这个时间段里,相同的跨域请求就不再进行检查
        configuration.setMaxAge(300l);
        
        source.registerCorsConfiguration("/**",configuration);
        return new CorsFilter(source);
    }
}

注意
zuul必须要保证高可用,zuul的高可用就是把多个zuul节点注册到Eurekaserver上面去。
还有一种方法就是Nginx和Zuul混合使用的方式,Nginx做负载均衡,把请求转发到多个zuul上,这样的话就把Nginx和Zuul的优点发挥出来。