三. Zuul 网关服务基础解释及基础搭建测试

2023-11-08

一. 基础

  1. 什么是网关: 分布式环境下客户端请求统一通过网关服务进行接收,通过网关服务将请求转发到指定的服务器上有点像nginx,拦截到请求后可以实现权限控制,负载均衡,日志管理,接口调用监控等功能
  2. nginx 适用于服务端接收请求负载,将请求发送到网关服务,通过网关服务对接收到的请求进行一些拦截处理,例如黑名单,白名单等
  3. 现在主流 网关框架 Zuul 和 SpringCloud 推出的 GateWay
    在这里插入图片描述

Zuul 与 Gateway 区别

  1. Gateway
  1. Gateway 底层通过 Netty 实现
  2. Gateway 基于异步非阻塞模型性能较高
  3. 动态路由,能够匹配任何请求属性
  4. 可以对路由指定 Predicate 断言,和 Filter 过滤器
  5. 集成Hystrix断路器功能
  6. 集成 Spring Cloud 服务发现功能
  7. 支持请求限流功能,支持路径重写功能
  1. Zuul
  1. Zuul1 底层通过 Ribbo 实现,基于 Servlet2.5使用阻塞式的,不支持唱两句,
  2. Zuul2 向基于 Netty实现非阻塞,并支持长连接
  3. 在 SpringCloud Finchley 版推荐使用 Zuul

二. Zuul1 网关

  1. Zuul 网关底层通过 Ribbon 和传统的 Servlet IO处理模型与 SpringCloud 进行整合,该Servlet是由servlet container进行管理,当container启动时会通过init()方法初始化Servlet,在接收请求时在线程池中获取一个空闲线程,调用service()执行请求,当container关闭是调用 destory() 销毁servlet
  2. 由于是通过线程绑定的方式,在并发较高的情况下线程数量也要增加切换等,有性能问题

三. Zuul 核心过滤器

  1. Zuul 内部集成了各种过滤器,在接收请求时按照顺序执行过滤器,对请求进行拦截,实现各种判断,看大神的
  2. 过滤器分为:

“pre”: 请求路由之前执行
“route”: 请求路由之后执行
“post”: 在"routing" 和 error 过滤器之后执行

