Nginx/OpenResty详解,Nginx Lua编程,Lua开发基础

Lua开发基础

Lua是一个可扩展的轻量级脚本语言,Lua的设计目是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。Lua的代码简洁优美,几乎在所有操作系统和平台上都可以编译和运行。

Lua脚本需要通过Lua解释器来解释执行,除了Lua官方的默认解释器外,目前使用广泛的Lua解释器叫作LuaJIT。

LuaJIT是采用C语言编写的Lua脚本解释器。LuaJIT被设计成全兼容标准Lua 5.1,因此LuaJIT代码的语法和标准Lua的语法没多大区别。LuaJIT和Lua的一个区别是,LuaJIT的运行速度比标准Lua快数十倍,可以说是一个Lua的高效率版本。

 Lua模块的定义和使用

与Java类似,实际开发的Lua代码需要进行分模块开发。Lua中的一个模块对应一个Lua脚本文件。使用require指令导入Lua模块,第一次导入模块后,所有Nginx进程全局共享模块的数据和代码,每个Worker进程需要时会得到此模块的一个副本,不需要重复导入,从而提高Lua应用的性能。接下来,演示开发一个简单的Lua模块,用来存放公有的基础对象和基础函数。

//代码清单:src/luaScript/module/common/basic.lua
--定义一个应用程序公有的Lua对象app_info
local app_info = { version = "0.10" }
--增加一个path属性,保存Nginx进程所保存的Lua模块路径,包括conf文件配置的部分路径
app_info.path = package.path;
--局部函数,取得最大值
local function max(num1, num2)
 if (num1 > num2) then
 result = num1;
 else
 result = num2;
 end
 return result;
end
--统一的模块对象
local _Module = {
 app_info = app_info;
 max = max;
}
return _Module

模块内的所有对象、数据、函数都定义成局部变量或者局部函数。然后,对于需要暴露给外部的对象或者函数,作为成员属性保存到一个统一的Lua局部对象(如_Module)中,通过返回这个统一的局部对象将内部的成员对象或者方法暴露出去,从而实现Lua的模块化封装。

 Lua模块的使用

接下来,创建一个Lua脚本
src/luaScript/module/demo/helloworld.lua来调用前面定义的这个基础模块src/luaScript/module/common/basic.lua文件。

helloworld.lua的代码如下:

//代码清单:src/luaScript/module/demo/helloworld.lua
---启动调试
local mobdebug = require("luaScript.initial.mobdebug");
mobdebug.start();
--导入自定义的模块
local basic = require("luaScript.module.common.basic ");
--使用模块的成员属性
ngx.say("Lua path is: " .. basic.app_info.path);
ngx.say("<br>" );
--使用模块的成员方法
ngx.say("max 1 and 11 is: ".. basic.max(1,11) );

在使用require内置函数导入Lua模块时,对于多级目录下的模块,使用require("目录1.目录2.模块名")的形式进行加载,源目录之间的“/”斜杠分隔符改成“.”点号分隔符。这一点和Java的包名的分隔符类似。

--导入自定义的模块
local basic = require("luaScript.module.common.basic");

Lua文件查找时,首先会在Nginx的当前工作目录查找,如果没有

找到,就会在Nginx的Lua包路径lua_package_path和lua_package_cpath声明的位置查找。整个Lua文件的查找过程和Java的.class文件查找的过程很类似。需要注意的是,Lua包路径需要在nginx.conf配置文件中进行配置:

 lua_package_path " E:/tool/ZeroBraneStudio-1.80/lualibs/?/?.lua;;";
 lua_package_cpath " E:/tool/ZeroBraneStudio-1.80/bin/clibs/?.dll;;";

这里有两个包路径配置项:lua_package_path用于配置Lua文件的包路径;lua_package_cpath用于配置C语言模块文件的包路径。在Linux系统上,C语言模块文件的类型是“.so”;在Windows平台上,C语言模块文件的类型是“.dll”。

Lua包路径如果需要配置多个路径,那么路径之间使用分号“;”分隔。末尾的两个分号“;;”表示加上Nginx默认的Lua包搜索路径,其中包含Nginx的安装目录下的lua目录。

