luanet最初只是一個網絡框架,它簡單的封裝了一些網絡相關接口然后暴露到lua中,讓lua可以構建簡單的網絡應用.
隨着我的手游服務器的開發,我發現在C語言中要實現一個簡潔易用的RPC調用接口並不容易,於是開始考慮將整個服務器框架從主C,
輔lua的模式轉變成主lua,輔C的模式.網絡通訊,異步日志等一些在lua中無法實現或cpu消耗大的組件由C語言實現注冊到lua中.由lua
組合這些組件來實現一 個分布式的網絡框架.
進入主題,首先簡單的介紹luanet的特性:
-
單線程(日志另開一個線程用於往磁盤異步寫)
-
支持TCP,UDP,UNIX域套接字
-
提供面向消息和RPC的通訊方式
-
利用coroutine實現同步的遠程調用接口
-
談化連接的概念,服務之間通過名字通訊
-
直接使用lua table作為通訊協議
下面通過一個簡單的實例來介紹luanet的使用:
local net = require "lua/net" local table2str = require "lua/table2str" local nameservice = net.netaddr_ipv4("127.0.0.1",8010) local function Plus(arg) return nil,arg[1] + arg[2] end luanet.RegRPCFunction("Plus",Plus) luanet.StartLocalService("PlusServer",SOCK_STREAM,net.netaddr_ipv4("127.0.0.1",8012)) luanet.Register2Name(nameservice)
上面的代碼實現了一個簡單的服務器,提供一個叫做Plus的遠程方法. 首先注冊遠程方法,名字就是Plus,之后用服務名PlusServer在8012
端口上啟動一個TCP監聽. 然后向名字服務注冊自己.一個簡單的遠程調用服務就實現了,下面再來看下客戶端.
local net = require "lua/net" local table2str = require "lua/table2str" local Sche = require "lua/scheduler" local nameservice = net.netaddr_ipv4("127.0.0.1",8010) --啟動本地服務 luanet.StartLocalService("PlusClient",SOCK_STREAM,net.netaddr_ipv4("127.0.0.1",8012)) --注冊到NameService luanet.Register2Name(nameservice) --啟動100個lightprocess執行遠程調用 for 1,100 do Sche.Spawn( function() while true do local ret,err = luanet.RPCCall("PlusServer","Plus",{1,2}) if err then return else print(ret) end end end ) end
client首先用名字PlusClient在8012上啟動一個TCP監聽用於跟其它服務通信.之后向名字服務注冊自己,然后 Spawn個coroutine不斷的執行對
PluServer的Plus遠程方法的調用.對於上面的示例需要說明的一點是,luanet中的所有代碼都運行在coroutine環境下,輸入luanet xxx.lua 的
時候,luanet首先會載入statr.lua文件,然后用xxx.lua作為參數調用start函數.由start函數啟動一個coroutine去執行xxx.lua文件,具體可以
查看luanet.c和start.lua.
下面簡單介紹luanet的公共接口:
StartLocalService(local_name,local_socktype,local_addr,cb_disconnected)
用local_name作為服務的名字,啟動本地服務.這里需要說明的是,當服務A向服務B發送消息或調用遠程方法的時候,A會建立一個到B的連接
(connect B的主監聽),B在這個連接上接收A發過來的所有消息和請求,但不會通過這個連接往A發送消息或遠程方法的響應.如果B要向A發送
消息或遠程回應,則B要建立一條到A的連接(connect A 的主監聽).也就是說A與B之間如果要雙向通信需要建立兩條單向的連接.
Register2Name(nameaddr)
將服務注冊到名字服務.
SendMsg(name,msg)
向name服務發送一條消息
RPCCall(name,funcname,arguments)
調用name服務的funcname方法
RegRPCFunction(name,func)
注冊一個遠程方法
GetRemoteFuncProvider(funcname)
獲取提供遠程方法funcname的服務名字列表
GetMsg()
提取發往本服務的消息,如果消息隊列為空阻塞當前coroutine.建議的使用方式是啟動一組coroutine調用GetMsg,這樣當一個
coroutine阻塞在遠程調用上時還有其它的coroutine可以繼續提取消息並服務.
有興趣的朋友可以關注luanet