什么是微服务网关?SpringCloud Gateway保姆级入门教程

您所在的位置:网站首页 服务网关的功能是什么 什么是微服务网关?SpringCloud Gateway保姆级入门教程

什么是微服务网关?SpringCloud Gateway保姆级入门教程

2024-02-21 19:23| 来源: 网络整理| 查看: 265

什么是微服务网关

SpringCloud Gateway是Spring全家桶中一个比较新的项目,Spring社区是这么介绍它的:

该项目借助Spring WebFlux的能力,打造了一个API网关。旨在提供一种简单而有效的方法来作为API服务的路由,并为它们提供各种增强功能,例如:安全性,监控和可伸缩性。

而在真实的业务领域,我们经常用SpringCloud Gateway来做微服务网关,如果你不理解微服务网关和传统网关的区别,可以阅读此篇文章 Service Mesh和API Gateway关系深度探讨 来了解两者的定位区别。

以我粗浅的理解,传统的API网关,往往是独立于各个后端服务,请求先打到独立的网关层,再打到服务集群。而微服务网关,将流量从南北走向改为东西走向(见下图),微服务网关和后端服务是在同一个容器中的,所以也有个别名,叫做Gateway Sidecar。

imgimg

为啥叫Sidecar,这个词应该怎么理解呢,吃鸡里的三蹦子见过没:

image-20210519001602403image-20210519001602403

摩托车是你的后端服务,而旁边挂着的额外座椅就是微服务网关,他是依附于后端服务的(一般是指两个进程在同一个容器中),是不是生动形象了一些。

由于本人才疏学浅,对于微服务相关概念理解上难免会有偏差。就不在此详细讲述原理性的文字了。

本文只探讨SpringCloud Gateway的入门搭建和实战踩坑。 如果小伙伴们对原理感兴趣,可以等后续原理分析文章。

注:本文网关项目在笔者公司已经上线运行,每天承担百万级别的请求,是经过实战验证的项目。

文章目录让我们造一个网关把 引入pom依赖编写yml文件接口转义问题获取请求体(Request Body)踩坑实战 获取客户端真实IP尾缀匹配总结源代码

完整项目源代码已经收录到我的Github:

https://github.com/qqxx6661/springcloud_gateway_demo

让我们造一个网关把引入pom依赖

我使用了spring-boot 2.2.5.RELEASE作为parent依赖:

org.springframework.boot spring-boot-starter-parent 2.2.5.RELEASE

在dependencyManagement中,我们需要指定sringcloud的版本,以便保证我们能够引入我们想要的SpringCloud Gateway版本,所以需要用到dependencyManagement:

org.springframework.cloud spring-cloud-dependencies Hoxton.SR8 pom import

最后,是在dependency中引入spring-cloud-starter-gateway:

org.springframework.cloud spring-cloud-starter-gateway

如此一来,我们便引入了2.2.5.RELEASE版本的网关:

此外,请检查一下你的依赖中是否含有spring-boot-starter-web,如果有,请干掉它。因为我们的SpringCloud Gateway是一个netty+webflux实现的web服务器,和Springboot Web本身就是冲突的。

org.springframework.boot spring-boot-starter-web

做到这里,实际上你的项目就已经可以启动了,运行SpringcloudGatewayApplication,得到结果如图:

image-20210518192253579image-20210518192253579编写yml文件

SpringBoot的核心概念是约定优先于配置,在以前初学Spring时,一直不理解这句话的意思,在使用SpringCloud Gateway时,更加深入的理解了这句话。在默认情况下,你不需要任何的配置,就能够运行起来最基本的网关。针对你之后特定的需求,再去追加配置。

而SpringCloud Gateway更强大的一点就是内置了非常多的默认功能实现,你需要的大部分功能,比如在请求中添加一个header,添加一个参数,都只需要在yml中引入相应的内置过滤器即可。

可以说,yml是整个SpringCloud Gateway的灵魂。

一个网关最基本的功能,就是配置路由,在这方面,SpringCloud Gateway支持非常多方式。比如:

通过时间匹配通过 Cookie 匹配通过 Header 属性匹配通过 Host 匹配通过请求方式匹配通过请求路径匹配通过请求参数匹配通过请求 ip 地址进行匹配

