分布式网关入门:概念详解与动态路由实现

分布式网关入门指南:了解网关概念、作用及动态路由实现方式,助你构建高性能微服务架构。

原文标题:分布式网关介绍及动态路由实现

原文作者:牧羊人的方向

冷月清谈:

本文介绍了分布式服务网关的概念及其在微服务架构中的重要作用。网关作为统一入口,提供了路由转发、安全认证、流量控制等核心功能,简化了客户端访问,并提升了系统的可维护性。文章详细解释了动态路由的优势,并列举了几种常见的分布式网关实现方式,包括Nginx+Lua、Kong、Spring Cloud Zuul和Spring Cloud Gateway,并对它们的特点进行了比较。最后,文章探讨了基于Nacos、数据库和内存三种不同方式实现Spring Cloud Gateway动态路由的方法。

怜星夜思:

1、文章提到了几种网关的实现方式,实际应用中,选择哪种网关方案需要考虑哪些因素?
2、动态路由在实际应用中,除了文章提到的三种方式,还有哪些其他的实现方式?
3、文章中提到Zuul和Gateway的对比,在选择时,性能是唯一的考量因素吗?还有哪些因素需要考虑?

原文内容

服务网关作为分布式系统对外服务的统一入口,设计功能上具有路由转发、熔断限流、安全认证以及监控等功能。本文简要介绍服务网关的基本概念,以及动态路由的实现方式,以加深了解。

1、什么是分布式服务网关
1.1 服务网关的基本概念

网关是客户端访问系统请求及系统响应所要经过的网络关口,网关封装了系统内部功能架构,为每个客户端访问提供不同的接口。网关在功能上主要是进行请求过滤,具体包括请求路由转发、安全认证、流量控制、负载均衡、日志、监控等功能。

在分布式系统尤其是微服务架构中,一个应用系统被拆分为多个服务,但是像路由请求、安全认证、流量控制、日志和监控等功能对每个服务来说都是标准的功能模块。如果没有服务网关,那么每个服务就需要单独实现一套这样的模块,功能重复并且不利于统一管理。另外,多个微服务可能部署在不同的服务器上,传统的直接访问应用服务的方式增加了客户端的复杂度,因此需要一个统一的客户端访问入口进行标准化管理。

1.2 服务网关的作用及功能
服务网关作为客户端请求的统一入口,具备以下功能:
  1. 动态路由:根据请求路由到对应的服务上去,如果服务不可用还会有重试机制

  2. 负载均衡:多服务器提供同一种服务,网关会从配置中心拉取各服务注册信息,然后将请求负载到这些服务器进行处理

  3. 流量控制:限制并发请求的流量,避免内部系统受到冲击

  4. 安全认证:网关对相关权限验证、脱敏和流量清洗、签名和黑名单功能

  5. 熔断降级:当服务不可用或者访问量过大,网关可以将请求做降级,将流量打到其他服务器或者做其他处理,提示用户暂时不可用

  6. 灰度发布:先进行小部分服务器升级,通过网关将少量的服务路由到已升级的服务器用来测试服务是否正常,大部分请求依旧在老版本服务器上处理

  7. 日志服务:服务访问情况监控和统计报表,请求的吞吐量、并发数、流量监控、性能监控和日常告警等

1.3 常见的分布式服务网关
常见的分布式服务网关有以下几种:
  1. Nginx+Lua:基于Nginx的服务端网关方案,实现web服务器、反向代理、负载均衡、动静分离等功能;

  2. Kong:基于OpenResty实现,是一款高性能、云原生、可扩展的网关系统;

  3. SpringCloud Zuul:基于Netty实现了异步IO,可自定义过滤器来处理请求,同时提供了动态路由、监控、弹性负载和安全功能

  4. SpringCloud Gateway:基于Spring WebFlux,提供统一的路由方式,并且基于Filter链的方式提供了网关的基本功能,如安全、限流、监控等