pre过滤器

  1. ServletDetectionFilter:pre类型,执行顺序为 -3,最先被执行的过滤器。该过滤器总是会被执行,主要用来检测当前请求是通过Spring的DispatcherServlet处理运行的,还是通过ZuulServlet来处理运行的。它的检测结果会以布尔类型保存在当前请求上下文的isDispatcherServletRequest参数中,这样后续的过滤器中,我们就可以通过RequestUtils.isDispatcherServletRequest()和RequestUtils.isZuulServletRequest()方法来判断请求处理的源头,以实现后续不同的处理机制.一般情况下被 Zuul 网关服务接收到的请求由 ZuulServlet 处理,主要用来应对大文件上传的情况,另外对于ZuulServlet的访问路径/zuul/*,我们可以通过zuul.servletPath参数进行修改。
  2. Servlet30WrapperFilter:pre类型,执行顺序为 -2,第二个执行的过滤器,将原始的HttpServletRequest包装成Servlet30RequestWrapper对象
  3. FormBodyWrapperFilter:pre类型,执行顺序为-1,第三个执行的过滤器。该过滤器仅对两类请求生效,第一类是Context-Type为application/x-www-form-urlencoded的请求,第二类是Context-Type为multipart/form-data并且是由String的DispatcherServlet处理的请求(用到了ServletDetectionFilter的处理结果)。而该过滤器的主要目的是将符合要求的请求体包装成FormBodyRequestWrapper对象
  4. DebugFilter:pre类型,执行顺序为1,第四个执行的过滤器,该过滤器会根据配置参数zuul.debug.request和请求中的debug参数来决定是否执行过滤器中的操作。而它的具体操作内容是将当前请求上下文中的debugRouting和debugRequest参数设置为true。由于在同一个请求的不同生命周期都可以访问到这二个值,所以我们在后续的各个过滤器中可以利用这二个值来定义一些debug信息,这样当线上环境出现问题的时候,可以通过参数的方式来激活这些debug信息以帮助分析问题,另外,对于请求参数中的debug参数,我们可以通过zuul.debug.parameter来进行自定义
  5. PreDecorationFilter:是pre阶段最后被执行的过滤器执行顺序是5,该过滤器会判断当前请求上下文中是否存在forward.do和serviceId参数,如果都不存在,那么它就会执行具体过滤器的操作(如果有一个存在的话,说明当前请求已经被处理过了,因为这二个信息就是根据当前请求的路由信息加载进来的)。而当它的具体操作内容就是为当前请求做一些预处理,比如说,进行路由规则的匹配,在请求上下文中设置该请求的基本信息以及将路由匹配结果等一些设置信息等,这些信息将是后续过滤器进行处理的重要依据,我们可以通过RequestContext.getCurrentContext()来访问这些信息。另外,我们还可以在该实现中找到对HTTP头请求进行处理的逻辑,其中包含了一些耳熟能详的头域,比如X-Forwarded-Host,X-Forwarded-Port。另外,对于这些头域是通过zuul.addProxyHeaders参数进行控制的,而这个参数默认值是true,所以zuul在请求跳转时默认会为请求增加X-Forwarded-*头域,包括X-Forwarded-Host,X-Forwarded-Port,X-Forwarded-For,X-Forwarded-Prefix,X-Forwarded-Proto。也可以通过设置zuul.addProxyHeaders=false关闭对这些头域的添加动作。

route过滤器

  1. RibbonRoutingFilter: route类型,是route阶段的第一个执行的过滤器,执行顺序为10。该过滤器只对请求上下文中存在serviceId参数的请求进行处理,即只对通过serviceId配置路由规则的请求生效。而该过滤器的执行逻辑就是面向服务路由的核心,它通过使用ribbon和hystrix来向服务实例发起请求,获取服务,并将服务实例的请求结果返回
  2. SimpleHostRoutingFilter:它的执行顺序为100,是route阶段的第二个执行的过滤器。该过滤器只对请求上下文存在routeHost参数的请求进行处理,即只对通过url配置路由规则的请求生效。而该过滤器的执行逻辑就是直接向routeHost参数的物理地址发起请求,从源码中我们可以知道该请求是直接通过httpclient包实现的,而没有使用Hystrix命令进行包装,所以这类请求并没有线程隔离和断路器的保护
  3. SendForwardFilter:它的执行顺序是500,是route阶段第三个执行的过滤器。该过滤器只对请求上下文中存在的forward.do参数进行处理请求,即用来处理路由规则中的forward本地跳转装配。

post过滤器

  1. SendErrorFilter:它的执行顺序是0,是post阶段的第一个执行的过滤器。该过滤器仅在请求上下文中包含error.status_code参数(由之前执行的过滤器设置的错误编码)并且还没有被该过滤器处理过的时候执行。而该过滤器的具体逻辑就是利用上下文中的错误信息来组成一个forward到api网关/error错误端点的请求来产生错误响应
  2. SendResponseFilter:它的执行顺序为1000,是post阶段最后执行的过滤器,该过滤器会检查请求上下文中是否包含请求响应相关的头信息,响应数据流或是响应体,只有在包含它们其中一个的时候执行处理逻辑。而该过滤器的处理逻辑就是利用上下文的响应信息来组织需要发送回客户端的响应内容。

四. 搭建 Zuul 网关服务,通过 Zuul 间接访问微服务接口

1. 步骤

  1. 需求解释: 现有 微服务接口,需要通过 Zuul 网关转发访问
  2. 创建微服务项目提供服务接口,以指定名称注册到服务注册中心
  3. 创建 Zuul 网关服务,并将该服务注册到注册中心
  4. Zuul 网关服务中配置代理转发的访问路径,配置转发到的目标服务地址(使用注册中心配置目标服务名称即可,如果不使用则需要配置指定的目标服务调用地址)

2. 创建目标服务(真实的提供服务接口的目标服务)

  1. pom 中添加 Eureka-client 依赖
<!--eureka client-->
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
  1. yml 配置文件,指定当前服务端口号,服务名称,以指定名称将服务注册到 Eureka 注册中心
server:
  port: 8001 #当前服务端口号

spring:
  application:
    name: cloud-payment-service #当前服务名称
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource #当前数据源操作类型
    driver-class-name: com.mysql.cj.jdbc.Driver #mysql驱动包
    url: jdbc:mysql://localhost:3306/test01?serverTimezone=GMT%2B8 #连接的库
    username: root
    password:
#=================eureka相关配置======================
eureka:
  client:
    register-with-eureka: true #true 表示将当前服务注册到eureka注册中心
    #true 表示是否在注册中心抓取已有的注册信息,集群环境时必须为true,配合ribbon进行负载
    fetchRegistry: true
    service-url:
      #eureka 注册中心访问连接,集群环境多个注册地址
      defaultZone: http://127.0.0.1:7001/eureka,http://127.0.0.1:7002/eureka
  instance:
    instance-id: payment8001 #配置当前服务向eureka注册中心注册时显示的服务器主机名称
    prefer-ip-address: true #配置在开发人员直接访问eureka服务器时显示当前eureka上注册的服务的ip
    lease-renewal-interval-in-seconds: 1 #指定定时向eureka注册中心发送代表当前服务心跳包的时间默认30秒
    lease-expiration-duration-in-seconds: 2 # Eureka 接收到当前服务最后一次发送代表正常心跳包的等待时间,默认90秒超过表示不存活
#=================eureka相关配置end======================

mybatis:
  mapperLocations: classpath:mapper/*.xml #扫描mappper.xml */
  type-aliases-package: com.test.dao #扫描对应mapper.xml 接口
  1. 提供服务接口