这些在官网教程中,都有详细的介绍,就算你百度下,也会有很多民间翻译的入门教程,我就不再赘述了,我只用一个请求路径做一个简单的例子。

在公司的项目中,由于有新老两套后台服务,我们使用不同的uri路径进行区分。

老服务路径为:url/api/xxxxxx,服务端口号为8001新服务路径为:url/api/v2/xxxxx,服务端口号为8002

那么可以直接在yml里面配置:

logging: level: org.springframework.cloud.gateway: DEBUG reactor.netty.http.client: DEBUG spring: cloud: gateway: default-filters: - AddRequestHeader=gateway-env, springcloud-gateway routes: - id: "server_v2" uri: "http://127.0.0.1:8002" predicates: - Path=/api/v2/** - id: "server_v1" uri: "http://127.0.0.1:8001" predicates: - Path=/api/**

上面的代码解释如下:

logging:由于文章需要,我们打开gateway和netty的Debug模式,可以看清楚请求进来后执行的流程,方便后续说明。default-filters:我们可以方便的使用default-filters,在请求中加入一个自定义的header,我们加入一个KV为gateway-env:springcloud-gateway,来注明我们这个请求经过了此网关。这样做的好处是后续服务端也能够看到。routes:路由是网关的重点,相信读者们看代码也能理解,我配置了两个路由,一个是server_v1的老服务,一个是server_v2的新服务。**请注意,一个请求满足多个路由的谓词条件时,请求只会被首个成功匹配的路由转发。**由于我们老服务的路由是/xx,所以需要将老服务放在后面,优先匹配词缀/v2的新服务,不满足的再匹配到/xx。

来看一下http://localhost:8080/api/xxxxx的结果:

image-20210519104944910image-20210519104944910

来看一下http://localhost:8080/api/v2/xxxxx的结果:

image-20210519105037416image-20210519105037416

可以看到两个请求被正确的路由了。由于我们真正并没有开启后端服务,所以最后一句error请忽略。

接口转义问题

在公司实际的项目中,我在搭建好网关后,遇到了一个接口转义问题,相信很多读者可能也会碰到,所以在这里我们最好是防患于未然,优先处理下。

问题是这样的,很多老项目在url上并没有进行转义,导致会出现如下接口请求,http://xxxxxxxxx/api/b3d56a6fa19975ba520189f3f55de7f6/140x140.jpg?t=1"

这样请求过来,网关会报错:

java.lang.IllegalArgumentException: Invalid character '=' for QUERY_PARAM in "http://pic1.ajkimg.com/display/anjuke/b3d56a6fa19975ba520189f3f55de7f6/140x140.jpg?t=1"

在不修改服务代码逻辑的前提下,网关其实已经可以解决这件事情,解决办法就是升级到2.1.1.RELEASE以上的版本。

The issue was fixed in version spring-cloud-gateway 2.1.1.RELEASE.

所以我们一开始就是用了高版本2.2.5.RELEASE,避免了这个问题,如果小伙伴发现之前使用的版本低于 2.1.1.RELEASE,请升级。

获取请求体(Request Body)

在网关的使用中,有时候会需要拿到请求body里面的数据,比如验证签名,body可能需要参与签名校验。

但是SpringCloud Gateway由于底层采用了webflux,其请求是流式响应的,即 Reactor 编程,要读取 Request Body 中的请求参数就没那么容易了。

网上谷歌了很久,很多解决方案要么是彻底过时,要么是版本不兼容,好在最后参考了这篇文章,终于有了思路:

https://www.jianshu.com/p/db3b15aec646

首先我们需要将body从请求中拿出来,由于是流式处理,Request的Body是只能读取一次的,如果直接通过在Filter中读取,会导致后面的服务无法读取数据。

SpringCloud Gateway 内部提供了一个断言工厂类ReadBodyPredicateFactory,这个类实现了读取Request的Body内容并放入缓存,我们可以通过从缓存中获取body内容来实现我们的目的。

首先新建一个CustomReadBodyRoutePredicateFactory类,这里只贴出关键代码,完整代码请看可运行的Github仓库:

@Component public class CustomReadBodyRoutePredicateFactory extends AbstractRoutePredicateFactory { protected static final Log log = LogFactory.getLog(CustomReadBodyRoutePredicateFactory.class); private List


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3