1.3.1 Nginx+Lua实现
在《》中对Nginx的功能做了简单的介绍,可以实现反向代理、负载均衡、动静分离等功能。

1.3.2 Kong

Kong基于OpenResty实现,OpenResty是基于Nginx的库,它将Nginx进行封装,使用Lua脚本就可以对Nginx进行插件化管理。

  • 使用PostgreSQL或Cassandra来对其配置文件进行持久化存储,使得可以进行集群管理

  • 提供了插件模型,使用Lua脚本来对Nginx整个生命周期进行扩展。实现了一些常用插件( 限流、熔断、验权等 )

  • 提供了Http/Rest的接口来实现配置,使得其可以更简单的构建图形化界面进行动态配置

1.3.3 Spring Cloud Zuul

Zuul是来自Netflix的基于JVM的路由器和服务器端负载均衡器,为微服务架构提供了前门保护的作用,同时将权限控制这些较重的非业务逻辑内容迁移到服务路由层面,使得服务集群主体能够具备更高的可复用性和可测试性。官方定义如下:

Zuul是在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门。即Zuul为微服务集群提供代理、过滤和路由等功能。

Zuul会把外部的请求过程分为不同阶段,每个阶段提供一系列的过滤器。这些过滤器实现了以下功能:
  1. 身份验证和安全性:对需要身份验证的资源进行过滤,拒绝处理不符合身份认证的请求。

  2. 观察和监控:跟踪重要的数据,为我们展示准确的请求状况

  3. 动态路由:将请求动态路由到不同的服务集群

  4. 负载均衡:设置每种请求的处理能力,删除那些超出限制的请求

  5. 静态响应处理:提供静态的过滤器,直接响应一些请求,而不将它们转发到集群内部

  6. 路由多样化:除了将请求路由到Spring Cloud集群内部,还可以将请求路由到其他服务

Zuul的核心原理是一系列filters,在请求路由合并到用户处理逻辑的过程中,这些过滤器参与验证、加载等过滤处理。zul定义了四种标准过滤器类型,用于请求的典型生命周期。
  1. PRE:这样的过滤器在请求被路由之前被调用。可以利用这样的过滤器实现认证,选择集群内要求的微服务器,记录调试信息。

  2. ROUTING:该过滤器将请求路由到微服务。此过滤器用于生成发送到微服务的请求,并使用Apachehttp客户端或Netfilx Ribbon请求微服务。

  3. POST:在路由到微服务之后执行该过滤器。使用此过滤器可以将标准HTTP Header添加到响应中,收集统计信息和指标,以及将响应从微服务器发送到客户端。

  4. ERROR:如果在其他阶段发生错误,则执行该过滤器。

1.3.4 Spring Cloud Gateway

Spring Cloud Gateway是Spring官方基于Spring5.0,Spring Boot2.0和Project Reactor等技术开发的网关,Spring Cloud Gateway旨在为微服务架构提供简单,有效且统一的API路由管理方式。Spring Cloud Gateway作为Spring Cloud 生态系统中的网关,目标是替代Netflix Zuul,其不仅提供统一的路由方式,并且还基于Filter链的方式提供了网关基本的功能,例如:安全,监控、埋点,限流等。

1)Spring Cloud Gateway特性
  • 基于Spring Framework 5、Reactor和Spring Boot 2.0框架。

  • 根据请求的属性可以匹配对应的路由。

  • 集成Hystrix。

  • 集成Spring Cloud DiscoveryClient。

  • 把易于编写的Predicates和Filters作用于特定路由。

  • 具备一些网关的高级功能,如动态路由、限流、路径重写。

