是什么
OpenResty并不是一个全新的web服务器,而是基于nginx。它利用了nginx模块化、可扩展的特性,开发了一系列的增强模块,并把它打包整合,形成了一个一站式的web开发平台。
虽然 OpenResty的核心是nginx,但是它又超越了nginx,关键在于其中的ngx_lua模块,把lua语言嵌入了nginx,可以用脚本的方式操作nginx内部的进程、多路复用、阶段式处理等各种关键
- OpenResty的工作语言是Lua(一种脚本语言,脚本语言是不需要编译的,随写随运行),它小巧灵活、执行效率高,支持“代码热加载”
- OpenResty把Lua自身的协程和nginx的事件机制完美结合在一起,优雅的实现了同步非阻塞编程范式,能够轻松开发出高性能的web应用
目前OpenResty有两个分支:开源免费的OpenResty和闭源、商业的OpenResty+
同步非阻塞
OpenResty的核心编程范式是同步非阻塞,使用协程,不需要异步回调函数
“同步非阻塞”本质上还是一种多路复用:
- epoll是操作系统级别的多路复用,运行在内核空间
- OpenResty的同步非阻塞是基于Lua内建的协程,是应用程序级别的多路复用,运行在用户空间,所以它的资源消耗更少
OpenResty里每一段Lua程序都有协程来调度运行。和Linux的epoll一样,每当可能发生阻塞的时候协程就会立即切换出去,执行其他的程序。这样单个处理流程就是阻塞的,但是整个OpenResty却是非阻塞的,多个程序都复用在一个Lua虚拟机里运行。
下面的代码是一个简单的例子,读取POST发送的body数据,然后再发回给客户端:
但即使因为网络原因没有收到或者发不出去,OpenResty也不会在这里“干等着”,而是做个“记号”,把等待的这段CPU时间用来处理其他的请求,等网络可读或者可写的时候再“回来”接着运行。
假设收发数据的等待时间是10ms,而真正CPU处理的时间是0.1ms,那么OpenResty就可以在这10ms里同时处理100个请求,而不是把这100个请求阻塞排队,用1000ms来处理
除了“同步非阻塞”,OpenResty还选用了LuaJIT作为Lua语言的“运行时(runtime)”,进一步“挖潜增效”
LuaJIT是一个高效的Lua虚拟机,支持JIT(Just In Time)技术,可以把Lua代码即时编译成“本地机器码”,这样就消除了脚本语言解释运行的劣势,让Lua脚本跑的跟原生C代码一样快
另外,LuaJIT还为Lua语言添加了一些特别的增强,比如二进制位运算符bit、内存优化库table、还有FFI(foreign function interface),让Lua直接调用底层C函数,比原生的压栈调用快得多。
阶段式处理
OpenRest也使用“阶段式处理”的工作模式,但是因为在阶段里面执行的都是Lua代码,所以非常灵活,配合redis等外部数据库能够实现各种动态配置。
和Nginx一样,OpenResty也使用“流水线”来处理HTTP请求,底层的运行基础是nginx的“阶段式处理”,但是它又有自己的特色。
Nginx的“流水线”是由一个个C模块组成的,只能在静态文件中配置,开发困难,配置麻烦。而OpenResty的“流水线”是由一个个的Lua脚本组成的,不仅可以从磁盘上加载,也可以从redis、MySQL里加载,而且编写、调试的过程非常方便。
如下为OpenResty各阶段
比起Nginx,OpenResty的阶段更注重对HTTP请求响应报文的加工和处理。
OpenResty里有几个阶段与Nginx是相同的,比如rewrite、access、content、filter,这些都是标准的HTTP处理。
OpenResty还有两个不同于Nginx的特殊阶段:
- 一个是init阶段,它又分为
master init
和worker init
,在master进程和worker进程启动的时候运行。这个阶段还没有开始提供服务,所以慢一点也没有关系,可以调用一些阻塞的接口初始化服务器,比如读取磁盘、MySQL、加载黑白名单或者数据模型,然后放进共享内存里供运行使用 - 一个是SSL阶段,可以在TLS握手时动态加载证书,或者发送OCSP Staping
- Nginx可以根据服务器名称指示来选择证书实现HTTPS虚拟主机,但是静态配置很不灵活,要编写很多雷同的配置快。虽然后来Nginx增加了变量支持,但它每次握手都要读磁盘、效率很低
- OpenResty里可以使用指令
ssl_certificate_by_lua
,编写Lua脚本,读取SNI名字后,直接从共享内存或者redis中获取证书。不仅没有读盘阻塞,而且证书也是完全动态可配置的,无需修改配置文件就能够轻松支持大量的HTTPS虚拟主机