1.为什么微服务一定需要网关?
客户端直接与微服务通信,存在问题:
1)客户端会多次请求不同的微服务,增加客户端的复杂性
2)很多横切(权限校验、日志记录、限流、监控等)功能,每个服务都需要独立实现
方式一:假设写到一个公共服务中,其他服务都依赖这个公共服务,虽然解决了代码冗余问题,但会出现两个问题:
- 会增加jar包大小,使用docker镜像进行部署的场景,jar越小越好。
- 如果公共服务进行升级,修改了权限校验,我们需要将所有依赖的服务重新编译部署才生效
方式二:假设直接在微服务应用中通过调用鉴权服务来实现校验,但是这种的做法仅仅只是解决了鉴权逻辑的分离,并没有本质上将这部分不属于冗余的逻辑从原有的微服务应用中拆分出,冗余的拦截器或过滤器依然存在
3)难以重构,随着项目的迭代,可能需要重新划分微服务,例如将多个微服务合并成一个,或者将一个微服务拆分成多个,如果客户端直接与微服务通信,那么重构将很难实施(重新划分微服务后,域名可能都变了)
网关:微服务网关封装了应用程序的内部结构,客户端只需要与网关交互,而无需直接调用特定微服务的接口。
优点:
- 易于监控,可在微服务网关收集监控数据并将其推送到外部系统进行分析。
- 易于认证,可在微服务网关上进行认证,然后再将请求转发到后端的微服务,而无须在每个服务中进行认证
2.Gateway 介绍
一听到“网关”这个词儿,你第一个想到的一定是 Nginx。没错,Nginx 作为网关领域的一哥,在大中小厂里的应用面那可不是一般的广泛。那究竟是 Nginx 过气了,还是 Spring Cloud Gateway 太牛了,以至于我们要引入一个新的网关组件呢?
其实都不是,Gateway 并没有抢 Nginx 的地盘,此网关非彼网关,Nginx 和 Gateway 在微服务体系中的分工是不一样的。Gateway 作为更底层的微服务网关,通常是作为外部 Nginx 网关和内部微服务系统之间的桥梁,起了这么一个承上启下的作用。
也许你会想,企业级应用不就是在最外层架设一道网关层么?其实不然,在大型微服务应用中,我们往往会搭建多个网关组件,这些网关的应用场景也各有不同。
接下来,我们就通过一个例子,了解一个服务请求从浏览器发出,直到抵达后台服务的整个过程。你可以在图中看到,这个服务请求都经历了哪些网关层。然后,我会再带你分析,为何需要 Gateway 横在中间做这么一层网络转发。
首先,网址解析的第一步是 DNS 解析。当用户在浏览器里输入一串网址时,这个网址会被 DNS 层解析成一个可被访问的 IP 地址。为了避免单点故障,我们可以在这一层加个双保险,比如将域名映射成两个 IP 地址做主备,又或者根据用户 IP 所属区域做 Loadbalancer,将请求导向就近的 IP 地址,这两种方式都是可以的。
在这里你会发现,每一个 IP 地址背后都别有洞天,因为它们只是所谓的虚 IP,后面会映射到一个大型的网关集群,这个集群便是我们业务系统对外的第一道网关。在这个环节中,使用最广泛而且最经济实惠的技术选型就是 Nginx 反向代理。因为它拥有超强的并发能力,而且很节省内存资源。
到这还没完,刚才我提到在大厂里往往会有多个网关组件。也就是说,一个请求抵达最外层的 Nginx 服务之后,还可能会经历多级 LVS+Nginx 集群的转发。大公司之所以这么玩,主要是出于对网络安全的考虑。他们往往会根据业务系统的属性和安全级别来设置不同的网络分区,而这些网络分区之间是相互独立的,分区之间需要开通白名单或者防火墙才能打通连接。比如有的网络分区可以直接对外,而有的高安全级别的分区(比如金融类业务)则部署在更底层的 Secure Zone 当中。这就和我们使用跳板机访问线上机房是一个道理。
然后,请求经过了多级网关服务的转发,抵达了最后的微服务层。在这一层上,Gateway 就需要出马来负责请求转发了。
那 Gateway 早不出现晚不出现,偏偏在请求抵达微服务的最后一刻冒了出来,它的底层逻辑是什么呢?
Gateway 既然叫“微服务网关”,就说明它自己就是一个微服务。换句话说,它也是 Nacos 服务注册中心的一员。既然 Gateway 能连接到 Nacos,那么就意味着它可以轻松获取到 Nacos 中所有服务的注册表。这样一来,Gateway 就可以根据本地的路由规则,将请求精准无误地送达到每个微服务组件中。
使用 Gateway 有一个显而易见的好处,那就是高可扩展性。当你对后台的微服务集群做扩容或缩容的时候,Gateway 可以从 Nacos 注册中心轻松获取所有服务节点的变动,不需要任何额外的配置,一切都在无感知的情况下自然而然地发生。如果使用其他技术方案,你可能还需要花些力气修改 VIP Pool 中的节点列表,将新增的机器手动添加到列表中,还要把移除的机器从列表中删除。
Gateway 的另一个优点就是高度可定制化。它提供了一种对开发人员非常友好的方式,可以让你通过 Java 代码去定制各种复杂的路由逻辑,还可以使用 Filter 对请求进行加工。
那么接下来,我就带你了解 Gateway 的几个核心功能模块,看一看它是如何组装路由规则的。
3.Gateway 路由规则
Gateway 的路由规则主要有三个部分,分别是路由、谓词和过滤器。我这里画了一张图来表示 Gateway 的路由结构。
你一定注意到了我在路由的框里还画了一个“过滤器”,还连了两条虚线到路由的“目标地址”,那么过滤器和路由、目标地址之间是什么关系呢?其实 Gateway 在把请求转发给目标地址的过程中,把这个任务全权委托给了 Filter(过滤器)来处理。我用一幅图为你比划一下 Filter 做了什么事儿。
Gateway 组件使用了一种 FilterChain 的模式对请求进行处理,每一个服务请求(Request)在发送到目标服务之前都要被一串 FilterChain 处理。同理,在 Gateway 接收服务响应(Response)的过程中也会被 FilterChain 处理一把。
Gateway 的过滤器主要分为两种,一种是 GlobalFilter,也就是“全局过滤器”;另一种是 GatewayFilter,也就是对指定路由生效的“局部过滤器”。
全局过滤器继承自 GlobalFilter 接口,它的作用大多是“例行公事”,也就是一些底层能力的支持。比如,RouteToRequestUrlFilter 这个全局过滤器就是用来解析“目标服务地址”的。
除此之外,Gateway 还有一系列用来做路径转发、请求跨域、WebSocket、WebClient 和 Loadbalancer 功能支持的全局过滤器。如果你想深入了解,可以参考 GatewayAutoConfiguration 的源码,这个类是 Gateway 的自动装配器,里面包含了大量 GlobalFilter 的声明。就算你不做任何配置,项目在初始化的时候也会把一大家子全局过滤器添加到上下文中。
GatewayFilter 也就是局部过滤器,它的功能可就多了。Gateway 提供了一系列的内置过滤器,可以实现对 Request/Response 的修改、请求路径修改、调用重试、限流等等功能。当然了,你也可以通过 Gateway 的扩展接口实现一个自定义过滤器并应用到路由规则中。