2)Spring Cloud Gateway核心概念
  • Filter(过滤器):和Zuul的过滤器在概念上类似,可以使用Filter拦截和修改请求,实现对上游的响应,进行二次处理,实现横切与应用无关的功能,如安全、访问超时设置、限流等功能。

  • Route(路由):网关配置的基本组成模块,和Zuul的路由配置模块类似。一个Route模块由一个ID、一个目标URI、一组断言和一组过滤器组成。如果断言为真,则路由匹配,目标URI会被访问。

  • Predicate(断言):Predicate来自Java 8的接口,它可以用来匹配来自HTTP请求的任何内容,例如headers或参数。接口包含多种默认方法,并将Predicate组合成复杂的逻辑(与、或、非),可以用于接口参数校验、路由转发判断等。

3)Spring Cloud Gateway核心处理流程

  1. Gateway的客户端回向Spring Cloud Gateway发起请求

  2. 请求首先会被HttpWebHandlerAdapter进行提取组装成网关的上下文,然后网关的上下文会传递到DispatcherHandler。

  3. DispatcherHandler是所有请求的分发处理器,DispatcherHandler主要负责分发请求对应的处理器,比如将请求分发到对应RoutePredicateHandlerMapping(路由断言处理器映射器)。

  4. 路由断言处理映射器主要用于路由的查找,以及找到路由后返回对应的FilteringWebHandler。

  5. FilteringWebHandler主要负责组装Filter链表并调用Filter执行一系列Filter处理,然后把请求转到后端对应的代理服务处理,

  6. 处理完毕后,将Response返回到Gateway客户端

1.3.5 Zuul和Gateway对比
Spring cloud Zuul和Spring Cloud Gateway处理的都是http请求,底层是serverlet服务。不同之处有以下几点:
  • 内部实现

    • gateway对比zuul多依赖了spring-webflux,在spring的支持下,功能更强大,内部实现了限流、负载均衡等,扩展性也更强,但同时也限制了仅适合于Spring Cloud套件

    • zuul则可以扩展至其他微服务框架中,其内部没有实现限流、负载均衡等

  • 是否支持异步

    • Zuul 1.x只支持同步。Zuul 2.x基于Netty实现了异步IO

    • Gateway支持异步。理论上gateway则更适合于提高系统吞吐量(但不一定能有更好的性能),最终性能还需要通过严密的压测来决定

  • 扩展性

    • Gateway具有更好的扩展性,稳定性也是非常好的

  • 性能

    • Spring webflux 有一个全新的非堵塞的函数式 Reactive Web 框架,可以用来构建异步的、非堵塞的、事件驱动的服务,在伸缩性方面表现非常好

    • Zuul 1.x,是一个基于阻塞io的API Gateway。Zuul已经发布了Zuul 2.x,基于Netty,也是非阻塞的,支持长连接,但Spring Cloud暂时还没有整合计划

在实际生产使用中,Zuul 1.x虽然使用的是同步io,但是可以通过参数优化提高性能理论上可以达到极限性能,而springcloud gateway使用的是异步io,不需优化既可以达到接近极限的性能。

2、分布式网关动态路由实现
相比较静态路由,动态路由的好处是对路由配置的修改、新增或者删除,可以动态生效,不需要重启网关服务。这里主要介绍基于Spring Cloud Gateway的动态路由几种实现方式,包括:
  • 基于Nacos

  • 基于数据库

  • 基于内存

2.1 基于Nacos的动态路由

Nacos集成在Spring Cloud中,提供了快速实现动态服务发现、服务配置、服务元数据及流量管理等功能。基于Nacos动态路由是通过Nacos更新修改路由配置,通过接口调用动态更新到本地缓存中。

1)加载配置和监听路由

configInfo = configService.getConfig(NACOS_ROUTE_DATA_ID, NACOS_ROUTE_GROUP, DEFAULT_TIMEOUT);
configService.addListener(dataId, group, new Listener()

2)路由的动态添加和删除

routeDefinitionWriter.delete(Mono.just(id)).subscribe()
routeDefinitionWriter.save(Mono.just(definition)).subscribe();

3)路由信息刷新到本地缓存

