# 簡單API Server框架
我們實現一個最最簡單的數學計算:加、減、乘、除,給大家演示如何搭建簡單的 API Server。
按照我們前面幾章的寫法,我們先來看看加法、減法示例代碼:
~~~
worker_processes 1; #nginx worker 數量
error_log logs/error.log; #指定錯誤日志文件路徑
events {
worker_connections 1024;
}
http {
server {
listen 6699;
# 加法
location /addition {
content_by_lua_block {
local args = ngx.req.get_uri_args()
ngx.say(args.a + args.b)
}
}
# 減法
location /subtraction {
content_by_lua_block {
local args = ngx.req.get_uri_args()
ngx.say(args.a - args.b)
}
}
# 乘法
location /multiplication {
content_by_lua_block {
local args = ngx.req.get_uri_args()
ngx.say(args.a * args.b)
}
}
# 除法
location /division {
content_by_lua_block {
local args = ngx.req.get_uri_args()
ngx.say(args.a / args.b)
}
}
}
}
~~~
代碼寫多了我們一眼就可以看出來,這么簡單的加減乘除,居然寫了這么長,而且我們還要對每個 API 都寫一個 location ,這明顯是讓人不爽的。如果我們的某個API接口比較冗長,這樣寫豈不是直接會撐爆 nginx.conf 文件。要知道即使你喜歡這樣寫,nginx的配置文件對字符串最大長度有限制,不能超過4K。而且代碼中如果需要出現單引號等字符,都需要進行轉義,這些都是比較痛苦的。
- 首先就是把這些 location 合并為一個;
- 其次是這些接口的實現放到獨立文件中,保持 nginx 配置文件的簡潔;
基于這兩點要求,我們可以改成下面的版本,看上去有那么幾分模樣:
> nginx.conf 內容:
~~~
worker_processes 1; #nginx worker 數量
error_log logs/error.log; #指定錯誤日志文件路徑
events {
worker_connections 1024;
}
http {
# 設置默認 lua 搜索路徑,添加 lua 路徑
lua_package_path 'lua/?.lua;/blah/?.lua;;';
# 對于開發研究,我們可以對代碼 cache 進行關閉,這樣我們不必每次都重新加載 nginx。
lua_code_cache off;
server {
listen 6699;
# 在代碼路徑中使用nginx變量
# 注意: nginx var 的變量一定要謹慎,否則將會帶來非常大的風險
location ~ ^/api/([-_a-zA-Z0-9/]+) {
# 準入階段完成參數驗證
access_by_lua_file lua/access_check.lua;
#內容生成階段
content_by_lua_file lua/$1.lua;
}
}
}
~~~
> 其他文件內容:
~~~
--========== {$prefix}/lua/addition.lua
local args = ngx.req.get_uri_args()
ngx.say(args.a + args.b)
--========== {$prefix}/lua/subtraction.lua
local args = ngx.req.get_uri_args()
ngx.say(args.a - args.b)
--========== {$prefix}/lua/multiplication.lua
local args = ngx.req.get_uri_args()
ngx.say(args.a * args.b)
--========== {$prefix}/lua/division.lua
local args = ngx.req.get_uri_args()
ngx.say(args.a / args.b)
~~~
既然我們對外提供的是 API Server,作為一個服務端程序員,怎么可以容忍輸入參數不檢查呢?萬一對方送過來的不是數字或者為空,這些都要過濾掉嘛。參數檢查過濾的方法是統一,在這幾個 API 中如何共享這個方法呢?這時候就需要 Lua 中的模塊來完成了。
- 使用統一的公共模塊,完成參數驗證;
- 對本示例,參數驗證的方式方法是統一的,我們可以把它們集中在一處完成;
> nginx.conf 內容:
~~~
worker_processes 1; #nginx worker 數量
error_log logs/error.log; #指定錯誤日志文件路徑
events {
worker_connections 1024;
}
http {
server {
listen 6699;
# 在代碼路徑中使用nginx變量
# 注意: nginx var 的變量一定要謹慎,否則將會帶來非常大的風險
location ~ ^/api/([-_a-zA-Z0-9/]+) {
access_by_lua_file /path/to/lua/app/root/access_check.lua;
content_by_lua_file /path/to/lua/app/root/$1.lua;
}
}
}
~~~
> 新增文件內容:
~~~
--========== {$prefix}/lua/comm/param.lua
local _M = {}
-- 對輸入參數逐個進行校驗,只要有一個不是數字類型,則返回 false
function _M.is_number(n, ...)
local arg = {...}
if n ~= #arg then
return false
end
local num
for _,v in ipairs(arg) do
num = tonumber(v)
if nil == num then
return false
end
end
return true
end
return _M
--========== {$prefix}/lua/access_check.lua
local param= require("comm.param")
local args = ngx.req.get_uri_args()
if not param.is_number(args.a, args.b) then
ngx.exit(ngx.HTTP_BAD_REQUEST)
return
end
~~~
看看curl測試結果吧:
~~~
$ nginx curl '127.0.0.1:6699/api/addition?a=1'
400 Bad Request
400 Bad Request
openresty/1.9.3.1
$ nginx curl '127.0.0.1:6699/api/addition?a=1&b=3'
4
~~~
基本是按照我們預期執行的。參數不全、錯誤時,會提示400錯誤。正常處理,可以返回預期結果。
我們來整體看一下目前的目錄關系:
~~~
.
├── conf
│ ├── nginx.conf
├── logs
│ ├── error.log
│ └── nginx.pid
├── lua
│ ├── access_check.lua
│ ├── addition.lua
│ ├── subtraction.lua
│ ├── multiplication.lua
│ ├── division.lua
│ └── comm
│ └── param.lua
└── sbin
└── nginx
~~~
怎么樣,有點 magic 的味道不?其實你的接口越是規范,有固定規律可尋,那么 OpenResty 就總是很容易能找到適合你的位置。當然這里你也可以把 `access_check.lua` 內容分別復制到加、減、乘、除實現的四個 Lua 文件中,肯定也是能用的。這里只是為了給大家提供更多的玩法,偶爾需要的時候我們可以有更多的選擇。
本章目的是搭建一個簡單API Server,記住這絕對不是終極版本。這里面還有很多需要我們進一步去考慮的地方,但是作為最基本的框架已經有了。
- 序
- Lua 入門
- Lua簡介
- Lua環境搭建
- 基礎數據類型
- 表達式
- 控制結構
- if/else
- while
- repeat
- for
- break,return
- Lua函數
- 函數的定義
- 函數的參數
- 函數的返回值
- 函數回調
- 模塊
- String庫
- Table庫
- 日期時間函數
- 數學庫函數
- 文件操作
- 元表
- 面向對象編程
- FFI
- 下標從1開始
- 局部變量
- 判斷數組大小
- 非空判斷
- 正則表達式
- 不用標準庫
- 虛變量
- 函數在調用代碼前定義
- 抵制使用module()函數來定義Lua模塊
- 點號與冒號操作符的區別
- Nginx
- Nginx 新手起步
- location 匹配規則
- if 是邪惡的
- 靜態文件服務
- 日志服務
- 反向代理
- 負載均衡
- 陷阱和常見錯誤
- 環境搭建
- Windows平臺
- CentOS平臺
- Ubuntu平臺
- Mac OS X平臺
- Hello World
- 簡單API Server框架
- 獲取Nginx內置綁定變量
- LuaRestyRedisLibrary
- select+set_keepalive組合操作引起的數據讀寫錯誤
- redis接口的二次封裝(簡化建連、拆連等細節)
- redis接口的二次封裝(發布訂閱)
- pipeline壓縮請求數量
- script壓縮復雜請求
- LuaCjsonLibrary
- json解析的異常捕獲
- 稀疏數組
- 空table編碼為array還是object
- 跨平臺的庫選擇
- PostgresNginxModule
- 調用方式簡介
- 不支持事務
- 超時
- 健康監測
- SQL注入
- LuaNginxModule
- 執行階段概念
- 正確的記錄日志
- 熱裝載代碼
- 阻塞操作
- 緩存
- sleep
- 定時任務
- 禁止某些終端訪問
- 請求返回后繼續執行
- 調試
- 調用其他C函數動態庫
- 我的lua代碼需要調優么
- 變量的共享范圍
- 動態限速
- shared.dict 非隊列性質
- 如何添加自己的lua api
- 正確使用長鏈接
- 如何引用第三方resty庫
- 典型應用場景
- LuaRestyDNSLibrary
- 使用動態DNS來完成HTTP請求
- 緩存失效風暴
- 測試
- 單元測試
- API測試
- 性能測試
- 持續集成
- 灰度發布
- Web 服務
- API的設計
- 數據合法性檢測
- 協議無痛升級
- 代碼規范
- 連接池
- C10K編程
- TIME_WAIT問題
- 與Docker使用的網絡瓶頸
- 火焰圖
- 什么時候使用
- 顯示的是什么
- 如何安裝火焰圖生成工具
- 如何定位問題
- 開源文化對360企業安全的影響