Zuul路由网关

08Zuul路由网关

一、Zuul路由网关概述

源码地址:https://github.com/Netflix/zuul

Zuul包含了请求的路由和过滤两大主要的功能:

  • 路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一的路径入口基础。通俗的说就“保安”。
  • 过滤器功能则负责对请求的处理过程进行干预,是实现请求校验、服务聚合等功能的基础。Zuul与Eureka进行整合,是将Zuul自身注册为Eureka服务治理下的应用,同时Eureka中获取其他的微服务的信息,也即以后微服务的访问都是通过Zuul跳转后获取。

注意

  1. Zuul服务最终还是会注册到Eureka中。
  2. Zuul还包含了hystrix,actuator,ribbon等功能。

在这里插入图片描述

二、路由基本配置

1. 新建路由网关模块

模块名称:springcloud-zuul-gateway

2. pom.xml

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

3. yml文件

server:
  port: 9527

spring:
  application:
    name: springcloud-zuul-gateway

eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka
  instance:
    instance-id: springcloud-zuul-gateway
    prefer-ip-address: true

# 微服务信息的描述
info:
  # 工程名称
  app.name: newcapec-springcloud
  # 公司名称
  company.name: www.newcapec.com.cn
  project.artifactId: springcloud-zuul-gateway
  project.version: v1.0.1

4. hosts修改

127.0.0.1  zuul9527.com

5. 主启动类

添加新注解@EnableZuulProxy,开启Zuul相关代理

@SpringBootApplication
//开启路由代理
@EnableZuulProxy
public class ZuulGatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(ZuulGatewayApplication.class, args);
    }
}

6. 测试

三、路由访问映射规则

1. 现有问题

通过路由访问的地址http://myzuul.com:9527/springcloud-dept/dept/list,在这里把真实的微服务名称暴露出来,现在需要用一个虚拟的路径来替代真实的微服务名称。

2. 映射路由

映射路由通常采用两种方式:

2.1 url方式

直接使用path和url地址路由到具体服务。在application.yml文件中添加以下配置:

