OpenResty 概要及原理科普

2024-09-02 09:48
文章标签 原理 openresty 概要 科普

本文主要是介绍OpenResty 概要及原理科普,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

点击上方“朱小厮的博客”,选择“设为星标”

后台回复"高效Java"领取《Effective Java第三版》

欢迎跳转到本文的原文链接:https://honeypps.com/architect/introduction-of-openresty/

OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。OpenResty 官网地址:https://openresty.org/cn/。

OpenResty主要包含两方面的技术:

  • Nginx: 一个免费的、开源的、高性能的 HTTP 服务器和反向代理,也是一个电子邮件(IMAP/POP3/SMTP)代理服务器。有关Nginx的介绍,可以查看这篇《Nginx架构原理科普》。

  • Lua: 一种轻量、小巧、可移植、快速的脚本语言;LuaJIT即时编译器会将频繁执行的Lua代码编译成本地机器码交给CPU直接执行,执行效率更高,OpenResty会默认启用LuaJIT。

历史

OpenResty 最早是雅虎中国的一个公司项目,起步于 2007 年 10 月。当时兴起了 OpenAPI 的热潮,用于满足各种 Web Service 的需求,就诞生了 OpenResty。在公司领导的支持下,最早的 OpenResty 实现从一开始就开源了。

最初的定位是服务于公司外的开发者,像其他的 OpenAPI 那样,但后来越来越多地是为雅虎中国的搜索产品提供内部服务。这是第一代的 OpenResty,当时的想法是,提供一套抽象的 Web Service,能够让用户利用这些 Web Service 构造出新的符合他们具体业务需求的 Web Service 出来,所以有些“meta web service”的意味,包括数据模型、查询、安全策略都可以通过这种 meta web service 来表达和配置。同时这种 Web Service 也有意保持 REST 风格。与这种概念相对应的是纯 AJAX 的 web 应用,即 web 应用几乎都使用客户端 JavaScript 来编写,然后完全由  Web Service 让 web 应用“活”起来。用户把 .html/ .js/ .css/ .jpg 等静态文件下载到 web browser 中,然后 js 开始运行,跨域请求雅虎提供的经过站长定制过的 Web Service ,然后应用就可以运行起来。不过随着后来的发展,公司外的用户毕竟还是少数,于是应用的重点是为公司内部的其他团队提供 Web Service e,比如雅虎中国的全能搜索产品,及其外围的一些产品。从那以后,开发的重点便放在了性能优化上面。

章亦春在加入淘宝数据部门的量子团队之后,决定对 OpenResty 进行重新设计和彻底重写,并把应用重点放在支持像量子统计这样的 web 产品上面,所以量子统计 3.0 开始也几乎完全是 Web Service 驱动的纯 AJAX 应用。这是第二代的 OpenResty,一般称之为 ngx_openresty,以便和第一代基于 Perl 和 Haskell 实现的 OpenResty 加以区别。章亦春和他的同事王晓哲一起设计了第二代的 OpenResty。在王晓哲的提议下,选择基于 Nginx 和 Lua 进行开发。

为什么要取 OpenResty 这个名字呢?OpenResty 最早是顺应 OpenAPI 的潮流做的,所以 Open 取自“开放”之意,而 Resty 便是 REST 风格的意思。虽然后来也可以基于 ngx_openresty 实现任何形式的 Web Service 或者传统的 web 应用。也就是说 Nginx 不再是一个简单的静态网页服务器,也不再是一个简单的反向代理了。第二代的 OpenResty 致力于通过一系列 Nginx 模块,把 Nginx 扩展为全功能的 web 应用服务器。(摘自:OpenResty作者章亦春访谈实录[1])

Lua 与 LuaJIT

要了解OpenResty,那么Lua语言是必须先要了解的,它是 OpenResty 中使用的编程语言。Lua 是一个小巧的脚本语言。是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组,由 Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo 所组成并于 1993 年开发。Lua在葡萄牙语里代表美丽的月亮。

Lua 在设计之初,就把自己定位为一个简单、轻量、可嵌入的胶水语言,没有走大而全的路线。虽然你平常工作中可能没有直接编写 Lua 代码,但 Lua 的使用其实非常广泛。很多的网游,比如魔兽世界,都会采用 Lua 来编写插件;而键值数据库 Redis 则是内置了 Lua 来控制逻辑。另一方面,虽然 Lua 自身的库比较简单,但它可以方便地调用 C 库,大量成熟的 C 代码都可以为其所用。比如在 OpenResty 中,很多时候都需要你调用 NGINX 和 OpenSSL 的 C 函数,而这都得益于 Lua 和 LuaJIT 这种方便调用 C 库的能力。

