APISIX Lua 编码风格指南

缩进

使用 4 个空格作为缩进的标记:

  1. --No
  2. if a then
  3. ngx.say("hello")
  4. end
  1. --Yes
  2. if a then
  3. ngx.say("hello")
  4. end

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

空格

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

  1. --No
  2. local i=1
  3. local s = "apisix"
  1. --Yes
  2. local i = 1
  3. local s = "apisix"

空行

不少开发者会在行尾增加一个分号:

  1. --No
  2. if a then
  3. ngx.say("hello");
  4. end;

增加分号会让 Lua 代码显得非常丑陋,也是没有必要的。

另外,不要为了显得“简洁”节省代码行数,而把多行代码变为一行。这样会在定位错误的时候不知道到底哪一段代码出了问题:

  1. --No
  2. if a then ngx.say("hello") end
  1. --Yes
  2. if a then
  3. ngx.say("hello")
  4. end

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

  1. --No
  2. local function foo()
  3. end
  4. local function bar()
  5. end
  1. --Yes
  2. local function foo()
  3. end
  4. local function bar()
  5. end

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

  1. --No
  2. if a == 1 then
  3. foo()
  4. elseif a== 2 then
  5. bar()
  6. elseif a == 3 then
  7. run()
  8. else
  9. error()
  10. end
  1. --Yes
  2. if a == 1 then
  3. foo()
  4. elseif a== 2 then
  5. bar()
  6. elseif a == 3 then
  7. run()
  8. else
  9. error()
  10. end

每行最大长度

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

  1. --No
  2. return limit_conn_new("plugin-limit-conn", conf.conn, conf.burst, conf.default_conn_delay)
  1. --Yes
  2. return limit_conn_new("plugin-limit-conn", conf.conn, conf.burst,
  3. conf.default_conn_delay)

在换行对齐的时候,要体现出上下两行的对应关系。

就上面示例而言,第二行函数的参数,要在第一行左括号的右边。

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

  1. --No
  2. return limit_conn_new("plugin-limit-conn" .. "plugin-limit-conn" ..
  3. "plugin-limit-conn")
  1. --Yes
  2. return limit_conn_new("plugin-limit-conn" .. "plugin-limit-conn"
  3. .. "plugin-limit-conn")
  1. --Yes
  2. return "param1", "plugin-limit-conn"
  3. .. "plugin-limit-conn")

变量

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

  1. --No
  2. i = 1
  3. s = "apisix"
  1. --Yes
  2. local i = 1
  3. local s = "apisix"

变量命名使用 snake_case(蛇形命名法) 风格:

  1. --No
  2. local IndexArr = 1
  3. local str_Name = "apisix"
  1. --Yes
  2. local index_arr = 1
  3. local str_name = "apisix"

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

  1. --No
  2. local max_int = 65535
  3. local server_name = "apisix"
  1. --Yes
  2. local MAX_INT = 65535
  3. local SERVER_NAME = "apisix"

表格/数组

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

  1. --No
  2. local t = {}
  3. for i = 1, 100 do
  4. t[i] = i
  5. end
  1. --Yes
  2. local new_tab = require "table.new"
  3. local t = new_tab(100, 0)
  4. for i = 1, 100 do
  5. t[i] = i
  6. end

不要在数组中使用 nil

  1. --No
  2. local t = {1, 2, nil, 3}

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

  1. --Yes
  2. local t = {1, 2, ngx.null, 3}

字符串

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

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

函数

函数的命名也同样遵循 snake_case(蛇形命名法):

  1. --No
  2. local function testNginx()
  3. end
  1. --Yes
  2. local function test_nginx()
  3. end

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

  1. --No
  2. local function check(age, name)
  3. local ret = true
  4. if age < 20 then
  5. ret = false
  6. end
  7. if name == "a" then
  8. ret = false
  9. end
  10. -- do something else
  11. return ret
  12. end
  1. --Yes
  2. local function check(age, name)
  3. if age < 20 then
  4. return false
  5. end
  6. if name == "a" then
  7. return false
  8. end
  9. -- do something else
  10. return true
  11. end

模块

所有 require 的库都要 local 化:

  1. ```lua
  2. --No
  3. local function foo()
  4. local ok, err = ngx.timer.at(delay, handler)
  5. end
  1. --Yes
  2. local timer_at = ngx.timer.at
  3. local function foo()
  4. local ok, err = timer_at(delay, handler)
  5. end

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

  1. --No
  2. local core = require("apisix.core")
  3. local timer_at = ngx.timer.at
  4. local function foo()
  5. local ok, err = timer_at(delay, handler)
  6. end
  1. --Yes
  2. local ngx = ngx
  3. local require = require
  4. local core = require("apisix.core")
  5. local timer_at = ngx.timer.at
  6. local function foo()
  7. local ok, err = timer_at(delay, handler)
  8. end

错误处理

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

  1. --No
  2. local sock = ngx.socket.tcp()
  3. local ok = sock:connect("www.google.com", 80)
  4. ngx.say("successfully connected to google!")
  1. --Yes
  2. local sock = ngx.socket.tcp()
  3. local ok, err = sock:connect("www.google.com", 80)
  4. if not ok then
  5. ngx.say("failed to connect to google: ", err)
  6. return
  7. end
  8. ngx.say("successfully connected to google!")

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

  1. --No
  2. local function foo()
  3. local ok, err = func()
  4. if not ok then
  5. return false
  6. end
  7. return true
  8. end
  1. --No
  2. local function foo()
  3. local ok, err = func()
  4. if not ok then
  5. return false, {msg = err}
  6. end
  7. return true
  8. end
  1. --Yes
  2. local function foo()
  3. local ok, err = func()
  4. if not ok then
  5. return false, "failed to call func(): " .. err
  6. end
  7. return true
  8. end