Spring Cloud之服务网关Zuul

一.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.配置文件
在这里插入图片描述
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

代码扫描,如果使用自动刷新配置必须要有这个。
在这里插入图片描述

过滤器

在这里插入图片描述
限流
限流就是很多请求来的时候,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的优点发挥出来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值