高性能网关设计实践( 二 )


?
OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台 , 其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项 。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关 。OpenResty® 的目标是让你的Web服务直接跑在 Nginx 服务内部 , 充分利用 Nginx 的非阻塞 I/O 模型 , 不仅仅对 HTTP 客户端请求,甚至于对远程后端诸如 MySQL、PostgreSQL、Memcached 以及 redis 等都进行一致的高性能响应 。
?
可以简单理解为 , OpenResty = Nginx + Lua, 通过 Lua 扩展 Nginx 实现的可伸缩的 Web 平台。它利用了 Nginx 的高性能 , 又在其基础上添加了 Lua 的脚本语言来让 Nginx 也具有了动态的特性 。通过 OpenResty 中 lua-Nginx-module 模块中提供的 Lua API , 我们可以动态地控制路由、上游、SSL 证书、请求、响应等 。甚至可以在不重启 OpenResty 的前提下 , 修改业务的处理逻辑 , 并不局限于 OpenResty 提供的 Lua API 。
关于静态和动态有一个很合适的类比:如果把 Web 服务器当做是一个正在高速公路上飞驰的汽车 , Nginx 需要停车才能更换轮胎 , 更换车漆颜色 , 而 OpenResty 中可以边跑边换轮胎 , 更换车漆 , 甚至更换发动机 , 直接让普通的汽车变成超跑!
除了以上的动态性 , 还有两个特性让 OpenResty 独出一格 。
「1.详尽的文档和测试用例」
作为开源项目 , 文档和测试毫无疑问是其是否靠谱的关键 , 它的文档非常详细 , 作者把每个注意的点都写在文档上了 , 多数时候只要看文档即可 , 每一个测试案例都包含完整的 Nginx 配置和 lua 代码 。以及测试的输入数据和预期的输出数据 。
「2.同步非阻塞」
OpenResty 在诞生之初就支持了协程 , 并且基于此实现了同步非阻塞的编程模型 。
「画外音:协程(coroutine)我们可以将它看成一个用户态的线程 , 只不过这个线程是我们自己调度的 , 而且不同协程的切换不需要陷入内核态 , 效率比较高 。(一般我们说的线程是要指内核态线程 , 由内核调度 , 需要从用户空间陷入内核空间 , 相比协程 , 对性能会有不小的影响)」
啥是同步非阻塞呢 。假设有以下两个两行代码:
local res, err  = query-mysql(sql)local value, err = query-redis(key)「同步」:必须执行完查询 mysql , 才能执行下面的 redis 查询 , 如果不等 mysql 执行完成就能执行 redis 则是异步 。
「阻塞」:假设执行 sql 语句需要 1s , 如果在这 1s 内 , CPU 只能干等着不能做其它任何事 , 那就是阻塞 , 如果在 sql 执行期间可以做其他事(注意由于是同步的 , 所以不能执行以下的 redis 查询) , 则是非阻塞 。
同步关注的是语句的先后执行顺序 , 如果上一个语句必须执行完才能执行下一个语句就是同步 , 如果不是 , 就是异步 , 阻塞关注的是线程是 CPU 是否需要在 IO 期间干等着 , 如果在 IO(或其他耗时操作期间)期间可以做其他事 , 那就是非阻塞 , 不能动 , 则是阻塞 。
那么 OpenResty 的工作原理是怎样的呢 , 又是如何实现同步非阻塞的呢 。
OpenResty 原理剖析工作原理剖析由于 OpenResty 基于 Nginx 实现的 , 我们先来看看 Nginx 的工作原理
高性能网关设计实践

文章插图
 
Nginx 启动后 , 会有一个 master 进程和多个 worker 进程  ,  master 进程接受管理员的信号量(如 Nginx -s reload, -s stop)来管理 worker 进程 , master 本身并不接收client 的请求 , 主要由 worker 进程来接收请求 , 不同于 Apache 的每个请求会占用一个线程 , 且是同步IO , Nginx 是异步非阻塞的 , 每个 worker 可以同时处理的请求数只受限于内存大小 , 这里就要简单地了解一下 nginx 采用的 epoll 模型:
epoll 采用多路复用模型 , 即同一时间虽然可能会有多个请求进来 ,  但只会用一个线程去监视 , 然后哪个请求数据准备好了 , 就调用相应的线程去处理 , 就像图中所示 , 同拨开关一样 , 同一时间只有一个线程在处理 ,  epoll 是基于事件驱动模型的 , 每个请求进来注册事件并注册 callback 回调函数 , 等数据准入好了 , 就调用回调函数进行处理 , 它是异步非阻塞的 , 所以性能很高 。


推荐阅读