//路径清单:一个默认的Lua文件搜索路径输出案例
./site/lualib/?.ljbc;
./site/lualib/?/init.ljbc;
./lualib/?.ljbc;
./lualib/?/init.ljbc;
./site/lualib/?.lua;
./site/lualib/?/init.lua;
./lualib/?.lua;
./lualib/?/init.lua;
.\?.lua;
E:\tool\openresty-1.13.6.2-win32\lualib\?.lua;
E:\tool\openresty-1.13.6.2-win32\lua\?.lua;
E:\tool\openresty-1.13.6.2-win32\lua\?\init.lua;;

在OpenResty的lualib下已经提供了大量第三方开发库,如CJSON、Redis客户端、MySQL客户端等,并且这些Lua模块已经包含到默认的搜索路径中。OpenResty的lualib下的模块可以直接在Lua文件中通过require方式导入:

导入
操作模块--导入redis操作模块
local redis = require("resty.redis")
--导入cjson操作模块
local cjson = require("cjson")

 Lua的数据类型

Lua中大致有8种数据类型,具体如表8-1所示。

Nginx/OpenResty详解,Nginx Lua编程,Lua开发基础

表8-1 8种数据类型

Lua是弱类型语言,和JavaScript等脚本语言类似,变量没有固定的数据类型,每个变量可以包含任意类型的值。使用内置的type(…)方法可以获取该变量的数据类型。下面是一段简单的类型输出演示程序。

