当前位置 博文首页 > 一颗小陨石的博客:SpringCloud(六)——Zuul

    一颗小陨石的博客:SpringCloud(六)——Zuul

    作者:[db:作者] 时间:2021-09-16 13:42

    Zuul是SpringCloud的一个网关组件,提供整个项目的请求过滤和转发等功能,如在前置过滤器中,我们可以完成用户的认证。

    接下来看看如何使用:

    一、引入依赖

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

    二、启动类开启注解

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

    三、配置路由

    spring.application.name=micro-zuul
    server.port=7070
    
    eureka.client.serviceUrl.defaultZone=http://127.0.0.1:8762/eureka/
    # 使用路径方式匹配路由规则。
    # 参数key结构: zuul.routes.customName.path=xxx
    # 用于配置路径匹配规则。
    # 其中customName自定义。通常使用要调用的服务名称,方便后期管理
    # 可使用的通配符有: * ** ?
    # ? 单个字符
    # * 任意多个字符,不包含多级路径
    # ** 任意多个字符,包含多级路径
    zuul.routes.web-service.path=/web/**
    zuul.routes.order.path=/order/**
    
    zuul.routes.order.serviceId=order-service
    
    zuul.routes.web-service.serviceId=web-service
    

    这样配置后,所有http://7070/web/**的请求统一转发到web-service服务,所有http://7070/order/**的请求统一转发到order-service服务。

    order服务仍然用之前的,同样注册到Eureka,并提供一个测试接口:

    @RefreshScope
    @RestController
    @RequestMapping("/user")
    public class UserController {
    
    @GetMapping("/testzuul")
        public String zuul(){
            return "hello zuul";
        }
    }
    

    这样当我们调用http://localhost:7070/order/user/testzuul的时候,就会转发到order服务的这个接口:

    在这里插入图片描述

    这里由于不小心把order/**改成haha/**忘改回来了,所有就用haha测试,以haha开头的路由转发到了上面的order服务。

    本地转发

    zuul.routes.zuul-server.path: /local/**
    zuul.routes.zuul-server.url: forward:/local
    

    当以/local为开头的请求时,统一转发到本地,即当前网关服务。

    我们在网关服务提供该接口:

    @Slf4j
    @RestController
    @RequestMapping("/local")
    public class LocalController {
    
        @RequestMapping("/queryUser")
        public String queryUser() {
            log.info("zuul->queryUser");
            return "zuul->queryUser";
        }
    }
    
    

    统一前缀

    我们可以给所有请求加一个前缀:

    zuul.prefix=/api
    

    这样我们在请求order服务就变成了:

    http://localhost:7070/api/order/user/testzuul

    在这里插入图片描述

    跳转到指定地址

    前面我们是转发到指定的服务,其实也可以直接转发到某个地址:

    zuul.routes.order-service.path=/order/**
    zuul.routes.order-service.url=http://localhost:8086/
    # 这里我们将order开头的请求通过url的方式转发到该服务地址 
    

    效果是一样的,就不截图了。

    add-host-header

    默认情况下,假如某个服务有个/login接口,如果要要重定向到该接口,就会直接重定向到该服务的具体的服务地址,而不是使用网关的地址,因此我们需要添加:

    zuul.add-host-header=true
    

    该配置会让网关在请求转发前为请求设置HOST头信息。

    敏感信息

    zuul中默认会将敏感的头信息进行过滤不转发:

    zuul.sensitive-headers=
    

    对应的默认值:

        private Set<String> sensitiveHeaders = new LinkedHashSet(Arrays.asList("Cookie", "Set-Cookie", "Authorization"));
    

    因此我们可以像上面那样去掉其敏感头信息。

    或者针对某个服务单独配置:

    zuul.routes.order-service.sensitive-headers=
    

    四、过滤器

    zuul的另一个核心功能就是过滤请求。

    zuul提供了4种过滤器:

    pre

    即在请求前调用,因此我们可以在该过滤器中进行身份认证、集群中选择请求的微服务等。

    routing:

    将请求转发给具体的服务,使用apache httpclient或neflix ribbon请求。

    post

    对应pre,是一个后置过滤器,用于在路由到服务后执行。

    我们可以在post中为响应添加http header、收集统计信息等。

    error

    在其他阶段发送错误时执行该过滤器。

    其流程如下:

    在这里插入图片描述

    前置过滤器

    @Slf4j
    @Component
    public class PreFilter extends ZuulFilter {
        /**
         * 过滤器类型
         * @return
         */
        @Override
        public String filterType() {
            return FilterConstants.PRE_TYPE;
        }
    
        /**
         * 调用顺序
         * @return
         */
        @Override
        public int filterOrder() {
            return 0;
        }
    
        /**
         * 是否进行过滤,
         *
         * @return true:过滤 false:不过滤
         */
        @Override
        public boolean shouldFilter() {
            return RequestContext.getCurrentContext().sendZuulResponse();
        }
    
        @Override
        public Object run() throws ZuulException {
            //获取上下文
            RequestContext ctx = RequestContext.getCurrentContext();
            //获取Request
            HttpServletRequest request = ctx.getRequest();
            //获取请求参数accessToken
            String accessToken = request.getParameter("accessToken");
            //使用String工具类
            if (StringUtils.isBlank(accessToken)) {
                log.warn("access failed");
                //设置为false不进行路由
                ctx.setSendZuulResponse(false);
                //设置响应码进行响应
                ctx.setResponseStatusCode(401);
                try {
                    ctx.getResponse().getWriter().write("accessToken is empty");
                } catch (Exception e) {
                }
                return null;
            }
            log.info("access success");
            return null;
        }
    }
    
    

    调用:

    在这里插入图片描述

    再加个打印请求的日志前置拦截器:

    @Slf4j
    @Component
    public class PreLogFilter extends ZuulFilter {
    
    
        @Override
        public String filterType() {
            return FilterConstants.PRE_TYPE;
        }
    
        @Override
        public int filterOrder() {
            return -1;
        }
    
        @Override
        public boolean shouldFilter() {
            return RequestContext.getCurrentContext().sendZuulResponse();
        }
    
        @Override
        public Object run() throws ZuulException {
            RequestContext currentContext = RequestContext.getCurrentContext();
            HttpServletRequest request = currentContext.getRequest();
            log.info("zuul pre filter-->" + request.getRequestURL() + "-->" + request.getMethod());
            return null;
        }
    }
    

    将order设置为-1让其在认证的拦截器前调用,打印请求:

    2020-07-30 22:21:16.041  INFO 16688 --- [nio-7070-exec-1] com.wml.zuul.filter.PreLogFilter    : zuul pre filter-->http://localhost:7070/api/order/user/testzuul-->GET
    2020-07-30 22:21:16.307  WARN 16688 --- [nio-7070-exec-1] com.wml.zuul.filter.PreFilter       : access failed
    
    cs