Lua 非常高效,它运行得比许多其它脚本(如 Perl、Python、Ruby)都快,这点在第三方的独立测评中得到了证实。尽管如此,仍然会有人不满足,他们总觉得“嗯,还不够快!”。LuaJIT 就是一个为了再榨出一些速度的尝试,它利用即时编译(Just-in Time)技术把 Lua 代码编译成本地机器码后交由 CPU 直接执行。LuaJIT 2 的测评报告表明,在数值运算、循环与函数调用、协程切换、字符串操作等许多方面它的加速效果都很显著。凭借着 FFI 特性,LuaJIT 2 在那些需要频繁地调用外部 C/C++ 代码的场景,也要比标准 Lua 解释器快很多。目前 LuaJIT 2 已经支持包括 i386、x86_64、ARM、PowerPC 以及 MIPS 等多种不同的体系结构。

LuaJIT 是采用 C 和汇编语言编写的 Lua 解释器与即时编译器。LuaJIT 被设计成全兼容标准的 Lua 5.1 语言,同时可选地支持 Lua 5.2 和 Lua 5.3 中的一些不破坏向后兼容性的有用特性。因此,标准 Lua 语言的代码可以不加修改地运行在 LuaJIT 之上。LuaJIT 和标准 Lua 解释器的一大区别是,LuaJIT 的执行速度,即使是其汇编编写的 Lua 解释器,也要比标准 Lua 5.1 解释器快很多,可以说是一个高效的 Lua 实现。另一个区别是,LuaJIT 支持比标准 Lua 5.1 语言更多的基本原语和特性,因此功能上也要更加强大。

对于 Lua 语法的学习和使用可以参考这里[2]。

使用示例

为了能够让大家对 OpenResty 有个大致的使用印象,这里引用一个官网的示例[3]来做讲解。在安装完OpenResty之后(安装过程略过),创建工作目录:

mkdir ~/work
cd ~/work
mkdir logs/ conf/

在新创建的conf/ 目录下创建一个nginx.conf配置文件,其内容如下:

pid logs/nginx.pid;
events{worker_connections 1024;
}http{server {listen 8080;location / {content_by_lua 'ngx.say("hello, world")';}}
}

启动OpenResty服务:

openresty -p `pwd` -c conf/nginx.conf

如果没有报错的话,OpenResty服务已经启动成功了。你可以打开浏览器,或者使用curl命令来查看返回的结果:

hidden:~ hidden$ curl -i localhost:8080
HTTP/1.1 200 OK
Server: openresty/1.15.8.3
Date: Wed, 22 Apr 2020 03:57:56 GMT
Content-Type: text/plain
Transfer-Encoding: chunked
Connection: keep-alivehello, world

这里只是简单的打印一个“hello, world”,真实使用场景下,肯定会包含大堆的代码,如果这些代码全部包含在nginx.conf配置文章中,那么可阅读性和可维护性是会大大降低的。所以,我们要进一步地将Lua代码抽离出来。

我们在 ~/work 目录下再创建一个 lua/ 的目录,然后再在 lua/ 目录下创建一个 hello.lua 文件,文件内的内容为:ngx.say("hello, world")。对应的目录结构如下:

hidden:work hidden$ tree
.
├── conf
│   ├── nginx.conf
├── logs
│   └── nginx.pid
├── lua└── hello.lua

之后修改 nginx.conf 的配置,把其中的 content_by_lua 改为 content_by_lua_file:

pid logs/nginx.pid;
events{worker_connections 1024;
}http{server {listen 8080;location / {content_by_lua_file lua/hello.lua;}}
}

最后,重启OpenResty的服务就可以了。

做 OpenResty 开发,lua-nginx-module 的文档[4] 是你的首选,Lua 语言的库都是同步阻塞的,用的时候要三思。也就是说,尽量使用 ngx_lua提供的api,而不是使用 Lua 本身的。例如 ngx.sleep()与 lua提供的sleep,前者不会造成阻塞,后者是会阻塞的

原理剖析

OpenResty的工作原理如下图所示。

如《Nginx架构原理科普》介绍,Nginx 服务器启动后,产生一个 Master 进程(Master Process),Master 进程执行一系列工作后产生一个或者多个 Worker 进程(Worker Processes)。其中,Master 进程用于接收来自外界的信号,并向各 Worker 进程发送信号,同时监控 Worker 进程的工作状态。当 Worker 进程退出后(异常情况下),Master 进程也会自动重新启动新的 Worker 进程。Worker 进程则是外部请求真正的处理者。

