lua-resty-core 是什么?

lua-resty-core 是 OpenResty 组件的一部分。它由两部分组成,一部分是 resty.core.*,提供了对 lua-nginx-module Lua 接口的替换实现;另一部分是 ngx.*,OpenResty 新的接口一般都会放到这里。
跟其他 lua-resty 开头的库一样,lua-resty-core 也是用 Lua 实现的。说到这有人可能会问,既然 lua-nginx-module 已经有了一套 API,为什么还要在 lua-resty-core 里面重新实现一次,而且还是用 Lua?

需要澄清下,lua-nginx-module 提供的 API,并不是完全意义上的用 C 实现的。准确来说,是通过 C 实现,并通过 Lua CFunction 暴露出来的。而 lua-resty-core 提供的 API,也不是表面看上去那样用 Lua 实现的。准确来说,是通过在 lua-nginx-module 里面的以 *_lua_ffi_* 形式命名的 C 函数实现的,并在 lua-resty-core 里面通过 LuaJIT FFI 暴露出来的。所以其实两者都是 C 实现。两者的比较,应该是 Lua CFunction 和 LuaJIT FFI 的比较。

那 LuaJIT FFI 有着怎样的优点,值得在已有一套的基于 Lua CFunction 的接口的前提下,去费大力气重新实现一遍?

FFI + JIT

LuaJIT FFI 的实现深深地根植于解释器自身。如果当前 LuaJIT 正处于 JIT 模式,它会在 FFI 调用时优化 Lua 领域和 C 领域间传参和返回的过程,因此采用 FFI 要比直接调用 Lua CFunction 要快。至于能快多少,则取决于调用时两个领域间数据交换频繁情况。

举个例子,

init_by_lua_block {-- 注释下面一行来禁用 lua-resty-corerequire 'resty.core'
}location /foo {content_by_lua_block {local s = ("test"):rep(256)local start = ngx.now()for _ = 1, 1e6 dongx.md5(s)endngx.update_time()ngx.say(ngx.now() - start)}
}

在启用了 lua-resty-core 的情况下(走 FFI 路径),用时是

¥ curl localhost:8888/foo
2.6159999370575

禁用 lua-resty-core 后(走 CFunction 路径),用时是

¥ curl localhost:8888/foo
2.664999961853

两者并无明显区别。

不过换个需要跟 C 领域频繁交互的调用,

local s = ("test"):rep(256)
local start = ngx.now()
for _ = 1, 1e8 dongx.ctx.test = slocal r = ngx.ctx.test
end
ngx.update_time()
ngx.say(ngx.now() - start)

启用了 lua-resty-core,用时

¥ curl localhost:8888/foo
1.800999879837

禁用后用时

¥ curl localhost:8888/foo
38.345999956131

两者便有天壤之别。

ngx.ctx 一样,会收益于 FFI + JIT 的接口,还有 ngx.shared.dictngx.re 这样两类。(当然对它们的加成相对没有那么显著)

前面在提到 FFI 优化的时候,我特意强调了“当前 LuaJIT 正处于 JIT 模式”。如果当前 LuaJIT 处于解析器模式,很不幸,FFI 调用会比 CFunction 的形式慢。

在继续之前,先跳出 FFI 的话题,介绍下 LuaJIT 的 JIT 原理。

LuaJIT 是 tracing JIT Compiler。它的 JIT 是基于分支(循环或者函数)的。对于每个 tracing 的分支,它会维护一个计数器。一旦某个分支足够热,LuaJIT 会把该分支编译掉,并用编译掉的结果替换原来的代码。这要求一点:整个分支都需要是可被编译的。如果分支中有不能编译的语句,LuaJIT 会中断 tracing,该分支也就一直没法被 JIT 掉。这种不能被编译的语句,在 LuaJIT 里面叫 NYI。可以在 http://wiki.luajit.org/NYI 查看当前的 NYI 列表。

