美文网首页
SpringCloud(四)zuul网关

SpringCloud(四)zuul网关

作者: 茧铭 | 来源:发表于2019-05-19 11:06 被阅读0次

    在微服务项目搭建之后,我们不可能让客户知道所有的微服务,让客户进行针对性地使用某一个或者几个服务。我们应当有一个统一的请求入口,这个入口替服务器拦截所有的请求,并将请求分发到对应的服务中去调用,这就是服务网关。那么作为项目的唯一入口,服务网关应当有一些必要的特性:

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

    平时我们使用到的服务网关最多的就是Nginx + lua的形式,Nginx凭借性能的优势占据了网关服务的很大一部分市场。在SpringCloud全家桶中Netflix给我们提供了他自己的解决方案智能路由Zuul,相对而言Zuul提供了更多有意思的功能如身份验证、服务迁移、分级卸载等。至于性能,它的第一个版本的性能应该是差于Nginx的,不过也在后续的版本中对它的性能进行了升级了。

    Zuul原理

    zuul的核心是一系列的过滤器,这些过滤器分位四个类型
    "pre":位置在路由请求到服务调用之前,一般可用于数据校验和限流等
    "route":这类过滤器将请求路由到微服务,用于构建发送给微服务的请求,并发起微服务请求。
    "post":这类过滤器在路由到微服务之后请求,可以对返回的数据进行处理或包装
    "error":其他三类过滤器执行中如果发生错误,会执行该类过滤器。


    网络图片

    Zuul大部分功能都是通过过滤器来实现的,这些过滤器类型对应于请求的典型生命周期。

    Zuul使用

    新建一个名为zuul的modular,引入依赖如下

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
    </dependency>
    <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>
    

    启动类上增加注解@EnableZuulProxy

    @SpringBootApplication
    @EnableZuulProxy
    public class ZuulApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(ZuulApplication.class, args);
        }
    
    }
    

    配置文件bootstrap.yml

    eureka:
      client:
        service-url:
          defaultZone: http://root:root@localhost:1001/eureka/
    
    spring:
      application:
        name: zuul
      cloud:
        config:
          discovery:
            enabled: true
            service-id: config
          label: master
          profile: dev
    

    github上面的 /zuul-dev.yml

    server:
      port: 2992
    zuul:
      routes:
        xxxxx:
          path: /api-product/**
          serviceId: product
    

    上面这段配置很明显是对product服务做了什么。我这里依次将eureka、config、zuul、product四个服务启动起来,通过以下三种方式去获取调用product的一个接口,结果如下:

    localhost:3003/product/findByProductStatus?productStatus=1
    localhost:2992/api-product/product/findByProductStatus?productStatus=1
    localhost:2992/product/product/findByProductStatus?productStatus=1


    其实很明显能看出来,zuul的端口为2992,后面连个请求已经可以通过zuul路由到了product服务上。其中第三个请求中有两个product,第一个是serviceId服务ID,后面的是完整的请求路径。第二个请求正如zuul-dev.yml上所示,将product服务映射到了一个自定义的路径“/api-product/**”下,因此也能获取到同意的服务。在配置中有个"xxxxx"的内容,这个内容名字可以自定义,也就是将该路由规则命名而已,没有实际意义。

    这样的配置还有一种更加简单的配置方式

    zuul:
      routes:
        product: /api-product/**
        # product就不是随便命名的了,而是product的服务serviceId
    
    Cookie相关

    我们平常使用cookie是时候,是可以直接从HttpServletRequest中获取到信息的,但是每当请求经过了一层zuul的转发之后,cookie就不能直接从request中获取到了。我们需要加入一下配置

    zuul:
      routes:
        xxxxx:
          path: /api-product/**
          serviceId: product
          sensitiveHeaders: 
    

    即在对应的服务路由配置下,加载一个sensitiveHeaders的空值,在Zuul的配置信息类ZuulProperties中,sensitiveHeaders参数有些默认值,它的作用是默认屏蔽掉这些内容的传递,其中就包括cookie和Authorization,因此我们想要使用Cookie的话就可以把其中的Cookie去掉,我这里直接设置为空了。


    此外,在里面还有一些参数我们可能也会用到,比如说有三个ignored开头的参数,和.gitignore类似,它们的作用是在不同维度划分的情况下,将某一些接口不适用zuul路由,配置了之后再通过zuul访问是找不到的(通常这些接口都是服务之间调用的,而不能被外部直接调用)

    举例,加入配置后重启项目:

    zuul:
      ignored-patterns:
        - /api-product/product/findByProductStatus
    ##  - /product/product/findByProductStatus
    

    再次访问,结果如下
    localhost:2992/api-product/product/findByProductStatus?productStatus=1
    localhost:2992/product/product/findByProductStatus?productStatus=1


    因此呢,ignored-patterns就可以达成我们屏蔽某些接口的效果,这个里面也可以使用 ** 通配符等。

    bus-refresh配置

    配置config通过bus-refresh刷新:之前的order和product的配置方式我们知道了要在对应的Controller上添加一个@RefreshScope,但是Zuul的项目看起来没有什么Controller,我们可以添加到这里来

    新建一个这样的类,在原有的ZuulProperties类上加入@RefreshScope即可

    @Component
    public class ZuulConfig {
    
        @ConfigurationProperties("zuul")
        @RefreshScope
        public ZuulProperties zuulProperties(){
            return new ZuulProperties();
        }
    
    }
    

    自定义过滤器

    原理的时候已经提到了,我们可以重写一些过滤器。比如说下面这个,这个例子是我在网上看到别人做的,我拿过来试了一下,自己写一下也能更好地理解它的原理:增加一个类型是pre的过滤器,要求请求中必须带有"token"参数,否则将不会路由过去。

    在这之前,先看看zuul原本有哪些已经存在的过滤器

    Zuul中默认实现的Filter(https://www.jianshu.com/p/8ea59534bedb

    按照上面的排列,接下来我根据token的逻辑,创建一个Filter内容如下:

    其中某些参数如PRE_TYPE、PRE_DECORATION_FILTER_ORDER 都是从 FilterConstants 中取出使用的,这些值对应了上面zuul的默认实现的值

    @Component
    public class TokenCheckFilter extends ZuulFilter{
        
        /**  表示类型是pre  */
        @Override
        public String filterType() {
            return PRE_TYPE;
        }
        
        /**  配许的序号为5-1 = 4  */
        @Override
        public int filterOrder() {
            return PRE_DECORATION_FILTER_ORDER - 1;
        }
    
        /** 为true表示可用,false代表该filter暂不拦截 */
        @Override
        public boolean shouldFilter() {
            return true;
        }
    
        /** 具体逻辑,没有token则不路由请求,且返回状态码401 */
        @Override
        public Object run() throws ZuulException {
            RequestContext ctx = RequestContext.getCurrentContext();
            HttpServletRequest request = ctx.getRequest();
            String token = request.getParameter("token");
            if (StringUtils.isEmpty(token)){
                /**  如果没有token或为空,不路由请求到服务 */
                ctx.setSendZuulResponse(false);
                ctx.setResponseStatusCode(HttpStatus.SC_UNAUTHORIZED);
            }
    
            return null;
        }
    }
    

    测试结果如下:


    最后在说一下禁用指定的Filter的配置,如下所示,通过对指定过滤器的配置,可以单独关闭或者启动一个或者多个过滤器

    zuul:
        TokenCheckFilter:
            pre:
                disable: true
    

    相关文章

      网友评论

          本文标题:SpringCloud(四)zuul网关

          本文链接:https://www.haomeiwen.com/subject/jtveoqtx.html