publisher.publishEvent(new RefreshRoutesEvent(this));
2.2 基于数据库的动态路由

基于数据库的动态路由实现,是将路由信息写入数据库中,再通过接口读取路由配置信息以及更新路由信息,最后刷新到本地缓存中。

2.3 基于内存的动态路由

基于本地内存的方式比较简单,Spring Boot已经提供了两个组件Spring Boot Admin和Spring Boot Actuator。通过调用接口更新路由信息,再刷新即可完成路由更新

o.s.c.g.a.GatewayControllerEndpoint:
{GET /routes/{id}}: route(String)
{GET /routes}: routes()
{GET /routedefinitions}: routesdef()
{GET /globalfilters}: globalfilters()
{GET /routefilters}: routefilers()
{GET /routepredicates}: routepredicates()
{GET /routes/{id}/combinedfilters}: combinedfilters(String)
{DELETE /routes/{id}}: delete(String)
{POST /routes/{id}}: save(String,RouteDefinition)
{POST /refresh}: refresh()

参考资料:

  1. https://cloud.tencent.com/developer/article/2110571

  2. https://blog.csdn.net/huangjinjin520/article/details/124976940

  3. https://docs.spring.io/spring-cloud-gateway/docs/

  4. https://blog.csdn.net/github_35631540/article/details/116241016

  5. https://blog.csdn.net/weixin_39885962/article/details/125714689

  6. 《重新定义Spring Cloud实战》,许进等著

  7. https://blog.csdn.net/u013733643/article/details/123970824

除了技术栈,还要考虑成本、社区支持、文档完善程度等。比如,Kong的社区版功能有限,企业版需要付费。而Nginx+Lua需要自己编写和维护Lua脚本,对团队的技术能力有一定要求。Spring Cloud Gateway作为Spring官方项目,社区支持和文档都比较完善。

还要考虑网关的功能需求。Zuul 1.x只支持同步,而Gateway支持异步。如果需要处理高并发请求,Gateway的异步特性可能更有优势。但是Zuul 2.x也支持异步,并且提供了更丰富的功能,只是Spring Cloud还没有整合计划。

选择网关方案要看具体场景。比如,如果团队熟悉Nginx和Lua,并且对性能要求极高,那么Nginx+Lua是不错的选择。如果需要更丰富的插件和易于管理的界面,Kong可能更合适。如果是Spring Cloud生态,Gateway或Zuul就更方便集成,选择哪个取决于对异步和扩展性的需求。

性能当然很重要,但不是唯一因素。选择Zuul还是Gateway还要考虑项目的整体架构、团队的技术栈、社区支持、文档完善程度等等。如果团队对Spring技术栈比较熟悉,并且项目已经使用了Spring Cloud,那么Gateway是更自然的选择。

一些云平台也提供了自己的网关服务,比如AWS的API Gateway,Azure的Application Gateway等。这些云网关通常支持动态路由,并且可以与其他云服务集成,方便管理和部署。

动态路由的实现方式有很多,除了Nacos、数据库和内存,还可以使用配置中心,例如Apollo、Consul等。这些配置中心可以集中管理路由配置,并提供实时更新的功能。

我觉得还得考虑团队的规模和项目的复杂度。小项目或者团队技术能力有限的情况下,使用现成的网关方案,比如Kong或者Spring Cloud Gateway,可以快速上手,降低开发成本。大项目或者对定制化需求比较高的情况下,Nginx+Lua的灵活性更高。

我觉得易用性和可维护性也很重要。Gateway的配置相对简单,并且与Spring Cloud集成紧密,方便管理和监控。而Zuul的配置相对复杂,需要一定的学习成本。

还可以使用一些服务发现组件,例如ZooKeeper、Eureka等,结合自定义的路由规则来实现动态路由。当服务实例发生变化时,网关可以根据服务发现组件提供的最新信息动态更新路由。