之前已經說過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_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節點通訊
每個模塊需要實現四個最基本的函數,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的配置加載
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框架之菜鳥手冊》