多个 Worker 进程之间是对等的,他们同等竞争来自客户端的请求,各进程互相之间是独立的。一个请求,只可能在一个 Worker 进程中处理,一个 Worker 进程不可能处理其它进程的请求。Worker 进程的个数是可以设置的,一般我们会设置与机器 CPU 核数一致。同时,Nginx 为了更好的利用多核特性,具有 CPU 绑定选项,我们可以将某一个进程绑定在某一个核上,这样就不会因为进程的切换带来cache的失效(CPU affinity)。所有的进程的都是单线程(即只有一个主线程)的,进程之间通信主要是通过共享内存机制实现的。

OpenResty本质上是将 LuaJIT 的虚拟机嵌入到 Nginx 的管理进程和工作进程中,同一个进程内的所有协程都会共享这个虚拟机,并在虚拟机中执行Lua代码。在性能上,OpenResty接近或超过 Nginx 的C模块,而且开发效率更高。下面深入介绍一下OpenResty的原理。

Lua协程

协程是不被操作系统内核所管理的,而完全由程序控制(也就是用户态执行),这样带来的好处就是性能得到了极大地提升。进程和线程切换要经过用户态到内核态再到用户态的过程,而协程的切换可以直接在用户态完成,不需要陷入内核态,切换效率高,降低资源消耗。Lua协程与线程类似,拥有独立的堆栈、独立的局部变量、独立的指令指针,同时又与其他协同程序共享全局变量和其他大部分东西。

cosocoket

OpenResty中的核心技术cosocket将 Lua 协程和 Nginx 的事件机制结合在一起,最终实现了非阻塞网络IO。不仅和HTTP客户端之间的网络通信是非阻塞的,与MySQL、Memcached以及Redis等众多后端之间的网络通信也是非阻塞的。在OpenResty中调用一个cosocket相关的网络函数,内部关键实现如图所示:

从图中可以看出,用户的Lua脚本每触发一个网络操作,都会有协程的yield和resume。当遇到网络 I/O 时,Lua协程会交出控制权(yield),把网络事件注册到 Nginx 监听列表中,并把运行权限交给 Nginx 。当有 Nginx 注册网络事件到达触发条件时,便唤醒(resume)对应的协程继续处理。这样就可以实现全异步的 Nginx 机制,不会影响 Nginx 的高并发处理性能。

多阶段处理

基于 Nginx 使用的多模块设计思想,Nginx 将HTTP请求的处理过程划分为多个阶段。这样可以使一个HTTP请求的处理过程由很多模块参与处理,每个模块只专注于一个独立而简单的功能处理,可以使性能更好、更稳定,同时拥有更好的扩展性。

OpenResty在HTTP处理阶段基础上分别在Rewrite/Access阶段、Content阶段、Log阶段注册了自己的handler,加上系统初始阶段master的两个阶段,共11个阶段为Lua脚本提供处理介入的能力。下图描述了OpenResty可以使用的主要阶段:

(图片来源于 lua-nginx-module 文档)

OpenResty将我们编写的Lua代码挂载到不同阶段进行处理,每个阶段分工明确,代码独立。

  • init_by_lua*:Master进程加载 Nginx 配置文件时运行,一般用来注册全局变量或者预加载Lua模块。

  • init_worker_by_lua*:每个worker进程启动时执行,通常用于定时拉取配置/数据或者进行后端服务的健康检查。

  • set_by_lua*:变量初始化。

  • rewrite_by_lua*:可以实现复杂的转发、重定向逻辑。

  • access_by_lua*:IP准入、接口权限等情况集中处理。

  • content_by_lua*:内容处理器,接收请求处理并输出响应。

  • header_filter_by_lua*:响应头部或者cookie处理。

  • body_filter_by_lua*:对响应数据进行过滤,如截断或者替换。

  • log_by_lua*:会话完成后,本地异步完成日志记录。

资料推荐

  1. OpenResty最佳实践https://legacy.gitbook.com/book/moonbingbing/openresty-best-practices/details

  2. OpenResty官网:https://openresty.org/cn/

  3. lua-nginx-module文档: https://github.com/openresty/lua-nginx-module#version

  4. 极客时间- OpenResty从入门到实践

参考资料

[1]

OpenResty作者章亦春访谈实录: https://www.oschina.net/question/28_60461

[2]

这里: https://moonbingbing.gitbooks.io/openresty-best-practices/content/lua/main.html

[3]

官网的示例: https://openresty.org/cn/getting-started.html

[4]

lua-nginx-module 的文档: https://github.com/openresty/lua-nginx-module#version

欢迎跳转到本文的原文链接:https://honeypps.com/architect/introduction-of-openresty/

