Zuul网关技术

  • 一、介绍
  • 二、如何使用
  • 2.1 pom依赖
  • 2.2 yml配置
  • 2.3 启动类
  • 2.4 测试
  • 三、Zuul过滤器
  • 3.1 zuul过滤器生命周期
  • bug1 请求头信息丢失
  • bug2 敏感头信息Authorization,Cookie,Set-Cookie默认是不转发
  • 解决办法


一、介绍

Zuul是Netflix开源的微服务网关,他可以和Eureka,Ribbon,Hystrix等组件配合使用。Zuul组件的核心是一系列的过滤器,这些过滤器可以完成以下功能:

  • 身份认证和安全: 识别每一个资源的验证要求,并拒绝那些不符的请求
  • 审查与监控:
  • 动态路由:动态将请求路由到不同后端集群
  • 压力测试:逐渐增加指向集群的流量,以了解性能
  • 负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求
  • 静态响应处理:边缘位置进行响应,避免转发到内部集群

在微服务架构中,通常会有多个服务提供者。设想一个电商系统,可能会有商品、订单、支付、用户等多个类型的服务,而每个类型的服务数量也会随着整个系统体量的增大也会随之增长和变更。作为UI端,在展示页面时可能需要从多个微服务中聚合数据,而且服务的划分位置结构可能会有所改变。网关就可以对外暴露聚合API,屏蔽内部微服务的微小变动,保持整个系统的稳定性。
当然这只是网关众多功能中的一部分,它还可以做负载均衡,统一鉴权,协议转换,监控监测等一系列功能。

lua内存泄露snapshot zuul内存泄漏_网关

二、如何使用

2.1 pom依赖

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

2.2 yml配置

#  后台网关
server:
  port: 9009
spring:
  application:
    name: tensquare-manager
eureka:
  client:
    service-url:
      defaultZone: http://localhost:6868/eureka
zuul:
  routes:
    tensquare-base:
      path: /base/**
      serviceId: tensquare-base
    tensquare-article:
      path: /article/**
      serviceId: tensquare-article
    tensquare-friend:
      path: /friend/**
      serviceId: tensquare-friend
    tensquare-qa:
      path: /qa/**
      serviceId: tensquare-qa
    tensquare-recruit:
      path: /recruit/**
      serviceId: tensquare-recruit
    tensquare-spit:
      path: /spit/**
      serviceId: tensquare-spit
    tensquare-user:
      path: /user/**
      serviceId: tensquare-user

2.3 启动类

@EnableZuulProxy
@EnableEurekaClient
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class ManagerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ManagerApplication.class);
    }
}

2.4 测试

启动Eureka服务器,启动user服务
以一个user服务为例,不使用网关访问时,是: http://localhost:9008/user 即可查询全部数据

有了网关是这样的http://localhost:9011/user/user 第一个user是zuul中网关路由匹配规则.第二个user是user服务访问的前缀

三、Zuul过滤器

使用过滤器对请求进行解析认证,登录前台请求一般没有太多的权限管理,所以放行即可.
后台的请求需要由严格的权限,只要对应的角色权限才可访问.
我们使用zuul对请求进行拦截过滤判断是否放行.
Zuul中提供了过滤器定义,可以用来过滤代理请求,提供额外功能逻辑。如:权限验证,日志记录等。

Zuul提供的过滤器是一个父类。父类是ZuulFilter。通过父类中定义的抽象方法filterType,来决定当前的Filter种类是什么。有前置过滤、路由后过滤、后置过滤、异常过滤。

前置过滤:是请求进入Zuul之后,立刻执行的过滤逻辑。
路由后过滤:是请求进入Zuul之后,并Zuul实现了请求路由后执行的过滤逻辑,路由后过滤,是在远程服务调用之前过滤的逻辑。
后置过滤:远程服务调用结束后执行的过滤逻辑。
异常过滤:是任意一个过滤器发生异常或远程服务调用无结果反馈的时候执行的过滤逻辑。无结果反馈,就是远程服务调用超时。
filterType:方法返回字符串数据,代表当前过滤器的类型。可选值有-pre, route, post, error。

1. pre - 前置过滤器,在请求被路由前执行,通常用于处理身份认证,日志记录等;2. route - 在路由执行后,服务调用前被调用;3. error - 任意一个filter发生异常的时候执行或远程服务调用没有反馈的时候执行(超时),通常用于处理异常;4. post - 在route或error执行后被调用,一般用于收集服务信息,统计服务性能指标等,也可以对response结果做特殊处理。

filterOrder:返回int数据,用于为同filterType的多个过滤器定制执行顺序,返回值越小,执行顺序越优先。
shouldFilter:返回boolean数据,代表当前filter是否生效。
run:具体的过滤执行逻辑。如pre类型的过滤器,可以通过对请求的验证来决定是否将请求路由到服务上;如post类型的过滤器,可以对服务响应结果做加工处理(如为每个响应增加footer数据)。

3.1 zuul过滤器生命周期

lua内存泄露snapshot zuul内存泄漏_过滤器_02

bug1 请求头信息丢失

zuul网关导致请求头信息丢失

bug2 敏感头信息Authorization,Cookie,Set-Cookie默认是不转发

在过滤器中能获取到 Authorization 信息,但是在另一个服务中依旧获取不到,
原因:敏感头信息Authorization,Cookie,Set-Cookie默认是不转发的,所以其他服务中也就获取不到这部分信息

解决办法

package com.lsh.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

/**
 * @author :LiuShihao
 * @date :Created in 2020/11/4 10:45 上午
 * @desc :前台服务可能会在头中存放一些信息,让其全部放行即可.此处假设请求头中带了一个叫做” Authorization”的头信息.
 */
@Slf4j
@Component
public class WebZuulFilter extends ZuulFilter {

    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 0;
    }

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

    @Override
    public Object run() throws ZuulException {
        //得到request上下文
        RequestContext currentContext = RequestContext.getCurrentContext();
        //得到request域
        HttpServletRequest request = currentContext.getRequest();
        //得到头信息
        String header = request.getHeader("Authorization");
        //判断是否有头信息
        if(header!=null && !"".equals(header)){
            log.info("Authorization:"+header);
            //把头信息继续向下传
            currentContext.addZuulRequestHeader("Authorization", header);
        }
        return null;
    }
}

并且在application.yml文件中添加sensitive-headers:

#  后台网关
server:
  port: 9009
spring:
  application:
    name: tensquare-manager
eureka:
  client:
    service-url:
      defaultZone: http://localhost:6868/eureka
  instance:
    prefer-ip-address: true
zuul:
  routes:
    tensquare-base:
      path: /base/**
      serviceId: tensquare-base
    tensquare-article:
      path: /article/**
      serviceId: tensquare-article
    tensquare-friend:
      path: /friend/**
      serviceId: tensquare-friend
    tensquare-qa:
      path: /qa/**
      serviceId: tensquare-qa
    tensquare-recruit:
      path: /recruit/**
      serviceId: tensquare-recruit
    tensquare-spit:
      path: /spit/**
      serviceId: tensquare-spit
    tensquare-user:
      path: /user/**
      serviceId: tensquare-user
  # sensitive-headers: 空    敏感头信息Authorization,Cookie,Set-Cookie 就不会被过滤
  sensitive-headers: