Spring Cloud 系列之 Netflix Zuul 服务网关(一)

  

1|0什么是 Zuul

  

  Zuul 是从设备和网站到应用程序后端的所有请求的前门。作为边缘服务应用程序,Zuul 旨在实现动态路由,监视,弹性和安全性。Zuul 包含了对请求的路由过滤两个最主要的功能。

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

  • 身份认证与安全:识别每个资源的验证要求,并拒绝那些与要求不符的请求
  • 审查与监控:在边缘位置追踪有意义的数据和统计结果,从而带来精确的生产试图
  • 动态路由:动态地将请求路由到不同的后端集群
  • 压力测试:逐渐增加只想集群的流量,以了解性能
  • 负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求
  • 静态响应处理:在边缘位置直接建立部份响应,从而避免其转发到内部集群\
  • 多区域弹性:跨越AWS Region进行请求路由,旨在实现ELB(Elastic Load Balancing)使用的多样化,以及让系统的边缘更贴近系统的使用者

  

2|0什么是服务网关

  

  API Gateway(APIGW / API 网关),顾名思义,是出现在系统边界上的一个面向 API 的、串行集中式的强管控服务,这里的边界是企业 IT 系统的边界,可以理解为企业级应用防火墙,主要起到隔离外部访问与内部系统的作用。在微服务概念的流行之前,API 网关就已经诞生了,例如银行、证券等领域常见的前置机系统,它也是解决访问认证、报文转换、访问统计等问题的。

  API 网关的流行,源于近几年来移动应用与企业间互联需求的兴起。移动应用、企业互联,使得后台服务支持的对象,从以前单一的 Web 应用,扩展到多种使用场景,且每种使用场景对后台服务的要求都不尽相同。这不仅增加了后台服务的响应量,还增加了后台服务的复杂性。随着微服务架构概念的提出,API 网关成为了微服务架构的一个标配组件

  API 网关是一个服务器,是系统对外的唯一入口。API 网关封装了系统内部架构,为每个客户端提供定制的 API。所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有非业务功能。API 网关并不是微服务场景中必须的组件,如下图,不管有没有 API 网关,后端微服务都可以通过 API 很好地支持客户端的访问。

  但对于服务数量众多、复杂度比较高、规模比较大的业务来说,引入 API 网关也有一系列的好处:

  • 聚合接口使得服务对调用者透明,客户端与后端的耦合度降低
  • 聚合后台服务,节省流量,提高性能,提升用户体验
  • 提供安全、流控、过滤、缓存、计费、监控等 API 管理功能

  

3|0为什么要使用网关

  

  • 单体应用:浏览器发起请求到单体应用所在的机器,应用从数据库查询数据原路返回给浏览器,对于单体应用来说是不需要网关的。
  • 微服务:微服务的应用可能部署在不同机房,不同地区,不同域名下。此时客户端(浏览器/手机/软件工具)想要请求对应的服务,都需要知道机器的具体 IP 或者域名 URL,当微服务实例众多时,这是非常难以记忆的,对于客户端来说也太复杂难以维护。此时就有了网关,客户端相关的请求直接发送到网关,由网关根据请求标识解析判断出具体的微服务地址,再把请求转发到微服务实例。这其中的记忆功能就全部交由网关来操作了。

总结

  

如果让客户端直接与各个微服务交互:

  • 客户端会多次请求不同的微服务,增加了客户端的复杂性
  • 存在跨域请求,在一定场景下处理相对复杂
  • 身份认证问题,每个微服务需要独立身份认证
  • 难以重构,随着项目的迭代,可能需要重新划分微服务
  • 某些微服务可能使用了防火墙/浏览器不友好的协议,直接访问会有一定的困难

  

因此,我们需要网关介于客户端与服务器之间的中间层,所有外部请求率先经过微服务网关,客户端只需要与网关交互,只需要知道网关地址即可。这样便简化了开发且有以下优点:

  • 易于监控,可在微服务网关收集监控数据并将其推送到外部系统进行分析
  • 易于认证,可在微服务网关上进行认证,然后再将请求转发到后端的微服务,从而无需在每个微服务中进行认证
  • 减少了客户端与各个微服务之间的交互次数

  

4|0网关解决了什么问题

  

  网关具有身份认证与安全、审查与监控、动态路由、负载均衡、缓存、请求分片与管理、静态响应处理等功能。当然最主要的职责还是与“外界联系”。

  总结一下,网关应当具备以下功能:

  • 性能:API 高可用,负载均衡,容错机制。
  • 安全:权限身份认证、脱敏,流量清洗,后端签名(保证全链路可信调用),黑名单(非法调用的限制)。
  • 日志:日志记录,一旦涉及分布式,全链路跟踪必不可少。
  • 缓存:数据缓存。
  • 监控:记录请求响应数据,API 耗时分析,性能监控。
  • 限流:流量控制,错峰流控,可以定义多种限流规则。
  • 灰度:线上灰度部署,可以减小风险。
  • 路由:动态路由规则。

  

5|0常用网关解决方案

  

5|1Nginx + Lua

  

  Nginx 是由 IgorSysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的,一个高性能的 HTTP 和反向代理服务器。Ngnix 一方面可以做反向代理,另外一方面做可以做静态资源服务器。

  • Nginx 是 C 语言开发,而 Zuul 是 Java 语言开发
  • Nginx 负载均衡实现,采用服务器实现负载均衡,而 Zuul 负载均衡的实现是采用 Ribbon + Eureka 来实现本地负载均衡
  • Nginx 适合于服务器端负载均衡,Zuul 适合微服务中实现网关
  • Nginx 相比 Zuul 功能会更加强大,因为 Nginx 可以整合一些脚本语言(Nginx + Lua)
  • Nginx 是一个高性能的 HTTP 和反向代理服务器,也是一个 IMAP / POP3 /SMIP 服务器。Zuul 是 Spring Cloud Netflix 中的开源的一个 API Gateway 服务器,本质上是一个 Servlet 应用,提供动态路由,监控,弹性,安全等边缘服务的框架。 Zuul 相当于是从设备和网站到应用程序后端的所有请求的前门。
  • Nginx 适合做门户网关,是作为整个全局的网关,对外的处于最外层的那种;而 Zuul 属于业务网关,主要用来对应不同的客户端提供服务,用于聚合业务。各个微服务独立部署,职责单一,对外提供服务的时候需要有一个东西把业务聚合起来。
  • Zuul 可以实现熔断、重试等功能,这是 Nginx 不具备的。

  

5|2Kong

  

  Kong 是 Mashape 提供的一款 API 管理软件,它本身是基于 Ngnix + Lua 的,但比 Nginx 提供了更简单的配置方式,数据采用了 ApacheCassandra/PostgreSQL 存储,并且提供了一些优秀的插件,比如验证,日志,调用频次限制等。Kong 非常诱人的地方就是提供了大量的插件来扩展应用,通过设置不同的插件可以为服务提供各种增强的功能。

优点:基于 Nginx 所以在性能和稳定性上都没有问题。Kong 作为一款商业软件,在 Nginx 上做了很扩展工作,而且还有很多付费的商业插件。Kong 本身也有付费的企业版,其中包括技术支持、使用培训服务以及 API 分析插件。

缺点:如果你使用 Spring Cloud,Kong 如何结合目前已有的服务治理体系?

  

5|3Traefik

  

  Traefik 是一个开源的 GO 语言开发的为了让部署微服务更加便捷而诞生的现代HTTP反向代理、负载均衡工具。 它支持多种后台 (DockerSwarmKubernetesMarathonMesosConsulEtcdZookeeperBoltDB, Rest API, file…) 来自动化、动态的应用它的配置文件设置。Traefik 拥有一个基于 AngularJS 编写的简单网站界面,支持 Rest API,配置文件热更新,无需重启进程。高可用集群模式等。

相对 Spring Cloud 和 Kubernetes 而言,目前比较适合 Kubernetes。

  

5|4Spring Cloud Netflix Zuul

  

  Zuul 是 Netflix 公司开源的一个 API 网关组件,Spring Cloud 对其进行二次基于 Spring Boot 的注解式封装做到开箱即用。目前来说,结合 Sring Cloud 提供的服务治理体系,可以做到请求转发,根据配置或者默认的路由规则进行路由和 Load Balance,无缝集成 Hystrix。

虽然可以通过自定义 Filter 实现我们想要的功能,但是由于 Zuul 本身的设计是基于单线程的接收请求和转发处理,是阻塞 IO,不支持长连接。目前来看 Zuul 就显得很鸡肋,随着 Zuul 2.x 一直跳票(2019 年 5 月发布了 Zuul 2.0 版本),Spring Cloud 推出自己的 Spring Cloud Gateway。