想知道更多?描下面的二维码关注我

后台回复”加群“获取公众号专属群聊入口

【原创系列 | 精彩推荐】

  • Paxos、Raft不是一致性算法嘛?

  • 越说越迷糊的CAP

  • 分布式事务科普——初识篇

  • 分布式事务科普——终结篇

  • 面试官居然问我Raft为什么会叫做Raft!

  • 面试官给我挖坑:URI中的//有什么用

  • 面试官给我挖坑:a[i][j]和a[j][i]有什么区别?

  • 面试官给我挖坑:单机并发TCP连接数到底有多少?

  • 网关Zuul科普

  • 网关Spring Cloud Gateway科普

  • Nginx架构原理科普

最后一天(4/23)!当当全场自营图书5折,用优惠码:TASEMU(长按复制,只有少量了),实付满200(原价400)再减30,相当于170=400,四折多一点。使用渠道:当当小程序或当当APP。

朕已阅 

这篇关于OpenResty 概要及原理科普的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1129625

相关文章

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

hdu4407(容斥原理)

题意:给一串数字1,2,......n,两个操作:1、修改第k个数字,2、查询区间[l,r]中与n互质的数之和。 解题思路:咱一看,像线段树,但是如果用线段树做,那么每个区间一定要记录所有的素因子,这样会超内存。然后我就做不来了。后来看了题解,原来是用容斥原理来做的。还记得这道题目吗?求区间[1,r]中与p互质的数的个数,如果不会的话就先去做那题吧。现在这题是求区间[l,r]中与n互质的数的和

hdu4407容斥原理

题意: 有一个元素为 1~n 的数列{An},有2种操作(1000次): 1、求某段区间 [a,b] 中与 p 互质的数的和。 2、将数列中某个位置元素的值改变。 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOException;import java.io.Inpu

hdu4059容斥原理

求1-n中与n互质的数的4次方之和 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.PrintWrit

寻迹模块TCRT5000的应用原理和功能实现(基于STM32)

目录 概述 1 认识TCRT5000 1.1 模块介绍 1.2 电气特性 2 系统应用 2.1 系统架构 2.2 STM32Cube创建工程 3 功能实现 3.1 代码实现 3.2 源代码文件 4 功能测试 4.1 检测黑线状态 4.2 未检测黑线状态 概述 本文主要介绍TCRT5000模块的使用原理,包括该模块的硬件实现方式,电路实现原理,还使用STM32类

TL-Tomcat中长连接的底层源码原理实现

长连接:浏览器告诉tomcat不要将请求关掉。  如果不是长连接,tomcat响应后会告诉浏览器把这个连接关掉。    tomcat中有一个缓冲区  如果发送大批量数据后 又不处理  那么会堆积缓冲区 后面的请求会越来越慢。

PHP原理之内存管理中难懂的几个点

PHP的内存管理, 分为俩大部分, 第一部分是PHP自身的内存管理, 这部分主要的内容就是引用计数, 写时复制, 等等面向应用的层面的管理. 而第二部分就是今天我要介绍的, zend_alloc中描写的关于PHP自身的内存管理, 包括它是如何管理可用内存, 如何分配内存等. 另外, 为什么要写这个呢, 因为之前并没有任何资料来介绍PHP内存管理中使用的策略, 数据结构, 或者算法. 而在我们

Smarty模板执行原理

为了实现程序的业务逻辑和内容表现页面的分离从而提高开发速度,php 引入了模板引擎的概念,php 模板引擎里面最流行的可以说是smarty了,smarty因其功能强大而且速度快而被广大php web开发者所认可。本文将记录一下smarty模板引擎的工作执行原理,算是加深一下理解。 其实所有的模板引擎的工作原理是差不多的,无非就是在php程序里面用正则匹配将模板里面的标签替换为php代码从而将两者

Restful API 原理以及实现

先说说API 再说啥是RESRFUL API之前,咱先说说啥是API吧。API大家应该都知道吧,简称接口嘛。随着现在移动互联网的火爆,手机软件,也就是APP几乎快爆棚了。几乎任何一个网站或者应用都会出一款iOS或者Android APP,相比网页版的体验,APP确实各方面性能要好很多。 那么现在问题来了。比如QQ空间网站,如果我想获取一个用户发的说说列表。 QQ空间网站里面需要这个功能。

laravel框架实现redis分布式集群原理

在app/config/database.php中配置如下: 'redis' => array('cluster' => true,'default' => array('host' => '172.21.107.247','port' => 6379,),'redis1' => array('host' => '172.21.107.248','port' => 6379,),) 其中cl