@RestController
public class PaymentController {

    @Autowired
    private PaymentServerApi paymentServerApi;

    @GetMapping(value = "/getRun")
    public JsonResult getRun() {
        System.out.println("执行查询所有数据8001");
        return JsonResult.success();
    }
 }
  1. 目标服务启动类
@SpringBootApplication
@EnableEurekaClient //表示当前 PaymentMain8001 服务注册到Eureka中
public class PaymentMain8001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain8001.class,args);
    }
}

3. 创建 Zuul 网关服务

  1. pom 文件中添加 Zuul 依赖
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--Zuul 依赖-->
<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>
  1. yml 文件配置当前网关服务名称,服务端口号,配置服务拦截请求路径,配置转发的真实服务地址
server:
  port: 9527

spring:
  application:
    name: cloud-zuul

zuul:
  host:
    # 连接时间semaphores
    connect-timeout-millis: 3000
    # 每个router最大连接数
    max-per-route-connections: 100
    # 最大连接数
    max-total-connections: 1000
     # socket超时时间
    socket-timeout-millis: 3000
  #忽略路径或指定服务,*表示忽略所有,只通过下面配置的路由进行访问,多个用逗号隔开
  ignored-services: *
  routes:
    api-a:
      #假设请求当前网关服务"http://网关服务ip:网关服务端口号/此处配置拦截的路径/目标服务接口路径"
      path: /myZuul-payment/**   #当前网关服务拦截路径 */
      #会自动转发到目标服务,通过该名称在注册中心获取调用地址列表,本地服务选择指定的地址进行调用
      #实际会转发"http://cloud-payment-service/目标服务接口路径"
      serviceId: cloud-payment-service #目标服务在注册中心注册的名称
      
    #第二个
    #api-b:
      #path: /api-order/**  #*/
      #serviceId: cloud-order-service


#将当前 Gateway 服务注册到 Eureka注册中心
eureka:
  client:
    register-with-eureka: true #true 表示将当前服务注册到eureka注册中心
    #true 表示是否在注册中心抓取已有的注册信息,集群环境时必须为true,配合ribbon进行负载
    fetchRegistry: true
    service-url:
      #eureka 注册中心访问连接,集群环境多个注册地址
      defaultZone: http://127.0.0.1:7001/eureka,http://127.0.0.1:7002/eureka
  instance:
    instance-id: zuul9527 #配置当前服务向eureka注册时显示id为指定的服务器主机名称
    prefer-ip-address: true #配置在开发人员直接访问eureka服务器时显示当前eureka上注册的服务的ip
    lease-renewal-interval-in-seconds: 1 #指定定时向eureka注册中心发送代表当前服务心跳包的时间默认30秒
    lease-expiration-duration-in-seconds: 2 # Eureka 接收到当前服务最后一次发送代表正常心跳包的等待时间,默认90秒超过表示不存活
    hostname: cloud-zuul-service ##配置当前服务向eureka注册时显示端口号
#=================eureka相关配置end======================
  1. Zuul 网关服务启动类添加 @EnableZuulProxy 开启 Zuul 网关服务代理转发
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

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

4. 最终实现功能

  1. 以前通过 “http://目标服务ip:目标服务端口号/目标服务接口路径” 直接访问目标服务
    在这里插入图片描述
  2. 现在 “http://网关服务ip:网关服务端口号/网关服务拦截的路径/目标服务接口路径” 网关服务间接访问目标服务
    在这里插入图片描述
  3. 可以在网关服务中创建过滤器,对请求进行拦截,作出指定的操作,例如统一验证用户是否登录,黑白名单,设置允许跨域等