大意就是:Zuul 已死,Spring Cloud Gateway 永生(手动狗头)。

  

Zuul 1.0

  

  

Zuul 2.0

  

  

5|5Spring Cloud Gateway

  

  Spring Cloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Netflix Zuul,其不仅提供统一的路由方式,并且还基于 Filter 链的方式提供了网关基本的功能。目前最新版 Spring Cloud 中引用的还是 Zuul 1.x 版本,而这个版本是基于过滤器的,是阻塞 IO,不支持长连接。

  Zuul 2.x 版本一直跳票,2019 年 5 月,Netflix 终于开源了支持异步调用模式的 Zuul 2.0 版本,真可谓千呼万唤始出来。但是 Spring Cloud 已经不再集成 Zuul 2.x 了。

  Spring Cloud Gateway 是基于 Spring 生态系统之上构建的 API 网关,包括:Spring 5,Spring Boot 2 和 Project Reactor。Spring Cloud Gateway 旨在提供一种简单而有效的方法来路由到 API,并为它们提供跨领域的关注点,例如:安全性,监视/指标,限流等。由于 Spring 5.0 支持 Netty,Http2,而 Spring Boot 2.0 支持 Spring 5.0,因此 Spring Cloud Gateway 支持 Netty 和 Http2 顺理成章。

  

5|6总结

  

  API 网关在微服务架构中的作用大概是这样的:

  我们目前所学习的网关关注的就是 Aggr API Gateway 这部分,在这里做聚合服务的操作。

  

6|0环境准备

  

  zuul-demo 聚合工程。SpringBoot 2.2.4.RELEASESpring Cloud Hoxton.SR1

  • eureka-server:注册中心
  • eureka-server02:注册中心
  • product-service:商品服务,提供了根据主键查询商品接口 http://localhost:7070/product/{id}
  • order-service:订单服务,提供了根据主键查询订单接口 http://localhost:9090/order/{id} 且订单服务调用商品服务。

  

  

7|0Nginx 实现 API 网关

  

  点击链接观看:Nginx 实现 API 网关视频(获取更多请关注公众号「哈喽沃德先生」)

  

  之前的课程中我们已经详细的讲解过 Nginx 关于反向代理、负载均衡等功能的使用,这里不再赘述。这里主要通过 Nginx 来实现 API 网关方便大家更好的学习和理解 Zuul 的使用。

  

7|1下载

  

  官网:http://nginx.org/en/download.html 下载稳定版。为了方便学习,请下载 Windows 版本。

  

7|2安装

  

  解压文件后直接运行根路径下的 nginx.exe 文件即可。

  Nginx 默认端口为 80,访问:http://localhost:80/ 看到下图说明安装成功。

  

7|3配置路由规则

  

  进入 Nginx 的 conf 目录,打开 nginx.conf 文件,配置路由规则:

