谈一谈API网关和应用网关

因为最近一直在做网关相关的事情,所以谈一谈我对网关的一些理解。从网关的职责和定位上来说在企业内部网关分为两类,一类是 API 网关,一类是应用网关。为什么会分成类那?主要是两者关注的侧重点不同。

api gateway.jpg

从流量上来说:API 网关主要用来处理南北流量,应用网关主要用来处理网格内部东西向流量。

从功能上来说:API 网关和应用网关会有重叠的部分。比如,鉴权、限流、熔断、路径重写、健康检查、多协议等。但是,API 网关和业务完全无关,应用网会渗透一些业务处理,比如服务的聚合和编排、服务发现等。

从部署方式上来说: API 网关和 LVS 一样都会部署在性能比较好的物理机上(当然有些大厂可能部署在云上了),属于公司级应用,应用网关根据部门部门业务的不同,可以选择部署在云上也可以部署在虚拟机上,属于部门级应用。

从技术选型上来说: API 网关的性能是第一指标,一般选择会选择 Kong、Apisix 等基于 OpenResty+Lua 的高性能网关(得益于Ngnix 基于 C++ 的高性能非阻塞网络IO模型),应用网关一般会结合自身业务的技术栈进行选择,比如 SpringCloud Gateway、Zuul等。当然这并不是绝对的,如果你非常熟悉 Kong,用它来做应用网关也不是不可以。

例举一些开源的网关项目:

  1. Kong
  2. Apisix
  3. Envoy
  4. Traefik
  5. SpringCloud Gateway
  6. Zuul / Zuul2

接下来我们主要来探讨一下应用网关,在网格内部应用网关主要聚焦以下几点功能(和 API 网关不同的)

  1. 动态路由
  2. 服务发现
  3. 服务聚合/编排
  4. 可观测性

如果你使用的是 Sping 技术栈,采用 SpringCloud Gateway 和 Zuul 可以轻易的重用现有的类库,比如集成你的注册中心、使用 Hystrix、resilience4j完成熔断限流功能等,快速完成一个生产级别可用的应用网关,如果引入一个新的复杂的技术栈成本将会直线上升。根据使用场景的不同,性能有的时候并非第一指标,但是通常情况下我们很容易陷入性能误区。

那么 SpringCloud Gateway 和 Zuul(指的是 Zuul 不是 Zuul2) 又有什么区别那?最大的区别就是,SpringCloud Gateway 采用的是响应式架构,而 Zuul 采用的是阻塞式架构。SpringCloud Gateway 基于 Reactor Netty 构建、Zuul基于 Servelt 构建。

阻塞式架构的问题是什么那?下面引用《RxJava 响应式编程-解决C10k问题》 一节的解释来回答,如下

阻塞式架构每一个请求都需要一个线程来处理,如果有10000个线程,那么将会面临如下情况。

  • 为了存储栈空间,消耗数个千兆字节的RAM。
  • 给垃圾收集机制带来巨大的压力,不过栈空间是不能进行垃圾收集的(大量的GC根和存活对象)。-
  • 浪费大量的CPU时间只是用于切换核心以运行各种线程(上下文切换)。

在有些场景中,经典的线程-Socket(thread-per-Socket)模型能够很好地满足要求,事实上,直到今天它在很多应用程序上都运行得非常好。但是,在达到一定级别的并发之后,线程的数量就会变得非常危险。由单个商用服务器处理1000个并发连接的情况并不罕见,在长期存活的TCP/IP连接场景中更是如此,比如带有Keep-Alive头信息的HTTP、服务器发送事件(server-sent event)和WebSocket。但是,不管线程正在进行计算还是等待数据的到达,每个线程都会占据一些内存(栈空间)。

在实现扩展性方面,有两种相互独立的方式:水平扩展和垂直扩展。为了处理更多的并发连接,我们可以部署更多的服务器,每个服务器管理负载的一个子集。这需要一个前端的负载均衡器,但是这样的方式也没有解决最初的C10k问题,即仅用一台服务器处理负载。而另一方面,垂直扩展意味着要购买更大更强的服务器。但是,由于阻塞式I/O,与未充分利用的CPU相比,需要与之不相称的内存占用。 即便大型的企业服务器能够处理数十万个并发连接(价格非常高昂),但是它却远远不能解决C10M的问题,也就是1000万个并发连接。这个数字并非巧合,数年之前,在一台典型的服务器上,一个经过精心设计的Java应用程序就达到了如此惊人的水准。

响式驱动架构又什么优势那?在阻塞式的处理模型中,每个请求对应一个线程显然无法进行扩展。响式驱动架构仅用少量线程就能管理大量客户端连接的方式。这种方式具有如下优点:

  • 减少内存消耗,减少线程上下文切换次数。
  • 更好的利用 CPU 和 CPU 缓存利用率。
  • 在单个节点上极大地提升可扩展性。

通过上述的讨论,我们很容易认为由于线程上下文的切换的减少和 CPU 缓存利用的提高,网关的性能会有一个量级的提高,但是事实真的如此吗?

Netfix 通过将核心业务逻辑放到阻塞式架构和非阻塞式架构中,在生产环境中运行了几个月后得出一个结论,系统受 CPU 限制越多,异步架构提升的效率就越少。

其实这也是符合道理的,因为响应式编程旨在通过异步的方式更好的利益 CPU,如果我们的系统本身就会处理大量的指标计算、加解密、响应压缩。从容量和 CPU 的角度来看,阻塞和非阻塞上本质上是等效的,所以每个节点的吞吐量级应该和原来差不多。

如果我们的系统请求量很大(大量的IO),但是响应很小,并且不需要加密。那么这个时候我们将同步更改为异步大约能够提升 25% 的吞吐量,同时 CPU 利用率降低 25%。根据 Netfix的生产实践,网关的工作越少我们从异步中收获的效率提升就越高。

猜你喜欢

转载自juejin.im/post/7036208306129469448