查看 JIT trace 结果很容易,仅需在 init_by_lua_block 里添加下面两行:

local v = require "jit.v"
v.on("/tmp/dump")
require "resty.core" -- 确保 lua-resty-core 是启用的

运行之后就能在 /tmp/dump 里查看 trace 情况了。在我们的例子里,结果只有一行:

[TRACE   1 content_by_lua(nginx.conf:21):4 loop]

它表示 content_by_lua 第 4 行有一个循环,能够被完整地 trace 掉。

如果想了解更详细的情况,可以改用下面两行:

local dump = require "jit.dump"
dump.on(nil, "/tmp/dump")

这时候它会记录更详细的内容,包括 trace 的过程、IR 和 mcode 的生成情况。当 LuaJIT 中断 tracing 时,你可以凭 dump 下来的内容找出它是在哪里中断的。

回归正题。让我们找个 NYI 语句,插入到循环中,比如下面这样:

local t = {}
for _ = 1, 1e6 dongx.md5(s)next(t)
end

重新跑下,用时

¥ curl localhost:8888/foo
2.9719998836517

比调用 Lua CFunction 时要慢一些。

欲抑先扬,欲扬先抑。即使解释器模式下 FFI 会明显地慢,但有些时候还是比 CFunction 快一些。比如前面的 ngx.ctx 这个例子,在解释器模式下,它的用时是:

¥ curl localhost:8888/foo
19.00200009346

慢得要命,但还是 CFunction 版本的两倍。

如果担心项目支持的 NYI 语句太多,启用 lua-resty-core 会导致性能不升反降,那么我插一句:Lua CFunction 调用就是一种 NYI 语句,而 FFI 调用是可以 JIT 的。也即是说,启用 lua-resty-core 会减少项目中一类 NYI 语句的存在。这算是切换到 lua-resty-core 的另一个理由了。

lua-resty-core 寄托着 OpenResty 的未来

你可能会觉得,JIT 啊、NYI 啊什么的离我太远了,我们的项目不需要什么性能上的优化,所以也无需引入 lua-resty-core。

OK,即使不考虑性能,你也应该引入 lua-resty-core。春哥(OpenResty 的作者)曾经公开说过,有计划淘汰掉现有的一套 Lua CFunction 接口。所以迟早你也会用上 lua-resty-core 所暴露的接口。这是其一。

其二,OpenResty 目前新的功能开发,都是放到 lua-resty-core 上的。毕竟旧的接口要淘汰了嘛。对 FFI 的偏好并不仅仅体现在新功能开发上。如果改用 FFI 能解决 CFunction 接口的 bug,OpenResty 开发者会认为这个问题已经解决了。(参见 BUG Report 严重(特别是使用了lua-resty-lock库的服务,有一定概率workers死锁,可重现))

最后,同样的方法,来自 lua-resty-core 的版本除了性能外,还会有其他优势。举个例子,因为内部实现上的差异,lua-resty-core 中的 ngx.re 可以用在 init_by_lua* 阶段,而原来的 Lua CFunction 版本不支持这么用。