http { ... server { listen 80; server_name localhost; ... # 路由到商品服务 location /api-product { proxy_pass http://localhost:7070/; } # 路由到订单服务 location /api-order { proxy_pass http://localhost:9090/; } ... } ... }

  

7|4访问

  

  之前我们如果要访问服务,必须由客户端指定具体服务地址访问,现在统一访问 Nginx,由 Nginx 实现网关功能将请求路由至具体的服务。

  访问:http://localhost/api-product/product/1 结果如下:

  

  访问:http://localhost/api-order/order/1 结果如下:

  

8|0Zuul 实现 API 网关

  

  点击链接观看:Zuul 实现 API 网关视频(获取更多请关注公众号「哈喽沃德先生」)

  

  官网文档: https://cloud.spring.io/spring-cloud-static/spring-cloud-netflix/2.2.1.RELEASE/reference/html/#router-and-filter-zuul

  

8|1搭建网关服务

  

创建项目

  

  创建 zuul-server 项目。

  

添加依赖

  

  添加 spring cloud netflix zuul 依赖。

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>zuul-server</artifactId> <version>1.0-SNAPSHOT</version> <!-- 继承父依赖 --> <parent> <groupId>com.example</groupId> <artifactId>zuul-demo</artifactId> <version>1.0-SNAPSHOT</version> </parent> <!-- 项目依赖 --> <dependencies> <!-- spring cloud netflix zuul 依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> </dependencies> </project>

  

配置文件

  

  application.yml

server: port: 9000 # 端口 spring: application: name: zuul-server # 应用名称

  

启动类

  

  启动类需要开启 @EnableZuulProxy 注解。

package com.example; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; @SpringBootApplication // 开启 Zuul 注解 @EnableZuulProxy public class ZuulServerApplication { public static void main(String[] args) { SpringApplication.run(ZuulServerApplication.class, args); } }

  

8|2配置路由规则

  

URL 地址路由

  

# 路由规则 zuul: routes: product-service: # 路由 id 自定义 path: /product-service/** # 配置请求 url 的映射路径 url: http://localhost:7070/ # 映射路径对应的微服务地址

  

  通配符含义:

通配符含义举例解释
? 匹配任意单个字符 /product-service/? /product-service/a,/product-service/b,...
* 匹配任意数量字符不包括子路径 /product-service/* /product-service/aa,/product-service/bbb,...
** 匹配任意数量字符包括所有下级路径 /product-service/** /product-service/aa,/product-service/aaa/b/ccc

  

  访问:http://localhost:9000/product-service/product/1 结果如下:

  

服务名称路由

  

  微服务一般是由几十、上百个服务组成,对于 URL 地址路由的方式,如果对每个服务实例手动指定一个唯一访问地址,这样做显然是不合理的。

  Zuul 支持与 Eureka 整合开发,根据 serviceId 自动从注册中心获取服务地址并转发请求,这样做的好处不仅可以通过单个端点来访问应用的所有服务,而且在添加或移除服务实例时不用修改 Zuul 的路由配置。

  

添加 Eureka Client 依赖

  

<!-- netflix eureka client 依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>

  

配置注册中心和路由规则

  

# 路由规则 zuul: routes: product-service: # 路由 id 自定义 path: /product-service/** # 配置请求 url 的映射路径 serviceId: product-service # 根据 serviceId 自动从注册中心获取服务地址并转发请求 # 配置 Eureka Server 注册中心 eureka: instance: prefer-ip-address: true # 是否使用 ip 地址注册 instance-id: ${spring.cloud.client.ip-address}:${server.port} # ip:port client: service-url: # 设置服务注册中心地址 defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/

  

启动类

  

package com.example; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; @SpringBootApplication // 开启 Zuul 注解 @EnableZuulProxy // 开启 EurekaClient 注解,目前版本如果配置了 Eureka 注册中心,默认会开启该注解 //@EnableEurekaClient public class ZuulServerApplication { public static void main(String[] args) { SpringApplication.run(ZuulServerApplication.class, args); } }

  

访问

  

  访问:http://localhost:9000/product-service/product/1 结果如下:

  

简化路由配置

  

  Zuul 为了方便大家使用,提供了默认路由配置:路由 id 和 微服务名称 一致,path 默认对应 /微服务名称/**,所以以下配置就没必要再写了。

# 路由规则 zuul: routes: product-service: # 路由 id 自定义 path: /product-service/** # 配置请求 url 的映射路径 serviceId: product-service # 根据 serviceId 自动从注册中心获取服务地址并转发请求

  

访问

  

  此时我们并没有配置任何订单服务的路由规则,访问:http://localhost:9000/order-service/order/1 结果如下:

  

8|3路由排除

  

  我们可以通过路由排除设置不允许被访问的资源。允许被访问的资源可以通过路由规则进行设置。

  

URL 地址排除

  

# 路由规则 zuul: ignored-patterns: /**/order/** # URL 地址排除,排除所有包含 /order/ 的路径 # 不受路由排除影响 routes: product-service: # 路由 id 自定义 path: /product-service/** # 配置请求 url 的映射路径 serviceId: product-service # 根据 serviceId 自动从注册中心获取服务地址并转发请求

  

服务名称排除

  

# 路由规则 zuul: ignored-services: order-service # 服务名称排除,多个服务逗号分隔,'*' 排除所有 # 不受路由排除影响 routes: product-service: # 路由 id 自定义 path: /product-service/** # 配置请求 url 的映射路径 serviceId: product-service # 根据 serviceId 自动从注册中心获取服务地址并转发请求

  

8|4路由前缀

  

zuul: prefix: /api

  

访问

  

  访问:http://localhost:9000/api/product-service/product/1 结果如下:

下一篇我们讲解 Zuul 网关过滤器实现统一鉴权以及网关过滤器异常统一处理,记得关注噢~

posted @ 2020-05-11 10:20  YoungDeng  阅读(132)  评论(0编辑  收藏  举报