分享实录 | 使用 NGINX Unit 简化应用栈

原文作者:洪志道

原文链接:分享实录 | 使用 NGINX Unit 简化应用栈

转载来源:NGINX 开源社区


本文为 NGINX Sprint China 2022 年度线上大会的分享实录,点击“查看视频”免费观看大会完整视频回放。

使用容器管理应用已经成为现代应用的事实标准。容器是一种很好的工具,可以使应用具有可移植性和安全性,但同时也要付出一定的代价。在生产环境中处理容器化工作负载是复杂的,但是如果安全管理您的应用可以像调用 API 一样简单呢?

NGINX Unit 将监听 IP 和端口的网络配置与 7 种语言的应用运行时结合在一起,并且这些都与一个强大的路由引擎结合在一起,就像你的应用程序栈的 Ingress controller。阅读本文了解详情。

NGINX 创新

首先我们先开始今天的第一个议题 NGINX 的创新。我个人觉得 NGINX 能成为一个出色且成功的开源软件,是因为它的创新。

Igor 在刚开始创建项目的时候,目的就很明确,是怎么让单机服务器处理更多的用户请求,如果把 NGINX 和 Unit 比喻成棋手,相当于一个棋手在跟很多的对手在下棋。早期处理请求的方式是 Web server 服务器会为每个请求分配一个独立的进程或者线程,早期互联网的并发请求都比较低,并且大部分是可以及时响应的短链接,所以在当时是没有任何问题的,但随着短视频及信息越来越丰富,就需要一个 NGINX 这样的软件。

那么NGINX如何处理请求?NGINX 只跑在一个单独的进程里,每个进程只跑一个且它处理请求都是完全异步的。这就很像一个很强悍的棋手,在跟很多人下棋。多核的情况下, NGINX 跑在一个独立的进程里,每个工作进程都是相互独立的,他们处理所有过来的请求,所以也完全充分利用了 CPU 的处理能力,我个人觉得这些是 NGINX 的创新之处。

首先是异步事件机制,它利用了操作系统提供的事件异步机制,在NGINX 内部没有任何的阻塞,这样就能很高效地处理所有的请求。还有一个是它的多进程架构,因为现在服务器都是多核的,所以 NGINX 充分利用了 CPU 的处理能力。

最后一个是 NGINX 的配置更新,这点其实在当时是比较超前的设计,用户可以不用中断服务就能更新配置。但是这跟我们现在理解的配置的热更新、热加载又有点不一样。因为 NGINX 配置更新的时候会创建新的工作进程。如果是对那些像长连接这样的请求,比如用户访问一些视频,这样会导致新旧进程会同时存在,那些老的进程会等到所有的请求处理完才会退出,那就会让它的资源消耗更多。内存方面,就是一个比较棘手,而且是很明显的一个问题。

我们来回顾一下 NGINX 的发展历程。2004 年 NGINX 开源,2001年发布了一个标志性的版本 NGINX 1.0。而且在差不多那个时候, NGINX 成立了商业公司。到了2019年,NGINX 已经成为全球 Web server 使用率第一的开源软件,而且看这个趋势还会持续挺长时间的。

复杂的 Web 应用栈

我们接下来聊一下复杂的应用栈。随着现在互联网越来越丰富,应用在这一方面已经成为一个必不可少,而且是非常重要的一部分。我们先看一个比较典型的架构,一个多层的架构,请求从开始到结束会有三部分的参与。最前端的是浏览器或者客户端,负载均衡这些比较边缘的服务代理,先接受到请求后转发到后面的应用软件负责的部分,应用软件会根据不同的语言执行相应的代码,然后再返回到客户端。

我们先看一个比较简单的架构,很多服务会在前面挂一个负载均衡,像 NGINX 这样的负载均衡软件,它负责流量路由,又能保证高可用性,这种架构很实用又很稳定。但是由于后端应用栈非常复杂,所以有时我们会将负载均衡和服务代理给分开。最前面放一个 NGINX 负责安全方面 TLS 或者 WAF,后端挂跟应用栈相关的负责流量路由的 NGINX,以保证服务的高可用性。

现在比较主流的微服务、容器,会让整个架构变得越来越细,服务之间有时还会相互调用,这样对程序员运维的要求会越来越高,而且层面越来越大,这是容器的。但是回到刚才那个架构,其实程序员关心的代码只是整个架构的一小部分,所以我们在思考有哪些东西是可以合并的,尤其是应用端这一方面,我们相信一定有更好且更有效的方式来管理整个应用栈,这就是我们一会儿会讲到的主角—— Unit。