5. 在 Zuul 网关服务中创建自定义过滤器

  1. 在 Zuul 网关服务中创建自定义过滤器,当访问网关时拦截用户请求,做出指定判断,成功后放行,否则拦截拒绝访问等
  2. 自定义过滤器要继承 ZuulFilter 父类,重写父类中的抽象方法

filterType() : 设置当前过滤器类型
shouldFilter() : 设置当前过滤器是否生效
filterOrder() : 设置当前过滤器执行优先级
run() : 当前过滤器实际拦截或放行的逻辑方法

  1. 将自定义过滤器注入到 Spring 容器中
  2. 代码示例
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class MyZuulFilter extends ZuulFilter {

    //1.设置当前过滤器的过滤类型
    @Override
    public String filterType() {
        //"pre": 请求路由之前执行
        //"route": 请求路由之后执行
        //"post": 在"routing" 和 error 过滤器之后执行
        return "pre";
    }

    //2.根据返回值设置当前过滤器执行的优先级,参数越小优先级越高
    @Override
    public int filterOrder() {
        return 0;
    }

    //3.判断过滤是否生效
    @Override
    public boolean shouldFilter() {
        //可以在该方法中通过 RequestContext 获取请求的上下文
        //通过获取到的数据判断设置当前过滤器是否生效
        HttpServletRequest request = RequestContext.getCurrentContext().getRequest();
        HttpServletResponse response = RequestContext.getCurrentContext().getResponse();
        return true;
    }

    //4.过滤方法,当向 RequestContext 中存储一个 "sendZuulResponse" 变量,值为false
    //时代表当前请求被拦截,解决访问,当没有存储该变量为false时return null 拦截放行
    @Override
    public Object run() throws ZuulException {
        //1.通过 RequestContext 获取上下文
        RequestContext currentContext = RequestContext.getCurrentContext();
        //2.通过上下文获取 Request
        HttpServletRequest request = currentContext.getRequest();
        //3.在 Request 中获取请求数据
        String userToken = request.getParameter("userToken");
        if (null == userToken) {
            //4.注意点,当上下文 RequestContext 调用 setSendZuulResponse() 方法,
            //设置为 false 时, 后续 return null,当前请求则不会继续向下执行,代表拒绝访问
            //查看该方法内部是向 RequestContext 中存储了一个"sendZuulResponse"变量值为false
            currentContext.setSendZuulResponse(false);
            currentContext.setResponseStatusCode(401);
            currentContext.setResponseBody("userToken is null");
            return null;
        }
        //5.RequestContext 中的 SendZuulResponse 不为 false
        //return null,代表当前过滤器放行,继续执行下一个过滤器
        return null;

    }
}
  1. 最终效果,当用户访问 Zuul 网关服务时,会自动执行该自定义过滤器中的 run() 方法,获取请求中是否携带了名为 “userToken” 的参数,如果有则拦截放行,执行下一个过滤器… 如果没有则拒绝访问想要前台"401",状态码与 “userToken is null” 提示信息
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

三. Zuul 网关服务基础解释及基础搭建测试 的相关文章