# 路由访问映射
zuul:
  routes:
    user-defined-a:
      path: /mydept-a/**
      url: http://localhost:8001
    user-defined-b:
      path: /mydept-b/**
      url: http://localhost:8002

user-defined-a和user-defined-b是自定义key,/mydept-a/**的请求会被路由到http://localhost:8001服务,/mydept-b/**的请求会被路由到http://localhost:8002服务

访问8001路径:http://zuul9527.com:9527/mydept-a/dept/list
访问8002路径:http://zuul9527.com:9527/mydept-b/dept/list

注意:此种方式不会触发ribbon负载均衡和hystrix熔断功能

2.2 服务id方式

直接使用path和serviceId服务路由到具体服务,具有ribbon负载均衡和hystrix熔断功能。在application.yml文件中添加以下配置:

# 路由访问映射
zuul:
  routes:
    user-defined:
      path: /mydept/**
      serviceId: springcloud-dept

user-defined是自定义key,/mydept/**的请求会被路由到服务名称是springcloud-dept的微服务

访问路径:http://zuul9527.com:9527/mydept/dept/list

3. 原真实服务名忽略

此时问题:原路径访问依然可以访问

在application.yml文件中添加以下配置:

# 路由访问映射
zuul:
  # 忽略微服务,禁止通过微服务名称访问
  ignored-services: springcloud-dept
  # 批量忽略
  # ignored-services: "*"
  routes:
    springcloud-dept:
      path: /mydept/**
      serviceId: springcloud-dept

单个具体,多个可以用"*"

4. 设置统一公共前缀

# 路由访问映射
zuul:
  # 设置统一公共前缀
  prefix: /api
  # 忽略微服务,禁止通过微服务名称访问
  ignored-services: springcloud-dept
  # 批量忽略
  # ignored-services: "*"
  routes:
    springcloud-dept:
      path: /mydept/**
      serviceId: springcloud-dept

访问路径:http://zuul9527.com:9527/api/mydept/dept/list

5. 头部过滤

在使用Zuul网关的时候可能会遇到Cookie丢失的情况,因为默认情况下Zuul会过滤掉HTTP请求头、响应头中的一些敏感信息,不会向后传播,默认值为:CookieSet-CookieAuthorization

测试Cookie和HttpSession是否可以使用:

@RestController
public class CookieController {

    @GetMapping("/set/cookie")
    public String setCookie(HttpServletResponse response){

        Cookie mycookie = new Cookie("mycookie", "zhangsan");
        mycookie.setMaxAge(60*60*24);
        mycookie.setPath("/");
        response.addCookie(mycookie);

        response.setHeader("testheader","lisi");

        return "cookie设置成功";
    }

    @GetMapping("/get/cookie")
    public String getCookie(HttpServletRequest request){

        String str = "";
        Cookie[] cookies = request.getCookies();
        if(cookies!=null){
            for (Cookie cookie : cookies) {
                if(cookie.getName().equals("mycookie")){
                    str = "name:" + cookie.getName() + "--value:" + cookie.getValue();
                }
            }
            return "cookie获取成功"+str;
        }
        return "cookie获取失败";

    }

    @GetMapping("/set/session")
    public String setSession(HttpSession session){

        session.setAttribute("mykey", "myval");

        return "session设置成功";
    }

    @GetMapping("/get/session")
    public String getSession(HttpSession session){

        Object str = session.getAttribute("mykey");

        if(str != null){
            return "session获取成功" + str;
        }
        return "session获取失败";
    }
}

敏感信息通过下面的配置设定,设置全局:

zuul:
  sensitive-headers: Cookie,Set-Cookie,Authorization

设置某一个微服务:

zuul:
  routes:
    user-defined-a:
      path: /mydept-a/**
      url: http://localhost:8001
      sensitiveHeaders: Cookie,Set-Cookie,Authorization

关闭头部过滤,sensitiveHeaders设置为空

6. 开启路由监控

打开actuator的监控端口,默认只打开了health、info的监控监控点*代表全部打开,此举是用于查看/routes接口,返回此zuul代理的服务,以及路由规则。

# 打开所有监控
management:
  endpoints:
    web:
      exposure:
        include: '*'

zuul有对外暴露的actuator接口:

  • /routes:获取zuul代理的服务列表
  • /routes/details:zuul代理的服务详情

访问路径:http://zuul9527.com:9527/actuator/routes

{
  "/api/mydept-a/**": "http://localhost:8001",
  "/api/mydept-b/**": "http://localhost:8002",
  "/api/mydept/**": "springcloud-dept"
}

访问路径:http://zuul9527.com:9527/actuator/routes/details

{
  "/api/mydept-a/**": {
    "id": "user-defined-a",
    "fullPath": "/api/mydept-a/**",
    "location": "http://localhost:8001",
    "path": "/**",
    "prefix": "/api/mydept-a",
    "retryable": false,
    "customSensitiveHeaders": false,
    "prefixStripped": true
  },
  "/api/mydept-b/**": {
    "id": "user-defined-b",
    "fullPath": "/api/mydept-b/**",
    "location": "http://localhost:8002",
    "path": "/**",
    "prefix": "/api/mydept-b",
    "retryable": false,
    "customSensitiveHeaders": false,
    "prefixStripped": true
  },
  "/api/mydept/**": {
    "id": "user-defined",
    "fullPath": "/api/mydept/**",
    "location": "springcloud-dept",
    "path": "/**",
    "prefix": "/api/mydept",
    "retryable": false,
    "customSensitiveHeaders": false,
    "prefixStripped": true
  }
}

四、Zuul的核心

1. 过滤器

Filter(过滤器)是Zuul的核心,用来实现对外服务的控制。Filter的生命周期有4个,分别是“PRE”、“ROUTING”、“POST”、“ERROR”,整个生命周期可以用下图来表示。

在这里插入图片描述

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

  • PRE: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
  • ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。
  • POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
  • ERROR:在其他阶段发生错误时执行该过滤器。 除了默认的过滤器类型,Zuul还允许我们创建自定义的过滤器类型。例如,我们可以定制一种STATIC类型的过滤器,直接在Zuul中生成响应,而不将请求转发到后端的微服务。

2. 默认实现的Filter

类型顺序过滤器功能
pre-3ServletDetectionFilter标记处理Servlet的类型
pre-2Servlet30WrapperFilter包装HttpServletRequest请求
pre-1FormBodyWrapperFilter包装请求体
pre1DebugFilter标记调试标志
pre5PreDecorationFilter处理请求上下文供后续使用
route10RibbonRoutingFilterserviceId请求转发
route100SimpleHostRoutingFilterurl请求转发
route500SendForwardFilterforward请求转发
post0SendErrorFilter处理有错误的请求响应
post1000SendResponseFilter处理正常的请求响应

禁用指定的Filter,可以在application.yml中配置需要禁用的filter。格式:

zuul:
  FormBodyWrapperFilter:
    pre:
      disable: true

3. 自定义Filter

实现自定义Filter,需要继承ZuulFilter的类,并覆盖其中的4个方法。

public class MyFilter extends ZuulFilter {

    /**
     * 定义filter的类型,有pre、route、post、error四种
     */
    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * 定义filter的顺序,数字越小表示顺序越高,越先执行
     */
    @Override
    public int filterOrder() {
        return 10;
    }

    /**
     * 表示是否需要执行该filter,true表示执行,false表示不执行
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * filter需要执行的具体操作
     */
    @Override
    public Object run() {
        return null;
    }
}

