当前位置: 首页 > news >正文

SpringCloud搭建微服务之Zuul网关

1. 概述

Zuul是一种提供动态路由、监视、弹性、安全性等功能的边缘服务,是一个基于JVM路由和服务端的负载均衡器,在Spring Cloud框架中,Zuul的角色是网关,负责接收所有REST请求,然后进行内部转发,是微服务提供者集群的流量入口。

1.1. 主要功能

路由:将不同REST请求转发至不同的微服务提供者,其作用类似于Nginx的反向代理。同时,也起到了统一端口的作用,将很多微服务提供者的不同端口统一到了Zuul的服务端口
认证:网关直接暴露在公网上时,终端要调用某个服务,通常会把登录后的token(令牌)传过来,网关层对token进行有效性验证。如果token无效(或没有token),就不允许访问REST服务。可以结合Spring Security中的认证机制完成Zuul网关的安全认证
限流:高并发场景下瞬时流量不可预估,为了保证服务对外的稳定性,限流成为每个应用必备的一道安全防火墙。如果没有这道安全防火墙,那么请求的流量超过服务的负载能力时很容易造成整个服务的瘫痪
负载均衡:在多个微服务提供者之间按照多种策略实现负载均衡

2. 搭建Zuul网关服务

Zuul作为网关层微服务,跟其他服务提供者一样都注册在Eureka Server上,可以相互发现。Zuul能感知到哪些服务Provider实例在线,同时通过配置路由规则可以将REST请求自动转发到指定的后端微服务提供者

2.1. 引入核心依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

2.2. 编写主启动类

@EnableZuulProxy
@SpringBootApplication
public class ZuulApplication {

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

在启动类中添加注解@EnableZuulProxy,声明这是一个网关服务提供者

2.3. 编写application.yml基础配置

server:
  port: 8810
spring:
  application:
    name: cloud-zuul
eureka:
  client:
    register-with-eureka: false
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:8761/eureka
  instance:
    prefer-ip-address: true
    ip-address: ${spring.cloud.client.ip-address}
    instance-id: ${spring.cloud.client.ip-address}:${server.port}
zuul:
  routes:
    cloud-provider:
      path: /cloud-provider/**
      serviceId: cloud-provider

2.4. 验证

依次启动服务Eureka Server、Provider和Zuul,在浏览器地址输入http://localhost:8770/provider/getProviderInfo/world
直接访问服务提供者
再次在浏览器地址栏输入http://localhost:8810/cloud-provider/provider/getProviderInfo/world
直接访问zuul网关

3. 路由规则配置

路由规则通常有两种方式,其一是路由到直接URL,其二是路由到微服务提供者
两种方式的区别如下:

  • 第一种方式使用url属性来指定直接的上游URL的前缀,第二种方式使用serviceId属性来指定上游服务提供者的名称
  • 第二种方式需要结合Eureka Client客户端来实现动态的路由转发功能,需要配置Eureka相关配置信息

3.1. 过滤敏感请求头部

防止请求头泄露的方式之一是,在Zuul的路由配置中指定要忽略的请求头列表,并且多个敏感头部之间可以用逗号隔开,默认情况,Zuul转发请求会把header清空,如果在微服务集群内部转发请求,上游Provider就会收不到任何头部,如果需要传递原始的header信息到最终的上游,就需要添加如下敏感头部设置

zuul:
  routes:
    cloud-provider:
      sensitiveHeaders: 

如何需要屏蔽头信息,需要如下配置

zuul:
  routes:
    cloud-provider:
      sensitiveHeaders: Cookie,Set-Cookie,token,backend,Authorization

3.2. 路径前缀处理

默认情况下Zuul会去掉路由的路径前缀,如果上游微服务提供者没有配置路径前缀,Zuul这种默认处理和转发就不会有问题,如果上游微服务提供者配置了统一的路径前缀,前缀去掉后,上游服务提供者就会报404错误,找不到URL对应的资源服务。可以设置配置项stripPrefix的值为false

zuul:
  routes:
    cloud-provider:
      path: /cloud-provider/**
      serviceId: cloud-provider
      stripPrefix: true #是否取消请求前缀

如果需要对访问网关的所有请求都加上前缀,可以设置配置prefix,具体配置如下:

zuul:
  prefix: /native #所有请求添加前缀
  routes:
    cloud-provider:
      path: /cloud-provider/**
      serviceId: cloud-provider
      stripPrefix: true #是否取消请求前缀
      sensitiveHeaders: Cookie,Set-Cookie,token,backend,Authorization

4. 查看路由信息

4.1. 引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

4.2. 开启路由监控端口

management:
  endpoints:
    web:
      exposure:
        include: 'routes' #开启查看路由端口

4.3. 验证

重新启动服务,在浏览器地址栏输入http://localhost:8810/actuator/routes
路由监控

5. 过滤器

通过定义过滤器来实现请求的拦截和过滤

5.1. 过滤器类型

pre类型过滤器
请求路由之前调用,用于实现身份验证、记录调试信息
route类型过滤器
发送请求到上游服务,例如使用Apache HttpClient或Netflix Ribbon请求上游服务
post类型过滤器
上游服务返回之后调用,为响应添加HTTP响应头、收集统计信息和指标、将响应回复给客户端
error类型过滤器
在其他阶段发生错误时执行

5.2. 请求处理流程

  1. 当外部请求到达Zuul网关时,首先会进入pre处理阶段,在这个阶段请求将被pre类型的过滤器处理,以完成再请求路由的前置过滤处理,比如请求的校验等。在完成pre类型的过滤处理之后,请求进入第二个阶段:route路由请求转发阶段
  2. 在route路由请求转发阶段,请求将被route类型的过滤器处理,route类型的过滤器将外部请求转发到上游的服务。当服务实例的结果返回之后,route阶段完成,请求进入第三个阶段:post处理阶段
  3. 在post处理阶段,请求将被post类型的过滤器处理,post类型的过滤器在处理的时候不仅可以获取请求信息,还能获取服务实例的返回信息,所以post阶段可以对处理结果进行一些加工或转换等
  4. 还有一个特殊的阶段error,在该阶段请求将被error类型的过滤器处理,在上述3个阶段发生异常时才会触发,但是error过滤器也能将最终结果返回给请求客户端
    Zuul请求处理流程

5.3. 实例

Zuul提供一个过滤器ZuulFilter抽象基类,可以作为自定义过滤器的父类,需要实现的方法主要有4个
filterType方法
返回自定义过滤器类型,以常量的形式定义在FilterConstants类中
filterOrder方法
返回过滤器顺序,值越小优先级越高
shouldFilter方法
返回过滤器是否生效,返回true表示生效,返回false表示不生效
run方法
过滤器业务逻辑处理,可以进行当前的请求拦截和参数定制,后续路由定制,返回结果定制
例如可以使用前置过滤器打印日志和黑名单处理过滤,具体代码如下

@Component
public class BlackListFilter extends ZuulFilter {

    private static final Logger logger = LoggerFactory.getLogger(BlackListFilter.class);
    static List<String> blackList = Arrays.asList("");

    /**
     * 过滤器类型pre:过滤之前;routing:路由之时;post:路由之后;error:发送错误调用
     * @return
     */
    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * 过滤执行次序
     * @return
     */
    @Override
    public int filterOrder() {
        return 0;
    }

