skynet源碼分析:模塊


之前已經說過skynet的是做什么的,現在開始從模塊上研究skynet的源碼。

 

skynet各層表現

 

 

從上大概就清楚skynet的“內部”

而skynet源碼目錄結構如下:

3rd:第三方代碼,有lua和jemalloc等。

lualib:使用lua寫的庫

lualib-src:使用C寫並封裝給lua使用的庫

service:使用lua寫的skynet的服務模塊

service-src:使用C寫的skynet的服務模塊

skynet-src:skynet核心代碼

 

skynet重要模塊

gate.so:為整個skynet提供socket功能(解決外部連接數據讀取的問題

snlua.so:啟動多個lua服務,skynet自帶的模塊中有一個重要的模塊是snlua.so模塊,通過snlua.so和指定lua腳本文件可以啟動多個lua編寫的服務,不用每個服務都是用c來編寫,而且大部分邏輯都是在 lua 腳本下開發,只有需要考慮性能的模塊才用 C 語言開發成庫,直接提供給 lua 調用。 

logger.so:日志模塊,一個簡單的日志系統,可以用來記錄服務的相關信息。

harbar.so:(集群模塊)節點服務,每個Skynet運行都是一個節點

 

skynet重要服務

launcher.lua:在lua中啟動服務

skynet.lua:lua常用功能封裝

skynet.so:lua調用skynet功能

 

skynet重要文件
skynet_server.c:管理服務
skynet_handle.c:管理服務唯一的handle
skynet_module.c:啟動c編寫的so模塊
skynet_monitor.c:監視服務死循環
skynet_mq.c:消息隊列
skynet_timer.c:定時器
skynet_socket.c:Socket
skynet_master.c:不同skynet節點服務名字中心服務
skynet_harbor.c:不同skynet節點通訊
 

 

skynet底層代碼位於skynet/skynet-src下,模塊加載相關在skynet-module.c skynet-module.h這兩個文件里。這里的模塊在linux下指的是so,在windows下指的是dll,在skynet中指的是config中配置的cpath下的文件。



每個模塊需要實現四個最基本的函數,create/init/release/signal。create做內存分配。init做初始化,它可能會做一些其它的事情,比如打開網絡,打開文件,函數回調掛載等等。relase做資源回收,包括內存資源,文件資源,網絡資源等等,signal是發信號,比如kill信號,告訴模塊該停了。

 

網絡

網絡部分是一個服務器最基礎最核心的部分,這個技術也已經是非常成熟了,現在已經很少有人自己實現一個網絡相關的庫了。skynet的網絡庫是自己實現的。

實際上雲風只實現了epoll和kqueue,windows上的變種請自行搜索吧。

 

epoll和kqueue的實現分別在skynet_epoll.h和epoll_kqueue.h當中。epoll的函數其實就是epoll_create/epoll_ctl/epoll_del/epoll_wait這幾個,要注意的是skynet中的epoll_create的參數是1024。所以連接數上不去的話很可能就是這里限制了。

skynet在skynet_poll.h中根據平台的不同包含了不同的頭文件,屏蔽了平台相關性。然后在socket_server.c中實現了網絡服務的邏輯。

然后skynet在skynet_socket.c中對socket_server.c中的邏輯再次做了一個封裝,還添加了socket客戶端相關的函數,就是connect/send/close之類的函數。

為了方便lua層使用socket,在lua-socket.c中再將對skynet_socket.c進行了一次封裝。這個封裝就是c語言層和lua語言層的相互轉換。目前只支持tcp和udp,基於tcp上的http/websocket之類統統是不支持的。

 

為什么操作一個網絡要費這么大的勁呢,繞來繞去非常的不直觀。因為skynet是基於消息的,而且每個服務都有一個monitor,每個消息處理的時候要盡可能的短,這樣才不會阻塞服務里其它的請求。而connect這種明顯是阻塞的,當然也可以寫成非阻塞的,但是非阻塞的話,你需要不斷地掛起,因為非阻塞實際上是基於select技術來實現的。而不斷地掛起,這個就很麻煩,寫起來很痛苦而且很容易出錯。因此雲風把這些都放到網絡線程中來做,這樣就不會影響工作線程。但是這樣做也有它的缺點,那就是網絡線程可能會被阻塞,網絡線程被阻塞就會導致服務無響應。或者導致大量的數據包積累,引起波峰。

skynet的配置加載

 

skynet的配置文件是以lua格式來寫的。使用過skynet的都清楚skynet的啟動命令是skynet config_file_name。配置文件名是作為命令行參數傳給skynet進程的。

skynet進程啟動以后,會讀取config文件,然后解析這個lua文件。然后把相關的配置信息設置到lua的環境變量里。

C層讀取配置的話是要從lua環境變量里去取的。

 

skynet一共有4種線程,monitor線程用於檢測節點內的消息是否堵住,timer線程運行定時器,socket線程進行網絡數據的收發,worker線程則負責對消息隊列進行調度(worker線程的數量,可以通過配置表指定)。消息調度規則是,每條worker線程,每次從全局消息隊列中pop出一個次級消息隊列,並從次級消息隊列中pop出一條消息,並找到該次級消息隊列的所屬服務,將消息傳給該服務的callback函數,執行指定業務,當邏輯執行完畢時,再將次級消息隊列push回全局消息隊列中。因為每個服務只有一個次級消息隊列,每當一條worker線程,從全局消息隊列中pop出一個次級消息隊列時,其他線程是拿不到同一個服務,並調用callback函數,因此不用擔心一個服務同時在多條線程內消費不同的消息,一個服務執行,不存在並發,線程是安全的

socket線程、timer線程甚至是worker線程,都有可能會往指定服務的次級消息隊列中push消息,push函數內有加一個自旋鎖,避免同時多條線程同時向一個次級消息隊列push消息的慘局。

參考:

《skynet_summary》

《Skynet框架之菜鳥手冊》

skynet源碼分析(1)--模塊加載

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM