net/http で起動しているウェブサーバーに Lua で記述された関数を登録し、URL経由で登録した関数を呼び出せるようにしてみます。
登録するLuaの関数には、実装の都合上、以下の制約を設けます
1. 関数名は lambda
とする
2. 関数は返り値を1つ返す
実装
Lua の関数を動的に登録するので goroutine を使い、その中でLua関数を呼び出します。
関数の呼び出しは channel で行います。
package main
import (
"fmt"
"io/ioutil"
"net/http"
"strconv"
"github.com/yuin/gopher-lua"
)
type httpRequest struct {
W http.ResponseWriter
R *http.Request
}
var (
count = 1 // 登録されたプロセス数
processMap = make(map[string]chan httpRequest)
)
const (
retNum = 1 // 登録されるLuaスクリプトの返り値の数
funcName = "lambda" // Luaスクリプトに登録される関数名
)
func execLuaProcess(w http.ResponseWriter, r *http.Request) {
processNum := r.URL.Path[len("/lua/exec/"):]
ch, ok := processMap[processNum]
if !ok {
fmt.Fprintf(w, "不明なプロセス番号です: %s", processNum)
return
}
ch <- httpRequest{w, r}
<-ch // Luaプロセスの完了を待つ
}
func registerLuaProcess(w http.ResponseWriter, r *http.Request) {
L := lua.NewState()
// defer L.Close() // TODO: どこで Close するのが適切か調べる
luaScript, err := ioutil.ReadAll(r.Body)
defer r.Body.Close()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "%v", err)
return
}
if err := L.DoString(string(luaScript)); err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "%v", err)
return
}
fn := L.GetGlobal(funcName)
ch := make(chan httpRequest)
go func() {
for {
httpReq := <-ch
if err := L.CallByParam(lua.P{
Fn: fn,
NRet: retNum,
Protect: true,
}); err != nil {
httpReq.W.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(httpReq.W, "%v", err)
ch <- httpReq
continue
}
// 関数の返り値を取得する
ret := L.Get(-1)
L.Pop(1)
fmt.Fprintf(httpReq.W, "Luaプロセスが %v を返しました", ret)
ch <- httpReq
}
}()
processMap[strconv.Itoa(count)] = ch
fmt.Fprintf(w, "luaプロセスを %d 番に登録しました", count)
count++
}
func main() {
http.HandleFunc("/lua/register/", registerLuaProcess)
http.HandleFunc("/lua/exec/", execLuaProcess)
http.ListenAndServe(":8080", nil)
}
実行結果
- Lua 関数を登録する
$ curl -X POST localhost:8080/lua/register/ -d 'lambda = function() return "hoge" end'
luaプロセスを 1 番に登録しました
$ curl -X POST localhost:8080/lua/register/ -d 'lambda = function() return math.random(1, 100) end'
luaプロセスを 2 番に登録しました
- 登録した関数を呼び出してみる
$ curl localhost:8080/lua/exec/1
Luaプロセスが hoge を返しました
$ curl localhost:8080/lua/exec/2
Luaプロセスが 82 を返しました
URL ごとに異なる関数が呼ばれていることが確認できました。
その他
実装の都合上、関数名と返り値を固定しましたが gopher-lua では ast が触れるみたいなので、関数名や返り値の数などは動的に取得できるかもしれません(未確認)