https://github.com/cloudwu/skynetgit
1: skynet-src 核心網絡層 2: service-src 服務層 3: service 使用lua實現的服務
Worker 工做執行器 Actor 封裝了上下文的相關服務實現 master 調度分配器
worker的實現 module的加載, 用於支持actor的實現 網絡分發和監聽 以及相關worker的調度
module加載的service_snlua 服務將用於 加載執行lua代碼,並將上下文環境保存到lua虛擬機中,從而構成一個Actorgithub
經過從消息隊列中獲取消息,並將消息分發出去交給service來執行。bootstrap
當沒有消息的時候 worker線程將會等在一個條件變量上。服務器
Skynet 存在一個全局消息隊列的隊列 global_queue,每一個worker 從 global_queue 中獲取一個消息隊列,接着執行當前消息隊列的上下文中的回調函數。網絡
skynet_context_new 函數用於分配一個新的上下文,而且生成一個新的消息隊列,而每一個上下文至關於一個Actor, 而上下文對應的消息隊列 至關於一個 mailbox, 這也就解釋了 在 thread_worker 線程中爲何依次去 取不一樣的消息隊列的消息,防止某個Actor被餓死。session
在skynet 存在一個 command_func cmd_funcs 裏面包含了全部的服務器命令,總共18條,分別爲架構
{ "TIMEOUT", cmd_timeout } { "REG", cmd_reg }, { "QUERY", cmd_query }, { "NAME", cmd_name }, { "NOW", cmd_now }, { "EXIT", cmd_exit }, { "KILL", cmd_kill }, { "LAUNCH", cmd_launch }, { "GETENV", cmd_getenv }, { "SETENV", cmd_setenv }, { "STARTTIME", cmd_starttime }, { "ENDLESS", cmd_endless }, { "ABORT", cmd_abort }, { "MONITOR", cmd_monitor }, { "MQLEN", cmd_mqlen }, { "LOGON", cmd_logon }, { "LOGOFF", cmd_logoff }, { "SIGNAL", cmd_signal },
當某處代碼調用LAUNCH 命令的時候就會新建一個上下文,同時分配一個新的消息隊列給該上下文,這樣Worker就能夠執行消息的分發了。less
而skynet_send 以及 skynet_sendname 函數實現了向消息隊列投遞消息;socket
這樣整個調度基本流程就清晰了:函數
在skynet_start 啓動函數中, 經過執行 snlua bootstrap 命令,執行系統初始化
snlua bootstrap表示執行 bootstrap.lua 腳原本具體初始化。
bootstrap 中將會註冊須要的一些 lua服務。
而調用 skynet_socket_init 將會啓動socket服務器
skynet中消息分爲兩類:
thread_socket 爲socket線程主循環,主要函數是skynet_socket_poll ,
存在兩個方向數據:即從socket接受到的網絡數據,和從內部向外發送的網絡數據。
skynet_socket是一個基礎,在之上構建的服務實例也是一個Actor, 例如service_gated 服務, 以及lua的 gateserver 服務。
這樣當內部服務須要經過socket向外部發送消息的時候,首先將message 投遞給gate,接着由gate來調用實際的socket接口向外發送消息。
一個例子以下:
lua層中經過實例化一個服務來構建一個新的lua狀態機,從而構建一個獨立的Actor。 lua中的接口爲 skynet.newservice 最終調用skynet_context_new 來構建新的Actor。
每當socket 須要接受或者發送數據,或者timer定時器時間到了,都會調用wakeup, 這樣worker線程就會去獲取消息隊列數據. 系統存在一個Monitor線程, 用於監控全部其它線程,包括socket線程,timer線程,worker線程。
skynet_timer 有一個 skynet_timeout API, 在skynet_server 中存在一個 TIMEOUT命令,當接受到TIME_OUT命令的時候,就會增長一個新的timer。
lua層接口爲 skynet.timeout。
Timer主要應用: 某些Actor須要按期執行,經過skynet.timeout 建立一個協程按期調用函數。
skynet lua接口中有一個 co_create 用於建立協程。在如下幾種狀況下建立:
協程能夠等在特定的事件上,當事件發生的時候,喚醒對應協程的執行,這個經過一個suspend函數實現的,將session, 事件以及對應的協程保存起來,當事件發生,找到對應的協程,接着執行協程便可。
skynet 中發送消息的接口主要兩個 skynet_send 和skynet_sendname.
lua層接口 skynet.send
發送消息的時候通常須要提供,當前發送服務的上下文,消息投遞來源,消息目標服務接受者,消息類型,消息會話id,消息數據和消息數據大小。
參考樣例:watchdog.lua 和agent.lua
在watchdog.lua中,close_agent 調用skynet.send(目標服務對象agent, 協議類型lua, 數據內容disconnect)
這樣就經過lua打包協議,將disconnect 字符串發送給了 一個agent服務對象。
在agent.lua中,skynet.start( skynet.dispatch("lua" ))
對經過lua協議分發來的消息,註冊相關的處理函數。在skynet.lua 的 raw_dispatch_message 函數中,當收到新的消息,則根據協議類型lua 找處處理函數.
而agent.lua 中調用skynet.ret()含義爲,將函數執行的結果按原路yield返回給請求方。
而調用agent.lua 的分發處理函數的 raw_dispatch_message 中以協程方式調用該函數,所以在suspend 函數中將會收到,RETURN 命令,而將函數調用結果返回給watchdog.lua
在watchdog.lua 的socket.OPEN 函數中,調用了skynet.call 這個接口和skynet.send不一樣在於call 須要等待請求的服務方的返回。
skynet.call實現使用了 yield_call函數,將會將目標服務和session匹配起來,接着yield一個call命令, 當對方服務的RESPONSE 發送回來的時候,會帶上相關的會話id,接着在raw_dispatch_message 中檢測到協議爲 RESPONSE, 根據會話id 去除watchdog.lua 的掛起的協程,繼續執行watchdog的協程。
skynet 底層只有skynet_send 標準接口用於向目標投遞消息,不一樣的通信模型創建在這個基礎之上。
request response lua層skynet.call 將啓動一個會話,並將lua協程等在該會話上,當response發送回來以後則重啓協程執行
request lua 層skynet.send 直接調用c接口,發送數據包
push 每當skynet raw_dispatch_message 時,都會啓動一個協程來處理對應的消息, 也就是每一個消息對應一個協程, 在同一個Actor中能夠同時存在多個協程