前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >OpenResty中Lua编码的最佳实践与规范

OpenResty中Lua编码的最佳实践与规范

作者头像
Tinywan
发布2024-03-11 13:42:40
1210
发布2024-03-11 13:42:40
举报
文章被收录于专栏:开源技术小栈开源技术小栈

OpenResty

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

OpenResty® 通过汇聚各种设计精良的 Nginx 模块(主要由 OpenResty 团队自主开发),从而将 Nginx 有效地变成一个强大的通用 Web 应用平台。这样,Web 开发人员和系统工程师可以使用 Lua 脚本语言调动 Nginx 支持的各种 C 以及 Lua 模块,快速构造出足以胜任 10K 乃至 1000K 以上单机并发连接的高性能 Web 应用系统。

Lua简介

Lua 以其简洁优雅的设计和卓越的性能,在全球编程语言家族中独树一帜。它是一门轻量级、可嵌入式的脚本语言,设计之初便以高效、灵活和易于扩展为目标。Lua名字来源于葡萄牙语中的“月亮”,寓意其小巧却蕴含强大能量。

Lua语法清晰简洁,学习曲线平缓,适合快速开发和原型验证,尤其在游戏开发、网络编程、配置文件解析等领域拥有广泛的应用。同时,Lua的跨平台特性使得它能够在Windows、Linux、Mac OS等多种操作系统上自由运行。

Lua 编码规范

缩进

在 OpenResty 中使用 4 个空格作为缩进的标记,虽然 Lua 并没有这样的语法要求。

代码语言:javascript
复制
--No
if a then
ngx.say("hello Tinywan")
end

--yes
if a then
    ngx.say("hello Tinywan")
end

你可以在使用的编辑器中,把 tab 改为 4 个空格,来简化操作。

空格

在操作符的两边,都需要用一个空格来做分隔:

代码语言:javascript
复制
--No
local i=1
local s    =    "Tinywan"

--Yes
local i = 1
local s = "Tinywan"

空行

不少开发者会把其他语言的开发习惯带到 OpenResty 中来,比如在行尾增加一个分号。

代码语言:javascript
复制
--No
if a then
    ngx.say("hello Tinywan");
end;

增加分号会让 Lua 代码显得非常丑陋,也是没有必要的。另外,不要为了节省代码的行数,后者为了显得“简洁”,而把多行代码变为一行。这样会在定位错误的时候不知道到底那一段代码出了问题:

代码语言:javascript
复制
--No
if a then ngx.say("hello Tinywan") end

--yes
if a then
    ngx.say("hello Tinywan")
end

函数之间需要用两个空行来做分隔:

代码语言:javascript
复制
--No
local function foo()
end
local function bar()
end

--Yes
local function foo()
end


local function bar()
end

如果有多个 if elseif 的分支,它们之间需要一个空行来做分隔:

代码语言:javascript
复制
--No
if a == 1 then
    foo()
elseif a== 2 then
    bar()
elseif a == 3 then
    run()
else
    error()
end

--Yes
if a == 1 then
    foo()

elseif a== 2 then
    bar()

elseif a == 3 then
    run()

else
    error()
end

每行最大长度

每行不能超过 80 个字符,超过的话,需要换行并对齐:

代码语言:javascript
复制
--No
return limit_conn_new("plugin-limit-conn", conf.conn, conf.burst, conf.default_conn_delay)

--Yes
return limit_conn_new("plugin-limit-conn", conf.conn, conf.burst,
                      conf.default_conn_delay)

在换行对齐的时候,要体现出上下两行的对应关系。就上面的示例而言,第二行函数的参数,要在第一行左括号的右边。

如果是字符串拼接的对齐,需要把 .. 放到下一行中:

代码语言:javascript
复制
--No
return limit_conn_new("plugin-limit-conn" ..  "plugin-limit-conn" ..
                      "plugin-limit-conn")
--Yes
return limit_conn_new("plugin-limit-conn" .. "plugin-limit-conn"
                      .. "plugin-limit-conn")