随机推荐

  • This system is not registered with RHN问题解决

    1 问题 公司有一台服务器 因为一直用的是centos的系统 但是那个分中心没有现成centos的系统 问我红帽5 3的能行吗 我说试一试 然后在配置yum源的时候就遇到了问题 提示如下错误 这是因为这个系统没有在红帽注册 所以不能用yum
  • 谷歌云

    Cloud Ace 是谷歌云全球战略合作伙伴 拥有 300 多名工程师 也是谷歌最高级别合作伙伴 多次获得 Google Cloud 合作伙伴奖 作为谷歌托管服务商 我们提供谷歌云 谷歌地图 谷歌办公套件 谷歌云认证培训服务 开放表格式依赖
  • R语言的微博数据处理

    用R语言处理微博数据 用到TM包 rJava包 slam包 自己还对李舰老师的Rwordseg进行了反编译 将最新的ansj弄了进去 首先来进行下微博的处理 我将每一个用户的微博放在一个文档中 文档名为用户id txt 首先导入需要用的包
  • C# Bitmap 与 Bytes数组,Bitmap与Image 控件的转换

    没事总结一下平时用到的几种图像相互转换方法 供大家参考 1 Bitmap 转byte 数组
  • Pandas ExcelWrite 简单的增改表格

    writer sheets 表格名字 writer pd ExcelWriter error report path engine xlsxwriter df pd DataFrame error info1 columns col df
  • rk3399调试ov2659(camera模块@dvp接口)--移植过程

    版权声明 本文为博主原创文章 转载请注明出处 https blog csdn net huang 165 article details 86130288 参考博客 RGB 与YUY格式简介 https blog csdn net u010
  • 远控博主远控博主

    Wh04m1001 SysmonEoP github com 关于这个博客 idiotc4t s blog
  • python 常用代码块

    1 计时 import time starttime time time print 计时 round time time starttime 0 秒 end r n 2 读文件 with open pi digits txt as f r
  • Unity事件触发

    一 EventSystem物体 当创建一个Canvas时会生成一个EventSystem物体 它包括以下3个组件 1 组件 Event System 被动触发事件系统 负责调度处理事件的输入 射线 发送 变量 First selected
  • 三分钟了解阿里云和腾讯云的DDoS防御策略

    三分钟了解阿里云和腾讯云的DDoS防御策略 DDoS攻击 即分布式拒绝服务 DDoS Distributed Denial of Service 攻击 是一种通过恶意流量导致受害者服务瘫痪的网络攻击行为 中小型网站站长在其父伍奇遭到DDoS
  • 华为发布HarmonyOS 3.0,向“万物互联”再迈一步

    整理 彭慧中 责编 屠敏 出品 CSDN ID CSDNnews HarmonyOS承载无数人操作系统的梦想再次向前迈进了一大步 7月27日晚 HarmonyOS 3 0系统正式发布 HarmonyOS是史上发展最快 也是覆盖升级机型最多的
  • Linux如何在屏幕上显示ASCII/中文字符

    能调API完成的事情非要自己折腾 这会严重影响效率 但这只是玩玩 下一篇文章我会介绍 setfont 命令的玩法 问题 如何在屏幕上显示一个字符 很简单 调用 printf 执行 echo 然而 我们知道 任何显示的操作 最终都是在显示器上
  • C++中fstream读写文件

    C 中fstream读写文件 fstream介绍 1 fstream是C 标准库中面向对象库的一个 用于操作流式文件 2 fstream本质上是一个class 提供file操作的一众方法 3 有核心课程中应用编程里文件操作的基础 fstre
  • Node写博客--添加博客分类功能(修改和删除)

    1 在layout html中加入一个 分类管理 ul class nav navbar nav li a href admin user 用户管理 a li li class dropdown a href class dropdown
  • 【Shell牛客刷题系列】SHELL20 打印只有一个数字的行:awk叫上正则表达式好兄弟来刷题~

    该系列是基于牛客Shell题库 针对具体题目进行查漏补缺 学习相应的命令 刷题链接 牛客题霸 Shell篇 该系列文章都放到专栏下 专栏链接为 专栏 Shell 欢迎关注专栏 本文知识预告 本文首先学习了正则表达式和awk命令的相关的用法
  • Linux (Ubuntu 14)下安装Android studio 时创建桌面快捷方式

    创建桌面快捷方式遇到问题 根据网上搜到的资料 进行了如下尝试 第一次是直接创建桌面快捷方式文件创建 第一次使用 Android studio 3 0 时使用的该方法成功了 但因为后期发现该版本Android studio 导入某项目出问题
  • socks协议解析

    SOCKS5 协议解析 一 定义 SOCKS5 是一个代理协议 旨在为位于 Intranet 防火墙后的用户提供访问 Internet 的代理服务 有些博文说Socks协议位于7层协议的传输层 有些博文说位于会话层 我个人的理解是位于会话层
  • 【JavaScript】数组去重

    数组去重 1 Set console log Array from new Set arr console log new Set arr 2 indexOf function unique var newArr for var i 0 i
  • Pytest 自定义HOOK函数

    除了系统提过的HOOK函数外 也可以通过自定义HOOK的方式实现想要的功能 首先创建一个py文件 里面定义自己的HOOK函数 主要pytest里面的hook函数必须以pytest开头 myhook py def pytest myhook
  • 三. Zuul 网关服务基础解释及基础搭建测试

    目录 一 基础 Zuul 与 Gateway 区别 二 Zuul1 网关 三 Zuul 核心过滤器 pre过滤器 route过滤器 post过滤器 四 搭建 Zuul 网关服务 通过 Zuul 间接访问微服务接口 1 步骤 2 创建目标服务