为什么你应该在 OpenResty 项目中使用 lua-resty-core相关推荐

  1. 在C++项目中引入Lua(AlphaGo使用的方案)

    最近大火的AlphaGo,其中的deepmind已经开源,可以到github中下载https://github.com/deepmind/lab·,网上还有一个基于Python开源AlphaGo,那个 ...

  2. 如何在多个项目中分离Asp.Net Core Mvc的Controller和Areas

    前言 软件系统中总是希望做到松耦合,项目的组织形式也是一样,本篇文章将介绍在ASP.NET CORE MVC中怎么样将Controller与主网站项目进行分离,并且对Areas进行支持. 实践 1.新 ...

  3. Spring Redis中使用Lua脚本实现高并发原子操作

    1. 前言 在上一文中我对 Lua 语言的一些简单的语法及其在 Redis 中的操作进行了介绍,但是在 Java 开发中我们还需要进一步的学习才能使这种技术落地.今天就结合Spring Data Re ...

  4. redis中使用lua脚本

    一.概述 1.什么是lua脚本 Lua是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放. 其设计目的就是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能.因为广泛的应用于:游戏开 ...

  5. 在OR项目中使用火焰图

    火焰图介绍 看到openresty里用火焰图觉得挺有意思的,又装逼又有用.它可以快速的定位到程序性能的瓶颈,生成程序的调用栈并且计算调用栈中每部分的 CPU 消耗,能够比较容易的找到CPU占用高.内存 ...

  6. 网关 Apache APISIX 在 360 基础运维平台项目中的实践

    女主宣言 今天小编为大家分享一篇关于Apache APISIX的文章,文章从开发者的角度讲述了 Apache APISIX 网关在 360 基础运维平台的落地实践,希望能对大家有所帮助. PS:丰富的 ...

  7. canvas java 上传截图_在Vue项目中使用html2canvas生成页面截图并上传

    使用方法 项目中引入 npm install html2canvas html代码 //html代码 js代码 // 引入html2canvas import html2canvas from 'ht ...

  8. android studio 自动提示jni代码,如何将JNI(C/C++本机代码)添加到现有的Android Studio项目中...

    从现有项目中执行以下步骤: 1.修改build.gradle(模块应用程序)看起来像这样(很多变化!): apply plugin: 'com.android.model.application' m ...

  9. android使用webview上传文件,Android项目中如何在webview页面中上传文件

    Android项目中如何在webview页面中上传文件 发布时间:2020-11-26 15:56:27 来源:亿速云 阅读:68 作者:Leah 本篇文章为大家展示了Android项目中如何在web ...

最新文章

  1. 以人为本的机器学习:谷歌人工智能产品设计概述 By 机器之心2017年7月17日 12:13 取代了手动编程,机器学习(ML)是一种帮助计算机发现数据中的模式和关系的科学。对于创建个人的和动态的经历
  2. linux线程(互斥锁、条件)
  3. [原]排错实战——拯救加载调试符号失败的IDA
  4. 我的Go+语言初体验——(1)超详细安装教程
  5. win10共享打印错误0x0000006_Win7打印机无法共享提示错误代码0x000006d9的解决方法...
  6. vue 表单 select option
  7. winscp怎么更改linux权限,Linux下,WinSCP普通用户登录sftp后切换到root权限 教程
  8. C语言之避免编译警告:unused用法(七)
  9. dial tcp 10.96.0.1:443: i/o timeout
  10. Win2019 ServerManager.exe 0xc0000135 应用程序错误
  11. 安全牛:安全与业务不存在平衡 证明价值是关键
  12. 摄像头、麦克风、耳麦免费在线检测(各种外设的在线检测网站)
  13. 杂记-Macbook Pro M1芯片能玩深度学习吗?
  14. 大连软件知名公司最新职位
  15. vim插件之pathogen,NERDTree,Command-T,Powerline
  16. 【全国大学生电子设计大赛】2021-04-26
  17. vi ~/.bashrc如何保存退出
  18. 江苏计算机学三本大学,2021年江苏三本大学最新排名及录取分数线
  19. 小程序多图上传云存储PHP
  20. mysql索引 实验报告_索引实验报告

热门文章

  1. html传值 location.search取
  2. Win7家庭版启用Administrator账户
  3. 初识NodeJS,一个基于GoogleV8引擎的Javascript运行环境
  4. 第十三节:使用Lombok简化你的代码
  5. linux特殊权限SUID,SGID和SBIT的介绍
  6. Jenkins 2.x版本的节点配置选项更新
  7. codeforces731E Funny Game(DP)
  8. metaspolit教程
  9. LeetCode - 48. Rotate Image
  10. android viewpager切换无法显示fragment问题