local skynet = require("skynet")vim
skynet.start(start_func)session
c服務snlua啓動後執行的第一個lua文件裏面的主邏輯一定是skynet.start(start_func),由此開始運行lua服務的邏輯數據結構
start_func是當前lua服務的初始化函數,也是當前服務的第一個協程的函數框架
以後在收到非response消息時dispatch_message會建立更多的協程來作邏輯異步
而調用skynet.start(start_func)的主線程會調度上述這些協程(yield)函數
dispatch_message(...) ui
這就是ctx的消息處理函數(skynet_context.skynet_cb, 返回零(假)時釋放消息的內存.那麼lua層如何控制呢?c.callback(dispatch_message,false)就是釋放內存,c.callback(dispatch_message,true)不釋放內存,具體參見vim -t _callback.那麼lua層如何控制發消息時不作複製呢?我看skynet.send/call/rawcall都不支持對複製的控制,參見vim -t _send. 底層的默認處理是:對於Lua string直接複製,對於lightuserdata不復制,對於其餘類型報錯)lua
dispatch_message將是本服務的發動機(消息驅動機),也就是底層工做線程拿到本服ctx.mq中的一個消息後執行的消息處理函數(vim -t _dispatch_message, 底層由worker thread取到消息後調用這個函數而觸發lua函數的調用),也就是本lua服的主線程(雖然線程ID不固定)spa
dispatch_message每處理完mq中的一個消息都要遍歷並執行消息處理過程當中fork而沒運行的新協程(遍歷fork_queue並coroutine.resume())prototype
對於收到的每一個非response(prototype!=skynet.PTYPE_RESPONSE)消息啓動一個新協程X,用該協程來運行協議類型對應的dispatch函數來處理消息
對於收到的每一個response消息,根據session從session_id_coroutine取出協程並恢復執行
協程X運行業務邏輯時可能會「對其餘服務作請求並等待結果」或者「睡眠幾秒」,這時協程X用yield拋出「CALL」/「SLEEP」等返回值並掛起,主線程根據yield拋出的值對協程作不一樣處理
CALL -> 協程X已對其餘服務發出請求並等待迴應 -> 主線程把協程X記錄到session_id_coroutine中,下次收到對應的response消息(sessionID一致)時喚醒
SLEEP -> 協程X[已調用skynet.sleep(ti)等定時器返回]或[已調skynet.wait()等其餘服務返回,這種狀況通常須要用skynet.wakeup()來喚醒,不然協程可能永遠沉睡下去了] -> 主線程把協程X記錄到session_id_coroutine,並記錄sleep_session,對於sleep_session須要用skynet.wakeup(co)喚醒 (session_id_coroutine和sleep_session二者怎麼維持數據一致,這個細節還須要結合實例再看看 markbyxds )
skynet.newservice(name,...)
建立lua服務 skynet.rawcall(".launcher", "lua" , skynet.pack("LAUNCH", "snlua", name, ...))
實際上是skynet_context_new(module_name("snlua"), param("cmaster"))
即,用snlua服跑着一個lua邏輯服(service/cmaster.lua),snlua創造了一個lua環境的沙盒
除了service/launch.lua自身之外,其餘lua服務通常都是由service/launch.lua這個lua服負責建立的
固然,launch服最終仍是調用的skynet.launch("snlua","xxx")來建立服務
skynet.launch
建立c服務, lualib-src/lua-skynet.c -> skynet_command(CTX,"LAUNCH",..) -> skynet_context_new(mod,args)
對於skynet.launch("snlua","xxx"),這是建立c服務snlua而後在它上面跑lua服務xxx
skynet.monitor(service, query) 監控服務退出,細節還沒仔細看 markbyxds
skynet.uniqueservice(global,...)
建立一個惟一的服務,調用屢次service/***.lua也只啓一個實例,好比clusterd和multicastd
global=true時,在全部節點之間是惟一的
實際上是用skynet.call(異步變同步)的方式讓service_mgr服務建立目標服務(相似於通知launch服建立服務同樣)
service_mgr這邊如已建立則直接返回服務地址;如沒則建立;如正在建立則等結果
skynet.queryservice(global,...) global=true時
若是尚未建立過目標服務則一直等下去,直到目標服務被(其餘服務觸發而)建立
skynet.rawcall
向目標服發送無協議的消息並返回response,協程掛起等返回,用同步代碼的樣式實現異步邏輯
當 A call B 時,若是 B 在迴應前就退出了,A 會收到一條異常,並正確的傳播到 A 裏的 call 調用處;
當 A call B ,而 B 在迴應前,A 本身退出了,B 也會收到一條異常,提示 A 已經不在了。但不會影響 B 的執行流程,只是讓框架回收一些必要的相關資源。
skynet.call 跟skynet.rawcall的區別是向目標服發送指定協議的消息
skynet.send 跟skynet.call的區別是僅僅發消息而已,不關心返回值也不會讓當前協程掛起(非request-response模式)
skynet.wait()
把當前協程掛起放入session_id_coroutine/sleep_session
當收到等待的消息(會把消息對應的正在等待session放入wakeup_session中)後該協程恢復執行
skynet.sleep(ti)
相似於skynet.wait(),只是要事先通知底層定時器
等定時器(在ti時間達到後)發來消息時該協程恢復執行
skynet.wakeup(co)
若是協程co正處於掛起等待的狀態(在sleep_session中)則把它加入wakeup_session
本服務的主協程會在調度過程當中把wakeup_session中的協程喚醒執行
skynet.ret 在當前協程(爲處理請求方消息而產生的協程)中給請求方(消息來源)的消息作迴應
skynet.retpack 跟skynet.ret的區別是向請求方做迴應時要用skynet.pack打包
skynet.register_protocol
註冊協議:
協議名(name)
協議ID(id)
發送消息的打包函數(pack)
接收消息的拆包函數(unpack)
接收消息的(分發)處理函數(dispatch)
已註冊的協議記錄在lualib/skynet.lua:proto這個數據結構上
每一個服務會默認初始化lua/response/error這幾種協議
skynet.dispatch(typename, func)
修改以typename爲協議名的協議:用func這個函數來做爲協議的dispatch函數(默認的lua協議沒提供dispatch,須要使用者根據業務須要寫)
skynet.fork
建立一個新的協程
這裏有作協程對象池來加速,相似於咱們項目中的線程池,建立一堆協程並掛起,接到業務後拿協程跑業務邏輯
skynet.register(name)
註冊當前服務的名字(默認用:%x做爲name,本地服務的自定義名以.打頭)
把<handleId + name>記錄到handle_storage->name,參數必須符合本地服務的命名規範
skynet.self() 返回當前服務的handleID,若是還沒註冊就先註冊(相似於skynet.register)
skynet.harbor(addr) return 「addr(ctx的handleID)對應的harborID",boolean(是否遠端節點)
skynet.address(addr) return "addr(ctx的handleID或者name)對應的name(string,:%x或者自定義名)"