多层网关已成过去,网关多合一成潮流,网关改造正当时丨Higress 正式发布 1.0 版本
Posted 阿里系统软件技术
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多层网关已成过去,网关多合一成潮流,网关改造正当时丨Higress 正式发布 1.0 版本相关的知识,希望对你有一定的参考价值。
作者:Higress 团队
01 前言
K8s 通过 Ingress / Gateway API 将网关标准化,逐步将安全网关、流量网关、微服务网关内聚,解决从单体到微服务到云原生多层网关的复杂度,合久必分,分久必合,多层网关已成过去,网关多合一成潮流,成为 K8s 开发者和微服务开发者共同关心的话题。
02 Higress 1.0 正式发布,即官方推荐生产可用
Higress 是阿里云开源的下一代网关,从 2022 年 11 月在云栖大会上宣布开源,走过大半年时间,发布了 GA 版本 1.0.0,即官方推荐生产可用。回顾 Higress 的发展历程,经历了三个阶段:
Higress 的技术选型和首次业务落地(2020.05~2020.11)
Higress 的创建源于阿里内部的“本地生活战役”,核心技术目标是实现阿里巴巴业务域与蚂蚁业务域之间 RPC 直接调用,但因阿里巴巴与蚂蚁业务域网络是隔离的,即网络是不通的,很自然想到利用网关来解决此问题。利用网关来解决阿里巴巴与蚂蚁跨业务域 RPC 互通问题,首先要对网关做技术选型。选型期,除了关注技术方案是否完美支持 HTTP/gRPC 协议、支持丰富路由策略以及是否业内主流技术,还关注是否支持热更新。
热更新是我们的核心关注点。
Tengine/Nginx 的配置更新需要 reload,reload 需要重启 worker 进程,重启时会引起流量抖动,对长连接影响尤为明显。在网关的集群规模非常大时,更是不能随意的做 reload,这时就会引发一个矛盾点:业务向网关提交配置后,希望能快速验证,但受限于 reload 机制和稳定性要求,无法满足业务快速验证与快速试错的诉求。
如何解决这点呢?
一是采用两层网关,即流量网关 + 业务网关;二是实现网关原生支持配置热更新。除了对比不同方案的优劣势,我们也调研了 Envoy 作为网关在业界的趋势,结论是目前 Envoy 作为 K8s 中的 Ingress Provider 增长最快的事实(Ingress Provider 指 K8s Ingress 规范具体实现,因 K8s Ingress 自身只是规范定义,是 K8s 下外部流量进入集群内部的网关规范定义),我们最终选择了 Envoy 来实现两层网关,并完美支撑双11大促每秒数十万的请求流量。
Higress 的重要演进和服务更多业务场景(2020.12~2021.10)
随着在阿里巴巴和蚂蚁的成功落地,越来越多的业务场景找到了我们。
这个过程中,Higress 实现了东西向、南北向全域流量的调度分发,东西向上不仅支持跨业务域的蚂蚁 RPC 互通,也扩展到了混合云的云上云下 RPC 互通场景,覆盖钉钉文档、阿里视频云、达摩院的店小蜜、智慧数字人等。
2021 年,阿里巴巴开启了中间件三位一体战役,目标是用云产品支撑集团业务。我们开始将孵化成熟的 Higress 技术沉淀为云产品,即目前阿里云上提供的 MSE 云原生网关,一方面面向广大的公有云用户提供托管的网关服务,另一方面也对内服务集团。
Higress 对外开源,通过社区力量加速发展(2021.11~至今)
随着 Higress 成为云产品服务于更多外部用户,我们逐步发现用户对 Higress 提出了更高的要求,其中反馈较多的大的需求点是插件扩展、Waf 防护、多注册中心、Nginx Ingress 注解兼容以及 HTTP 转 Dubbo 协议,当然也有很多小的需求点在此就不一一列出,因此该阶段我们重点发力在上述用户反馈的高频需求。
开源已经成为软件发展的必然趋势与快速路径,因为社区的力量是非常强大的。
因此我们将这套经过内部实践沉淀下来的网关方案 Higress 正式对外开源,以 Kubernetes Ingress 网关为契机带来了流量网关与微服务网关融合的可能性,结合阿里内部实践沉淀 Higress 实现了流量网关 + 微服务网关 + 安全网关三合一的高集成能力,同时深度集成了 Dubbo、Nacos、Sentinel 等,能够帮助用户极大的降低网关的部署及运维成本,而且能力不打折。
03 为什么 Higress 能替代多层网关,成为下一代网关
Higress 是标准化、高集成、易扩展、热更新的云原生网关。无缝集成容器和微服务生态,是云原生时代的默认选项。
高集成,连接微服务生态
Envoy 提供了 EDS/DNS/STATIC 等多种类型的 Cluster,Higress 基于此具备了对接多种服务发现的能力,可以实现:
-
通过 Nacos 发现服务 (EDS)
-
通过 Zookeeper 发现服务 (EDS)
-
通过 K8s Service 发现服务 (EDS)
-
通过 DNS 域名发现服务 (DNS)
-
通过配置静态 IP 发现服务 (STATIC)
通过 Higress 控制台可以很方便地进行相应的服务发现配置:
随着云原生技术的发展,不少企业开始从传统架构向云原生架构演进,但这过程中传统架构部署的服务无法被 K8s 的 Ingress 发现并路由成为一个阻塞点,导致业务架构无法平滑地向云原生平滑演进。Higress 依托于 Nacos 等注册中心的能力,无论服务是否部署在 K8s 集群内,都可以发现服务并进行请求路由。如上图所示,业务在迁移过程中,可以通过 Higress 将 5% 的灰度流量导入部署在 K8s 上新架构的服务中,进行灰度测试验证,逐步切流,从而实现业务架构平稳升级。
对于灰度能力,Higress 实现了和 OpenKruise Rollout 进行联动,可以实现服务灰度发布。整个 Rollout 过程,可以实现自动整合 Deployment、Service、Ingress 一起工作,并向用户屏蔽底层资源变化。用户无需手动编辑多个 K8s 资源,即可轻松使用金丝雀发布,A/B Test 等灰度机制。
易扩展,提升网关的业务使命
将插件的生命周期划分为三个阶段:
-
插件开发阶段
-
分发集成阶段
-
运行生效阶段
Envoy 提供的 Wasm 插件机制,解决了插件运行生效阶段的问题,基于 ECDS 配置更新机制,插件代码和配置发生变更均不会导致连接断开,并且插件运行在安全沙箱中,即使代码逻辑出现空指针等异常,也不会导致网关发生 Crash。
在插件开发阶段,Higress 基于 Proxy Wasm 生态提供了更容易上手的 C++ 和 Go 语言的 Wasm 插件 sdk,在 sdk 中封装了插件路由和域名级生效的机制,开发者只需关心插件配置解析和运行逻辑即可,在分发集成阶段,Higress 定义了 Wasm 插件的 OCI 镜像规范,将插件的 README 文档,配置字段约束信息,以及 Wasm 文件等一起打包在一个 OCI 镜像中,可以通过支持 OCI 格式的镜像仓库进行存储和拉取。并且可以通过 Higress 控制台快速启用插件:
Wasm 插件的 OCI 镜像规范:
Higress 的插件机制和传统的基于 OpenResty Lua 扩展的插件机制最本质的区别,也是往往最容易被开发者忽略的是插件分发集成的环节。传统的 Lua 插件扩展机制,插件自身的版本生命周期是跟着网关的版本走,插件版本更新,以及自己开发插件都需要重新部署网关。而 Higress 依托于 OCI 镜像进行网关插件的版本管理和分发,实现了插件版本生命周期和网关版本的解耦,用户只需调整一行插件 OCI 镜像地址,即可完成插件的热更新,整个过程网关连接不会发生断开,流量完全无损。
基于此,在网关上的业务插件逻辑可以很方便地实现热更新。Higress 也基于此能力提供了很多业务认证和安全相关的官方插件,开箱即用。
标准化,降低改造综合成本
因为 Envoy 是面向配置管理服务器设计的配置系统,对程序友好,对手写配置并不友好。因此像 Istio 设计了 VirtualService/DestinationRule/AuthorizationPolicy 等等 CRD 抽象,来解决 Envoy 配置复杂的问题,Istio 的 CRD 本身是解决 ServiceMesh 下复杂的服务治理场景而设计,而对于网关路由场景,更多用户需要的是 Ingress 这样更简单的 API 标准。
Higress 结合阿里内部实践以及阿里云产品沉淀,积累了基于 Ingress API 的丰富的路由策略扩展能力,同时还兼容大部分 Nginx Ingress 能力,并且可以通过 Higress 提供的控制台来创建路由,开箱即用:
Higress 控制台目前对接的底层模型是 Ingress API,如果你对 Gateway API 有了解,会发现 Higress 控制台上的路由模型,也完全可以用 Gateway API 进行描述:
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: foo
spec:
parentRefs:
- name: foo-example
hostnames:
- "foo.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /foo
headers:
- type: Prefix
name: x-higress-header
value: hi
queryParams:
- type: Exact
name: higressQuery
value: hi
method: POST
backendRefs:
- name: foo-service
port: 5678
Gateway API 标准目前还处在 beta 阶段,尚未完全定稿,生产使用我们更多还是建议用户使用 Ingress API,避免后续 Gateway API 标准改动带来 Breaking Change。Higress 对 Gateway API 的支持正在开发中,可以看到基于上面的模型,借助 Higress 控制台可以帮助用户屏蔽底层 API 路由标准的代际差异,实现路由从 Ingress API 平滑迁移到 Gateway API,根治对技术标准追赶的焦虑。
K8s 带来了云原生的路由标准 Ingress/Gateway API,如同 POSIX 定义 Unix 可移植操作系统标准,历时 35 年经久不衰,云原生的路由标准的生命周期一定会远超过 K8s 本身。
热更新,提升接入层稳定性
Higress 基于 Envoy 引擎,为适应现代应用和微服务架构的需求,对传统流量网关(本文以 Nginx 为例)的不足之处进行改进,实现了真正的配置热更新,并让流量网关和微服务网关的融合成为可能。
Nginx 的配置变更 reload ,会导致 downstream 和 upstream 连接都断开触发重连,在高并发场景下,downstream 并发重连将导致 Nginx 的 CPU 飙升,最严重的还是 upstream 的并发重连,很可能打垮后端业务程序的线程池,造成雪崩。
而 Envoy 依托于精确的配置变更管理,做到了真正的热更新。在 Envoy 中 downstream 对应 listener 配置,交由 LDS 实现配置发现;upstream 对应 cluster 配置,交由 CDS 实现配置发现。listener 配置更新重建,只会导致 downstream 连接断开,不会影响 upstream 的连接;downstream 和 upstream 的配置可以独立变更,互不影响。再进一步,listener 下的证书(cert),过滤器插件(filter),路由(router)均可以实现配置独立变更,这样不论是证书/插件/路由配置变更都不再会引起 downstream 连接断开。
04 Higress 生产实践最佳参考
可观测
Higress 提供了自带的 prometheus 和 grafana 可以开箱即用,同时也支持对接用户自建的监控系统,详细请参考:《基于 Prometheus 实现 Higress 流量观测》:https://higress.io/zh-cn/docs/user/prometheus
安装部署
可以使用 Helm 一键完成 Higress 的生产安装部署
helm repo add higress.io https://higress.io/helm-charts
helm install higress -n higress-system higress.io/higress --create-namespace --render-subchart-notes --set higress-console.domain=console.higress.io
通过增加 helm 参数 --set global.local=true 可以在本地 PC 环境基于 k3s/kind 等工具,进行全功能测试和试用:
helm install higress -n higress-system higress.io/higress --create-namespace --render-subchart-notes --set global.local=true --set higress-console.o11y.enabled=false --set higress-console.domain=console.higress.io --set higress-console.admin.password.value=admin
详情可以参考:
《Higress Quickstart》:
https://higress.io/zh-cn/docs/user/quickstart
《Higress 安装部署》:
https://higress.io/zh-cn/docs/ops/deploy-by-helm
微服务生态集成
不论 Dubbo/SpringCloud 服务是否部署在 K8s 集群内, Higress 都可以实现对接。因此在 K8s 场景下,用户可以将 Nginx Ingress 这类流量网关和 Spring Cloud Gateway 这类微服务网关合并,统一替换为 Higress。
《Higress 对接 Dubbo 服务》:
https://cn.dubbo.apache.org/zh-cn/overview/what/ecosystem/gateway/higress/
《Higress 对接 SpringCloud 服务》:
https://higress.io/zh-cn/docs/user/spring-cloud
性能压测数据
Higress 和 Nginx 对比,在 HTTP1 上会略逊一筹,但在现代化协议如 gRPC/HTTP2 上则比 Nginx 好很多。
《gRPC 吞吐是 Nginx 的 4 倍》:https://gist.github.com/johnlanni/aac7480c17b0fde05fa64a20fc93b165
而如果你使用的是 K8s Nginx Ingress,因为其 Lua 代码性能较差,即使在 HTTP1 场景下,Higress 性能也更好,具体数据可以参考:
《K8s 网关选型初判》:https://xie.infoq.cn/article/0a2c9ac4ed139bc28f881d7c3
企业用户落地
Higress 自从4月份发布 1.0.0 RC 版本以来,在社区有大量用户进行安装和测试,帮助 Higress 变得更成熟,并且适应了更多系统和安装环境。同时有多家企业完成了 Higress 技术的落地。
开源软件的发展离不开社区用户的实践,用户的参与和贡献是推动开源项目成功的关键因素。在这里,我们欢迎更多的社区用户加入 Higress 实践落地的行列!欢迎到 Higress GitHub issue 登记信息,社区将邀请您加入 Higress 落地支持群,我们会为落地用户提供指导和帮助。
Higress GitHub issue:
05 社区:回顾和展望
Higress 一路走来,保持一个月发布一个版本的频率,一共完成了 183 个 PR 的合并,发布了 13 个 Release,完成了 5 个里程碑:
Higress 在 1.0 版本 GA 后,将继续保持高投入,并快速迭代。社区未来三个大版本的核心功能规划如下:
- 1.1 版本(6月)
-
- 实现 HTTP2RPC API
- 支持非 K8s 场景下使用
- 1.2 版本(7月)
-
- 支持和 Skywalking 等更多可观测生态集成
- 完整实现 Gateway API
- 1.3 版本(8月)
-
- 实现 API 管理产品形态建设
- 推出独立的 Wasm 插件集市项目
其中 Wasm 插件生态会作为 Higress 社区长期重点投入方向,目前在中科院开源之夏/CCF编程之夏/云原生编程挑战赛等活动中均有 Higress Wasm 插件相关的项目推出,完成项目既能收获项目奖金,还能收获开源荣誉,欢迎有兴趣的同学参与。
致谢
要特别感谢大半年时间里,陪伴 Higress 一起成长的开发者伙伴们,你们为这个项目贡献了自己的时间、智慧和激情。你们的编码技巧、开源贡献和对社区的活跃参与,都使 Higress 变得更加稳定、功能更强大,没有你们的贡献,Higress 无法实现这一里程碑式的发布。以下是为 Higress 做过贡献的外部开发者名单:
Higress Committer
董艺荃(From 携程),刘训灼(From 腾讯),李强林(From 浙江大学),宋鹏远(From 小满科技),凌轶群(From 拼多多)
Higress Contributor
张小俊(From 端点科技),舞羡(From 蚂蚁金服),吴新军(From 创蓝云智),王金山(From 时速云科技),朱云辉(From 哈啰出行),封宇腾(From 西安邮电大学),韦鑫(From 南京航空航天大学),张宇坤(From 浙江大学),张嘉豪 (From 百威集团),陈忠润(From 端点科技),刘天祥(From 端点科技),陈淼晨(From 拓数派),陈特(From 云豪集团),张晨(From 论之语),谢怼怼(From 花生日记),Tom Kerkhove (From Microsoft Azure)
还有没留下真实姓名的贡献者, Github ID 如下:
whalecold,xcbeyond,gczz2022,tanjunchen,burningEvil0,ytwang0320, PerforMance308,OnlyPiglet,casun18,cobb-tx,wangshiqi308,gczz2022, tanjunchen, burningEvil0, ytwang0320, casun18
API网关异步化改造技术选型
背景
目前的网关是基于Spring Boot 1.5.x
和Tomcat 8.5.x
构建,采用多线程阻塞模型,也就是说每个请求都会占用一个独立的线程资源,而线程在JVM中是一个相对比较重的资源。当应用是CPU密集型的或者说依赖的远程服务都正常工作时,这种模型能够很好的满足需求,但一旦后端服务出现了延迟,比如慢查询、FullGC、依赖的第三方接口出问题等情况,线程池很容易被打满,使得整个集群服务出现问题。典型的IO密集型的应用也会有类似的问题,比如网关有很多HTTP请求、RPC远程调用等,当并发量比较大的时候,线程都阻塞在IO等待上,造成线程资源的浪费。
这种模型的优势比较明显:
编程模型简单
易于开发、调试、运维等。本地调试问题支持直接打断点、通过ThreadLocal变量实现监控、通过thread dump即可获取当前请求的处理流程等
但劣势也很明显:
连接数限制。容器的最大线程数一般是固定的,tomcat默认是200,因此当发生网络延迟、FullGC、第三方服务慢等情况造成上游服务延迟时,线程池很容易会被打满,造成新的请求被拒绝,但这个时候其实线程都阻塞在IO上,系统的资源被没有得到充分的利用。
tomcat默认可以接收10000个连接,worker线程默认为200,当线程池被打满后,poller线程会继续接收新的连接请求,并放到epoll队列中,当超过最大连接数后,则会拒绝响应,虽然Tomcat采用了NIO模型,但由于业务线程是同步处理的的,因此当并发比较高时,很容易造成线程池被打满。
容易受网络、磁盘IO等延迟影响。需要谨慎设置超时时间,如果设置不当,且接口之前的隔离做的不是很完善,则服务很容易被一个延迟的接口拖垮。
而异步化的方式则完全不同,通常情况下一个CPU核启动一个线程即可处理所有的请求、响应。一个请求的生命周期不再固定于一个线程,而是会分成不同的阶段交由不同的线程池处理,系统的资源能够得到更充分的利用。而且因为线程不再被某一个连接独占,一个连接所占用的系统资源也会低得多,只是一个文件描述符加上几个监听器,而在阻塞模型中,每条连接都会独占一个线程,是一个非常重的资源。对于上游服务的延迟情况,能够得到很大的缓解,因为在阻塞模型中,慢请求会独占一个线程资源,而异步化之后,因为单条连接诶所占用的资源变的非常低,因此系统可以同时处理大量的请求。
因此考虑对网关进行异步化改造,解决当前遇到的超时、延迟等问题。
技术选型
Zuul 2
Zuul 2基于Netty和RxJava实现,采用了异步非阻塞模型,本质上其实就是队列+事件驱动。在zuul 1中一个请求的完整生命周期都是在一个线程中完成的,但在zuul 2中,请求首先会经过netty server,接着会运行前置拦截器,然后通过netty客户端将请求转发给后端的服务,最后运行后置拦截器并返回响应。但是和zuul 1不同,这里的拦截器同时支持异步和同步两种模式,对于一些比较快的操作,可以直接使用同步拦截器。
异步拦截器示例:
class SampleServiceFilter extends HttpInboundFilter {
private static final Logger log = LoggerFactory.getLogger(SampleServiceFilter.class)
private final SampleService sampleService
@Inject
SampleServiceFilter(SampleService sampleService) {
this.sampleService = sampleService
}
@Override
int filterOrder() {
return 500
}
@Override
boolean shouldFilter(HttpRequestMessage msg) {
return sampleService.isHealthy()
}
@Override
Observable<HttpRequestMessage> applyAsync(HttpRequestMessage request) {
//模拟慢请求
return sampleService.makeSlowRequest().map({ response ->
log.info("Fetched sample service result: {}", response)
return request
})
}
}
这里返回的是一个Observable
,这是RxJava
中的概念,和Java8的CompletableFuture
有点像,对于方法调用者来说拿到的都是一个Observable
,而内部的实现方式可以是同步,也可以是异步,但是调用者不用关心这个东西,无论实现怎么改,方法的签名是不用变的,始终返回的都是一个Observable
。
关于响应式的概念这里就不多做介绍了,我觉得上手还是有点难度,个人更倾向于coroutine的方案。
String[] names = ...;
Observable.from(names)
.subscribe(new Action1<String>() {
@Override
public void call(String name) {
Log.d(tag, name);
}
});
Zuul 2是一个不错的选择,但是spring官方已经不打算集成zuul 2了,加上Netflix也打算把技术栈尽可能的迁移到Spring,hystrix和Eureka也都进入维护状态,不再开发新特性,zuul未来也有可能是同样的命运。
Moving forward, we plan to leverage the strong abstractions within Spring to further modularize and evolve the Netflix infrastructure. Where there is existing strong community direction — such as the upcoming Spring Cloud Load Balancer — we intend to leverage these to replace aging Netflix software. Where there is new innovation to bring — such as the new Netflix Adaptive Concurrency Limiters — we want to help contribute these back to the community.
基于Servlet3.1的异步
Servlet3.1引入了非阻塞式编程模型,支持请求的异步处理。
public void doGet(request, response) {
ServletOutputStream out = response.getOutputStream();
AsyncContext ctx = request.startAsync();
//异步写入
out.setWriteListener(new WriteListener() {
void onWritePossible() {
while (out.isReady()) {
byte[] buffer = readFromSomeSource();
if (buffer != null)
out.write(buffer); ---> Async Write!
else{
ctx.complete(); break;
}
}
}
});
}
Spring 4.x+
也增加了对非阻塞式IO的支持,例如下面的代码示例(SpringMVC5 + Tomcat 8.5+):
@GetMapping(value = "/asyncNonBlockingRequestProcessing")
public CompletableFuture<String> asyncNonBlockingRequestProcessing(){
ListenableFuture<String> listenableFuture = getRequest.execute(new AsyncCompletionHandler<String>() {
@Override
public String onCompleted(Response response) throws Exception {
logger.debug("Async Non Blocking Request processing completed");
return "Async Non blocking...";
}
});
return listenableFuture.toCompletableFuture();
}
@PostMapping
public Callable<String> processUpload(final MultipartFile file) {
return new Callable<String>() {
public String call() throws Exception {
// ...
return "someView";
}
};
}
虽然说Servlet3.1提供了对异步的支持,但是其编程模型本质上还是同步的:Filter
, Servlet
, 或者有一些方法仍然是阻塞的,比如getParameter
, getPart
等,解析请求体、写会响应本质上还是同步的,但一般来说性能损耗也不算大,网关的耗时基本上都在业务方的IO调用上。
Spring 5 Reactive
对于异步编程模型的选择,Spring5中引入了两种方式,一种是构建于Servlet 3.1之上的SpringMVC
,另一种是构建于Netty之上的Spring WebFlux
。Spring WebFlux
不同于Spring MVC
,是一个专门为异步设计的响应式框架,完全非阻塞,支持响应式编程模型,可以运行在 Netty, Undertow, 和 Servlet 3.1+容器中。
不同于SpringMVC,WebFlux的请求体、响应都支持响应式类型,可以异步的接受、写入响应,是一个完全异步化的框架。
@PostMapping("/accounts")
public void handle(@RequestBody Mono<Account> account) {
// ...
}
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public Mono<String> processSubmit(@Valid @ModelAttribute("pet") Mono<Pet> petMono) {
return petMono
.flatMap(pet -> {
// ...
})
.onErrorResume(ex -> {
// ...
});
}
另外Spring WebFlux
也提供了一个响应式、非阻塞的HTTP客户端:WebClient
. 其内部支持多种实现,默认是Reactor Netty
,也支持Jetty reactive HttpClient,当然也可以自己通过ClientHttpConnector
扩展。
Mono<Void> result = client.post()
.uri("/persons/{id}", id)
.contentType(MediaType.APPLICATION_JSON)
.body(personMono, Person.class)
.retrieve()
.bodyToMono(Void.class);
Spring Cloud Gateway
Spring Cloud Gateway是由spring官方基于Spring5.0、Spring Boot2.0、Project Reactor等技术开发的网关,目的是代替原先版本中的Spring Cloud Netfilx Zuul,目前Netfilx已经开源了Zuul2.0,但Spring没有考虑集成,而是推出了自己开发的Spring Cloud GateWay。该项目提供了一个构建在Spring生态系统之上的API网关。
特性:
基于Spring Framework 5, Project Reactor 和 Spring Boot 2.0
能够根据请求的任何属性匹配路由
支持Hystrix
支持
Spring Cloud DiscoveryClient
限流
路径重写
过滤器
@SpringBootApplication
public class DemogatewayApplication {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("path_route", r -> r.path("/get")
.uri("http://httpbin.org"))
.route("host_route", r -> r.host("*.myhost.org")
.uri("http://httpbin.org"))
.route("rewrite_route", r -> r.host("*.rewrite.org")
.filters(f -> f.rewritePath("/foo/(?<segment>.*)", "/${segment}"))
.uri("http://httpbin.org"))
.route("hystrix_route", r -> r.host("*.hystrix.org")
.filters(f -> f.hystrix(c -> c.setName("slowcmd")))
.uri("http://httpbin.org"))
.route("hystrix_fallback_route", r -> r.host("*.hystrixfallback.org")
.filters(f -> f.hystrix(c -> c.setName("slowcmd").setFallbackUri("forward:/hystrixfallback")))
.uri("http://httpbin.org"))
.route("limit_route", r -> r
.host("*.limited.org").and().path("/anything/**")
.filters(f -> f.requestRateLimiter(c -> c.setRateLimiter(redisRateLimiter())))
.uri("http://httpbin.org"))
.build();
}
}
自研
另外也可以参考Zuul2、Spring Cloud Gateway等,基于Netty、Vertx或者spring4.x提供的基于Servlet 3.1的异步机制自研,但自研成本会很高,需要从零开始开发。
对比
选型 | 优势 | 劣势 |
---|---|---|
Zuul 2 | 特性完善。重试、并发保护等 | Spring官方不打算集成,需要自己搞。后期项目的活跃度,Netflix开源的eureka、hystrix都进入了维护模式 |
Spring Boot 1.x + Spring 4.x Servlet 3.1 | 部分支持异步 | 如果目前是基于传统spring mvc的方式,相对改造成本比较小 |
Spring Boot 2 + Spring MVC | 部分支持异步 | 需要升级Spring Boot 2 |
Spring Boot 2 + Spring Web Flux | 完全异步化、异步Http客户端的WebClient | 需要升级Spring Boot 2 |
自研 | 能够更好的和业务结合 | 成本太高 |
问题
需要特别注意的一些问题:
异步化之后,整个流程都是基于事件驱动,请求处理的流程随时可能被切换断开,需要通过trace_id等机制才能把整个执行流再串联起来,给开发、调试、运维等引入了很多复杂性,比如想在IDE里面通过打断点排查问题就不是很方便了。
整个流程都是基于事件驱动,代码相对而言会变得更复杂,想梳理清楚整个工作流程会更麻烦,同步的方式只要跟着IDE一步一步点进去就可以。
ThreadLocal机制在异步化之后就不能很好的工作了。Netflix也遇到了很多ThreadLocal的问题,比如监控、traceId的传递、业务参数的传递等,这个需要特别注意。
异步的编程模式,采用回调、future还是响应式? 更激进一点可以考虑下kotlin的coroutine
总结
网关的异步化改造相对还是比较必要的,作为所有流量的入口,性能、稳定性是非常重要的一环,另外由于网关接入了内部所有的API,因此在大促时需要进行比较完善的压测,评估网关的容量,并进行扩容,但如果内部的业务比较复杂,网关接入了非常多的API,这种中心化的方案就会导致很难对网关进行比较准确的容量评估,后面可以考虑基于Service Mesh的思想,对网关进行去中心化改造,将网关的核心逻辑,比如鉴权、限流、协议转换、计费、监控、告警等都抽到sidecar中。
参考链接
https://tech.youzan.com/api-gateway-in-practice/
https://blog.wangqi.love/articles/Java/API%E7%BD%91%E5%85%B3%E6%8A%80%E6%9C%AF%E6%80%BB%E7%BB%93.html
https://blogs.oracle.com/arungupta/non-blocking-io-using-servlet-31:-scalable-applications-using-java-ee-7-totd-188
https://medium.com/@the.raj.saxena/springboot-2-performance-servlet-stack-vs-webflux-reactive-stack-528ad5e9dadc
https://blogs.oracle.com/arungupta/whats-new-in-servlet-31-java-ee-7-moving-forward
http://blog.didispace.com/api-gateway-Zuul-1-zuul-2-how-to-choose/
https://wanshi.iteye.com/blog/2410210
作者:aCoder2013
原文:https://github.com/aCoder2013/blog/issues/34
-------END-------
「架构艺术」云计算 | 微服务 | 互联网 | 软件 | App | Web
往期文章:
以上是关于多层网关已成过去,网关多合一成潮流,网关改造正当时丨Higress 正式发布 1.0 版本的主要内容,如果未能解决你的问题,请参考以下文章