    /**
     * 是否执行过滤
     * @return
     */
    @Override
    public boolean shouldFilter() {
        RequestContext context = RequestContext.getCurrentContext();
        if (!context.sendZuulResponse()) {
            return false;
        }
        //返回true,表示需要执行run方法
        HttpServletRequest request = context.getRequest();
        if (request.getRequestURI().startsWith("/native")) {
            return true;
        }
        return false;
    }

    /**
     * 过滤器具体执行方法
     * @return
     * @throws ZuulException
     */
    @Override
    public Object run() throws ZuulException {
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();
        String host = request.getRemoteHost();
        String method = request.getMethod();
        String uri = request.getRequestURI();
        logger.info("=====>Remote host:{},method:{},uri:{}", host, method, uri);
        String username = request.getParameter("username");
        if (null != username && blackList.contains(username)) {
            logger.info(username + " is forbidden: " + request.getRequestURL().toString());
            context.setSendZuulResponse(false);
            try {
                context.getResponse().setContentType("text/html;charset=utf-8");
                context.getResponse().getWriter().write("对不起,您已进入黑名单!");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
        return null;
    }
}

相关文章:

  • Python使用Redis计算经纬度距离
  • 可重入函数
  • ib课程北京国际学校哪里有?
  • 宠物保存服务市场现状及未来发展趋势分析
  • Three.js使用rotation旋转模型
  • 超越所有人的成就,牛顿的光芒也无法掩盖的天才数学巨人
  • C++11之智能指针
  • C++ | 高维数组、指针、返回指向数组的指针的函数
  • linux redis hash哈希 增删改查
  • OS | 【二 PV操作】强化阶段 —— 应用题
  • 超分辨率重建DRCN
  • C++ vector容器
  • 进程(fork()详解)
  • vue基础语法(上)
  • 李沐d2l(十一)--锚框
  • 《深入 React 技术栈》
  • Fastjson的基本使用方法大全
  • Git 使用集
  • go append函数以及写入
  • iOS小技巧之UIImagePickerController实现头像选择
  • Leetcode 27 Remove Element
  • Map集合、散列表、红黑树介绍
  • Netty 4.1 源代码学习:线程模型
  • springboot_database项目介绍
  • SQLServer插入数据
  • text-decoration与color属性
  • 浮现式设计
  • 欢迎参加第二届中国游戏开发者大会
  • 融云开发漫谈:你是否了解Go语言并发编程的第一要义?
  • 协程
  • 学习笔记:对象,原型和继承(1)
  • 用Canvas画一棵二叉树
  • ​​​​​​​​​​​​​​Γ函数
  • (1)Nginx简介和安装教程
  • (1)SpringCloud 整合Python
  • (Arcgis)Python编程批量将HDF5文件转换为TIFF格式并应用地理转换和投影信息
  • (echarts)echarts使用时重新加载数据之前的数据存留在图上的问题
  • (Spark3.2.0)Spark SQL 初探: 使用大数据分析2000万KF数据
  • (八十八)VFL语言初步 - 实现布局
  • (附源码)计算机毕业设计ssm基于B_S的汽车售后服务管理系统
  • (附源码)流浪动物保护平台的设计与实现 毕业设计 161154
  • (十二)python网络爬虫(理论+实战)——实战:使用BeautfulSoup解析baidu热搜新闻数据
  • (原創) 系統分析和系統設計有什麼差別? (OO)
  • (转载)利用webkit抓取动态网页和链接
  • *ST京蓝入股力合节能 着力绿色智慧城市服务
  • .NET CF命令行调试器MDbg入门(三) 进程控制
  • .NET Framework杂记
  • .NET 事件模型教程(二)
  • .NET 应用启用与禁用自动生成绑定重定向 (bindingRedirect),解决不同版本 dll 的依赖问题
  • .Net调用Java编写的WebServices返回值为Null的解决方法(SoapUI工具测试有返回值)
  • .Net接口调试与案例
  • .Net下使用 Geb.Video.FFMPEG 操作视频文件
  • @hook扩展分析
  • [ Linux ] git工具的基本使用(仓库的构建,提交)
  • [ web基础篇 ] Burp Suite 爆破 Basic 认证密码