Unit 介绍

就是在这样的一个越来越复杂的应用栈的背景下,我们自研了 Unit ,定位它是一个通用型的 Web 应用服务器。Unit 会有 3 个核心的功能,一个是文件服务,用户可以用来做文件资源这样的服务,也可以做应用程序,这是最核心也是我们投入最大的应用程序部分。还有一个是负载均衡,可以做像 H3 那样的服务代理。

Unit 是怎么处理请求的?首先,Unit 是一个全新的架构,跟 NGINX 是没有任何关系的,所以我们在实现以及架构设计上完全是从零开始的。

先看一下进程的类型,我们设计了 4 个不同的类型,一个是 Main 主进程,这个与 NGINX master 是一样的,用来维护还有创建进程。我们刚才说 Unit 已经完全解决了配置的热加载的问题,所以我们设计了一个 Control 进程来处理所有配置,处理完会分发给一个非常核心且重要的 Router 进程,它是多线程的,所以有时候我们经常会这样去比较两个软件。

NGINX 是一个多进程的软件,而 Unit 是一个多线程的,多线程已经是一个更符合现代化软件的一个设计的模型。Router 进程,它是一个多线程的一个模型,所以每个线程都会跑一个独立的,我们把它称为类似工作线程的一个东西。它跟 NGINX 的工作进程其实是很像的,每个里面都是完全异步的。所以相当于它里面有很多个骑手,每个骑手处理了很多的请求,彼此又是相互独立的。默认情况下,线程数会跟 CPU 的内核数是一样的,用户不用去关心这些细节。

我们还有多个 Application 进程,所有的跟程序代码执行相关的,都是放在 Application 进程的,我们在进程里面做了很多原创的事情,比如所有的语言的引擎,都集成到 Application 进程里面, Application 执行完程序代码,是跟 Router 进程进行交互的,也就是这些流量请求过来,会先到 Router 进程。Router 进程通过内部会转发到 Application 进程里面,两者之间会进行交互。而且我们做了很多性能方面的优化,比如用到共享内存、用到无锁的队列,所以在延时方面 Unit 做得很好,在低延时方面还有低内存。

我们看一下 Unit 的架构,左边是客户端的,中间是 Unit,右边是可选的,可能是应用端后面的。Unit 只是一个软件,但是它能跑所有的服务,跑所有的语言,这是一个很大的创新。配置过来,会先到刚才说的 Control 进程,然后流量请求会走 Unit 的 Router 进程,会自己根据不同的配置。就是整个配置会被生效到 Router 进程,再去做相应的服务,比如文件、应用程序和服务代理。也就是我们说的 Unit 的三个核心的功能。

我们从用户的角度来看一下 Unit 设计。在设计的时候会遵守遵守一个原则,就是尽量地将 Unit 设计的又简单又灵活强大。由此抽象出几个核心的概念,可以支撑整个 Unit 的软件的运转。

左边是监听,我们可以为每个监听配置,让每个监听 pass 到后面的 upstream,也可以 pass 到 applications。同时,内部又设计了一个很强大、很容易使用的路由,路由引擎,可以 pass 到 upstreams,也可以 pass 到文件服务,也可以 pass 到 applications。所以我们内部抽象出一个 pass 的概念,这个概念是一个完全通用的,你可以通过监听、路由,路由之间其实还可以跳转,也都是走 pass,用 pass 把它抽象成一个配置的选项。

目前 Unit 已经支持了所有主流的语言,大家都知道所有的语言都有它的一些主流的框架,比如 Ruby 有 Rails 这样的流行的框架,所有的主流的框架都能跑在用例这里,如果用户已经使用了自己的原来的一套,或者现在想用 Unit 的,可以完全无缝地衔接 Unit。

所以我们为什么要使用 Unit ?其实最核心的是它会极大地简化你的应用栈的部署。如果按以前的方式,你不得不为每个语言、每个应用、每个业务可去选择不同的应用软件,但是 Unit 只要一个软件,就像是所有的事情像是被塞进了一个黑盒里面。


更多资源

想要更及时全面地获取 NGINX 相关的技术干货、互动问答、系列课程、活动资源?

请前往 NGINX 开源社区:

{{o.name}}
{{m.name}}

猜你喜欢

转载自my.oschina.net/u/5246775/blog/6362921