如何在OpenResty里实现代码热更新

栏目: IT技术 · 发布时间: 4年前

内容简介:所谓「代码热更新」,是指代码发生变化后,不用 reload 或者 graceful restart 进程就能生效。比如有一个聊天服务,连接着一百万个用户的长连接,所谓代码热更新就是在长连接不断的前提下完成代码更新。通过关闭 lua_code_cache 可以实现此目标,但那样的话性能无疑将大打折扣!实际上因为所有的 require 操作都是通过 package.loaded 来加载模块的,所以通过 package.loaded 就可以实现代码热更新,并且基本不影响性能。下面让我们做个实验来说明一下如何实现代

所谓「代码热更新」,是指代码发生变化后,不用 reload 或者 graceful restart 进程就能生效。比如有一个聊天服务,连接着一百万个用户的长连接,所谓代码热更新就是在长连接不断的前提下完成代码更新。通过关闭 lua_code_cache 可以实现此目标,但那样的话性能无疑将大打折扣!实际上因为所有的 require 操作都是通过 package.loaded 来加载模块的,所以通过 package.loaded 就可以实现代码热更新,并且基本不影响性能。

下面让我们做个实验来说明一下如何实现代码热更新的,首先设置如下配置:

lua_code_cache on;
worker_processes 1;

location /run {
    content_by_lua '
        ngx.say(require("test").run())
    ';
}

location /unload {
    allow 127.0.0.1;
    deny all;

    content_by_lua '
        package.loaded[ngx.var.arg_m] = nil
    ';
}

需要说明的是,之所以把 worker_processes 设置为 1,是因为每个 worker 进程都有一个独立的 lua vm,设置成 1 更方便测试,稍后我会说明大于 1 的时候怎么办。此外,有两个 location,其中 run 是用来运行模块的,unload 的是用来卸载模块的。

接着在 package.path 所包含的某个路径上创建 test 模块:

local _M = {}

function _M.run()
    return 1
end

return _M

逻辑很简单,就是返回一个数字。一切准备就绪后,reload 一下 ngx,让我们开始实验:

  1. 请求 http://localhost/run,显示 1
  2. 修改模块 test.lua,把 1 改成 100
  3. 请求 http://localhost/unload?m=test,卸载 package.loaded 中的 test
  4. 请求 http://localhost/run,显示 100

由此可见,在模块内容被修改后,我们没有 reload 进程,只是通过卸载 package.loaded 中对应的模块,就实现了代码热更新。

看起来实现代码热更新非常简单。打住!有例外,让我们修改一下 test 模块:

local ffi = require("ffi")

ffi.cdef[[
struct test { int v; };
]]

local _M = {}

function _M.run()
    local test = ffi.new("struct test", {1})

    return test.v
end

return _M

还是打印一个数字,只是用 ffi 实现的,让我们再来重复一下实验步骤,结果报错了:

attempt to redefine …

究其原因,是因为当我们通过 package.loaded 卸载模块的时候,如果用到了 ffi.cdef 之类的 ffi 操作,那么其中的 C 语言类型声明是无法卸载的。

好在我们可以通过条件判断来决定是否要执行 ffi.cdef 语句:

if not pcall(ffi.typeof, "struct test") then
    ffi.cdef[[
    struct test { int v; };
    ]]
end

说明:如果我们要修改原始定义的话,那么就只能 reload 进程了。

最后,让我来说一说多进程的问题,在测试过程中,我只使用了一个进程,并且通过一个特定的 location 来实现卸载 package.loaded 中指定模块的功能,但是在实际情况中, worker_processes 多半是大于 1 的,也就说有多个 worker 进程,此时,如果再使用特定 location 来操作的话,你是无法确定到底是操作在哪个 worker 上的。

比较直观的解决方案是:

  1. 把需要动态加载的代码放在一个模块文件中,并标记版本号。
  2. 暴露一个 location,允许从外部写最新的版本号到共享内存字典。
  3. 检查模块的版本号与共享内存字典中的最新版本号是否一致,如果不一致的话,则通过 package.loaded 卸载模块,然后再调用 require 重新加载模块。

如此可以解决问题,但是不爽的是每个请求都要检查版本号。看看另一个方案:

  1. 在 init_worker 设置每个 worker 都定时启动 timer,扫描自己的共享内存队列。
  2. 暴露一个 location,允许从外部写模块名字到每一个 worker 的共享内存队列。
  3. 如果 timer 发现新数据,就说明有模块变化了,然后通过 package.loaded 卸载。

完美!

如何在OpenResty里实现代码热更新


以上所述就是小编给大家介绍的《如何在OpenResty里实现代码热更新》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

计算机是怎样跑起来的

计算机是怎样跑起来的

[日] 矢泽久雄 / 胡屹 / 人民邮电出版社 / 2015-5 / 39.00元

本书倡导在计算机迅速发展、技术不断革新的今天,回归到计算机的基础知识上。通过探究计算机的本质,提升工程师对计算机的兴趣,在面对复杂的最新技术时,能够迅速掌握其要点并灵活运用。 本书以图配文,以计算机的三大原则为开端、相继介绍了计算机的结构、手工汇编、程序流程、算法、数据结构、面向对象编程、数据库、TCP/IP 网络、数据加密、XML、计算机系统开发以及SE 的相关知识。 图文并茂,通俗......一起来看看 《计算机是怎样跑起来的》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器