方法解析

  • filterType():该方法需要返回一个字符串表示过滤器的类型,而这个类型就是在http请求过程中定义的各个阶段。在zuul中默认定义了4个不同的生命周期过程类型,具体如下:
    • pre:在请求被路由之前调用,FilterConstants.PRE_TYPE
    • routing:路由请求时被调用,FilterConstants.ROUTE_TYPE
    • post:在routing和error过滤器之后被调用,FilterConstants.POST_TYPE
    • error:处理请求时发生错误时被调用,FilterConstants.ERROR_TYPE
  • filterOrder():通过int值来定义过滤器的执行顺序,数值越小优先级越高。
  • shouldFilter():返回一个boolean值来判断该过滤器是否要执行。我们可以通过此方法来指定过滤器的有效范围。
  • run():过滤器的具体逻辑。在该方法中,我们可以实现自定义的过滤逻辑,来确定是否要拦截当前的请求,不对其进行后续的路由,或是在请求路由返回结果之后,对处理结果做一些加工等。

配置过滤器生效,在启动类或配置类中添加以下代码:

@Bean
public MyFilter myFilter() {
    return new MyFilter();
}

4. 请求身份认证案例

因为服务网关应对的是外部的所有请求,为了避免产生安全隐患,需要对请求做一定的限制。
比如请求中含有Token便让请求继续往下走,如果请求不带Token就直接返回并给出提示。

public class TokenFilter extends ZuulFilter {

  @Override
  public String filterType() {
    return "pre"; // 可以在请求被路由之前调用
  }

  @Override
  public int filterOrder() {
    return 0; // filter执行顺序,通过数字指定 ,优先级为0,数字越大,优先级越低
  }

  @Override
  public boolean shouldFilter() {
    return true;// 是否执行该过滤器,此处为true,说明需要过滤
  }

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

    //获取请求中的参数
    String token = request.getParameter("token-key");

    if (StringUtils.isEmpty(token)) {
      //不对其进行路由
      ctx.setSendZuulResponse(false);
      ctx.setResponseStatusCode(405);
      ctx.setResponseBody("token is empty");
      return null;
    } else {
      //对请求进行路由
      ctx.setSendZuulResponse(true);
      return null;
    }
  }
}

ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();

//获取请求中的参数
String token = request.getParameter("token-key");

if (StringUtils.isEmpty(token)) {
  //不对其进行路由
  ctx.setSendZuulResponse(false);
  ctx.setResponseStatusCode(405);
  ctx.setResponseBody("token is empty");
  return null;
} else {
  //对请求进行路由
  ctx.setSendZuulResponse(true);
  return null;
}

}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JTZ001

你的鼓励是我创作的最大动力?

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值