变量

应该永远使用局部变量,不要使用全局变量:

代码语言:javascript
复制
--No
i = 1
s = "Tinywan"

--Yes
local i = 1
local s = "Tinywan"

变量命名使用 snake_case 风格:

代码语言:javascript
复制
--No
local IndexArr = 1
local str_Name = "Tinywan"

--Yes
local index_arr = 1
local str_name = "Tinywan"

对于常量要使用全部大写:

代码语言:javascript
复制
--No
local max_int = 65535
local server_name = "Tinywan"

--Yes
local MAX_INT = 65535
local SERVER_NAME = "Tinywan"

数组

使用 table.new 来预先分配数组:

代码语言:javascript
复制
--No
local t = {}
for i = 1, 100 do
    t[i] = i
end

--Yes
local new_tab = require "table.new"
local t = new_tab(100, 0)
for i = 1, 100 do
    t[i] = i
end

不要在数组中使用 nil:

代码语言:javascript
复制
--No
local t = {1, 2, nil, 3}

如果一定要使用空值,请用 ngx.null 来表示:

代码语言:javascript
复制
--No
local t = {1, 2, ngx.null, 3}

字符串

不要在热代码路径上拼接字符串:

代码语言:javascript
复制
--No
local s = ""
for i = 1, 100000 do
    s = s .. "a"
end

--Yes
local t = {}
for i = 1, 100000 do
    t[i] = "a"
end
local s = table.concat(t, "")

函数

函数的命名也同样遵循 snake_case:

代码语言:javascript
复制
--No
local function testNginx()
end

--Yes
local function test_nginx()
end

函数应该尽可能早的返回:

代码语言:javascript
复制
--No
local function check(age, name)
    local ret = true
    if age < 20 then
        ret = false
    end

    if name == "a" then
        ret = false
    end
    -- do something else
    return ret
end

--Yes
local function check(age, name)
    if age < 20 then
        return false
    end

    if name == "a" then
        return false
    end
    -- do something else
    return true
end

模块

所有 require 的库都要 local 化:

代码语言:javascript
复制
--No
local function foo()
    local ok, err = ngx.timer.at(delay, handler)
end

--Yes
local timer_at = ngx.timer.at

local function foo()
    local ok, err = timer_at(delay, handler)
end

为了风格的统一,requirengx 也需要 local 化:

代码语言:javascript
复制

--No
local core = require("apisix.core")
local timer_at = ngx.timer.at

local function foo()
    local ok, err = timer_at(delay, handler)
end

--Yes
local ngx = ngx
local require = require
local core = require("apisix.core")
local timer_at = ngx.timer.at

local function foo()
    local ok, err = timer_at(delay, handler)
end

错误处理

对于有错误信息返回的函数,必须对错误信息进行判断和处理:

代码语言:javascript
复制
--No
local sock = ngx.socket.tcp()
local ok = sock:connect("www.google.com", 80)
ngx.say("successfully connected to google!")

--Yes
local sock = ngx.socket.tcp()
local ok, err = sock:connect("www.google.com", 80)
if not ok then
    ngx.say("failed to connect to google: ", err)
    return
end
ngx.say("successfully connected to google!")

自己编写的函数,错误信息要作为第二个参数,用字符串的格式返回:

代码语言:javascript
复制
--No
local function foo()
    local ok, err = func()
    if not ok then
        return false
    end
    return true
end

--No
local function foo()
    local ok, err = func()
    if not ok then
        return false, {msg = err}
    end
    return true
end

--Yes
local function foo()
    local ok, err = func()
    if not ok then
        return false, "failed to call func(): " .. err
    end
    return true
end
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2024-03-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开源技术小栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • OpenResty
  • Lua简介
  • Lua 编码规范
    • 缩进
      • 空格
        • 空行
          • 每行最大长度
            • 变量
              • 数组
                • 字符串
                  • 函数
                    • 模块
                      • 错误处理
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档