--输出数据类型
local function showDataType()
 local i;
 basic.log("字符串的类型", type("hello world"))
 basic.log("方法的类型", type(showDataTy
<think>我们正在处理一个OpenResty编译错误,错误信息是“multiple definition of `ngx_stream_lua_read_line'”。这通常发生在链接阶段,表明同一个函数被定义了多次。我们需要分析可能的原因和解决方案。 根据错误信息,函数`ngx_stream_lua_read_line`被多次定义。在C语言中,这通常是因为同一个函数在多个源文件中被定义,或者同一个目标文件被多次链接。 ### 可能的原因: 1. **重复的源文件**:可能在编译过程中,同一个源文件被包含了两次,或者两个不同的源文件定义了同一个函数。 2. **链接顺序问题**:在链接过程中,可能有两个不同的目标文件都包含了这个函数的定义。 3. **头文件中的函数定义**:如果这个函数的定义被放在了一个头文件中,而这个头文件又被多个源文件包含,那么每个包含该头文件的源文件都会有一个函数定义,导致多重定义。 ### 解决方案: 1. **检查函数定义位置**:首先,我们需要确定`ngx_stream_lua_read_line`函数是在哪里定义的。这个函数应该是OpenResty中与stream模块相关的Lua模块的一部分。查看OpenResty的源代码,找到这个函数的定义位置。 - 通常,函数应该在`.c`文件中定义,并且只在头文件中声明(使用`extern`)。 2. **检查头文件**:如果这个函数的定义在头文件中,我们需要将其移动到源文件中,并在头文件中改为声明。 3. **检查编译配置**:查看OpenResty的编译配置,确保没有重复包含某个模块或源文件。 4. **更新OpenResty**:如果这是OpenResty的一个已知bug,尝试更新到最新版本。 5. **检查第三方模块**:如果使用了第三方模块,可能是第三方模块与OpenResty的模块冲突。 ### 具体步骤: 由于我们无法直接访问OpenResty的源代码,但我们可以根据经验给出一般性建议。 1. **定位函数定义**:在OpenResty的源代码中搜索`ngx_stream_lua_read_line`,确定它定义在哪个文件中。假设我们找到它在`ngx_stream_lua_module.c`文件中。 2. **检查重复**:查看是否有其他文件也定义了同名的函数。 3. **检查头文件**:检查包含该函数声明的头文件(可能是`ngx_stream_lua_module.h`)中是否不小心包含了函数定义(即函数体)。如果是,将函数体移到`.c`文件中,头文件中只保留函数声明,例如: ```c // 在头文件中,只声明 ssize_t ngx_stream_lua_read_line(ngx_connection_t *c, ngx_str_t *line, ngx_uint_t set_flags, ngx_uint_t clear_flags, size_t *size); ``` 4. **使用`static`关键字**:如果这个函数只在同一个源文件中使用,可以将其定义为`static`,这样就不会被其他文件看到。 5. **检查Makefile**:确保在编译过程中没有重复包含同一个源文件。 ### 实际案例: 在OpenResty的官方GitHub仓库中,曾经有过类似的问题报告。例如,在某个版本中,由于头文件中包含了函数定义,导致链接错误。解决方案是将函数定义移动到源文件中。 ### 参考做法: 1. 克隆最新的OpenResty源代码: ```bash git clone https://github.com/openresty/openresty.git cd openresty ``` 2. 检查是否有已知修复:查看提交历史,搜索`ngx_stream_lua_read_line`相关的修复。 3. 如果问题依然存在,尝试手动修改: - 打开定义`ngx_stream_lua_read_line`的头文件(假设为`ngx_stream_lua_module.h`),如果其中有函数体,则删除函数体,只保留声明。 - 在对应的源文件(如`ngx_stream_lua_module.c`)中实现该函数。 4. 重新编译。 ### 注意事项: - 在修改代码前,备份原始文件。 - 如果使用了第三方模块,尝试在没有第三方模块的情况下编译,以确定是否是模块冲突。 ### 引用: 在OpenResty的文档和社区讨论中,类似问题通常通过上述方法解决。例如,在Lua-Nginx-Module的某个版本中,就曾因为头文件中定义函数而导致多重定义问题,后来通过将函数定义移到源文件中修复[^1]。 如果上述方法不能解决问题,建议提供更详细的错误日志和编译环境信息,以便进一步排查。 §§相关问题§§ 1. OpenResty编译时遇到链接错误,如何定位问题? 2. 如何避免在C语言项目中出现多重定义错误? 3. OpenResty的stream模块有什么作用?</think>### OpenResty编译错误:`multiple definition of ngx_stream_lua_read_line` 分析及解决 此错误发生在链接阶段,表明同一个函数`ngx_stream_lua_read_line`在多个目标文件中被重复定义,导致链接器无法确定使用哪个定义。以下是根本原因和解决方案: #### 根本原因 1. **头文件函数定义泄漏** `ngx_stream_lua_read_line`函数可能被错误地定义在头文件(`.h`)而非源文件(`.c`)中。当多个源文件包含此头文件时,每个源文件都会生成一份函数定义,导致链接冲突[^1]。 2. **缺少`static`限定符** 若该函数仅在单个源文件内使用,但未添加`static`关键字(如`static ssize_t ngx_stream_lua_read_line(...)`),编译器会将其视为全局符号,可能与其他模块的同名函数冲突。 3. **模块版本冲突** 同时加载了多个包含相同函数的模块(如不同版本的`ngx_stream_lua_module`),常见于自定义模块与官方模块混用。 --- #### 解决方案 ##### 方法1:检查并修复头文件(推荐) 1. 定位函数定义文件: ```bash grep -r "ngx_stream_lua_read_line" /path/to/openresty/bundle/ ``` 2. 若函数定义在头文件(如`ngx_stream_lua_module.h`)中: - **将函数体移动到源文件**(如`ngx_stream_lua_module.c`) - 头文件中仅保留声明: ```c // ngx_stream_lua_module.h ssize_t ngx_stream_lua_read_line(ngx_connection_t *c, ngx_str_t *line, ngx_uint_t set_flags, ngx_uint_t clear_flags); ``` 3. 重新编译: ```bash make clean ./configure make ``` ##### 方法2:添加静态限定符 若函数仅用于单文件,在定义前添加`static`: ```c // ngx_stream_lua_module.c static ssize_t ngx_stream_lua_read_line(...) { ... } ``` ##### 方法3:检查模块冲突 1. 检查`configure`命令: ```bash ./configure --help | grep stream ``` 2. 确保未重复加载模块: - 移除冗余的`--add-module`参数 - 更新到最新版OpenResty(可能已修复此问题): ```bash git clone https://github.com/openresty/openresty.git cd openresty && make ``` --- #### 预防措施 1. **头文件规范** 头文件只放声明(`extern`/函数原型),定义始终放在`.c`文件[^2]。 2. **符号可见性** 非共享函数使用`static`限制作用域。 3. **版本管理** 使用官方发布的稳定版本,避免混合不同来源的模块。 > 若问题仍存在,请提供以下信息以便进一步排查: > - OpenResty版本(`openresty -v`) > - 完整的`./configure`参数 > - 错误日志全文
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值