初探 Skynet:輕量級分布式游戲服務器框架實戰

在游戲服務器開發領域,高效、穩定且易于擴展的框架一直是開發者追求的目標。Skynet 作為一款輕量級、高性能的分布式游戲服務器框架,憑借其獨特的設計理念和強大的功能,贏得了眾多開發者的青睞

一.Skynet底層架構支持

1.Actor
erlang 從語言層面支持 actor 并發模型,并發實體是 actor (在 skynet 中稱之為 服務 ); skynet 采用 c + lua 來實現 actor 并發模型;底層也是通過采用多少個核心開啟多少個內核線程來充分利用多核;

?

有消息的 actor 為活躍的 actor,沒有消息為非活躍的 actor?

?2.config

我們要配置config文件來支持skynet的運行

thread=8    --8個工作線程
logger=nil  --不打日志
harbor=0 
start="main" --main文件作為入口函數
lua_path="./skynet/lualib/?.lua;./skynet/lualib/?/init.lua;"
-- lua 抽象進程
luaservice="./skynet/service/?.lua;./app/?.lua;"
lualoader="./skynet/lualib/loader.lua"
cpath="./skynet/cservice/?.so"
lua_cpath="./skynet/luaclib/?.so"
?3.core-eg

我們通過一個網絡消息的代碼案例來看actor是怎么運行的

local skynet = require "skynet"
local socket = require "skynet.socket"skynet.start(function()local listenfd = socket.listen("192.168.217.148",8888)socket.start(listenfd,function (clientfd,addr)print("recive a client:",clientfd,addr)end)skynet.timeout(100,function()print("after 1s ,do here")end)print("hello skynet")-- local slave = skynet.newservice("slave")-- local response = skynet.call(slave,"lua","ping")--[[main ->  ping ->slave  //協程掛起slave -> pong ->main   //協程喚醒c/c++ 異步流程 callbcak 協程來實現通過協程的方式消除異步產生的回調我們就能寫同步的代碼]]-- print(response)
end)

?

skynet.start?是服務的入口點,Skynet 會為這個服務創建一個獨立的 Actor服務啟動時執行的代碼(如打印 "hello skynet")在 Actor 初始化階段同步執行

socket.listen?和?socket.start?注冊了一個網絡監聽事件

當有新客戶端連接時,Skynet 會將這個事件封裝成消息發送給當前服務 Actor

服務 Actor 在處理消息時執行回調函數(打印客戶端信息)

skynet.timeout?注冊了一個定時器事件

Skynet 的定時器系統會在指定時間后向服務 Actor 發送一個超時消息

服務 Actor 收到超時消息后執行對應的回調函數

?

main

local skynet = require "skynet"
local socket = require "skynet.socket"skynet.start(function()local slave = skynet.newservice("slave")local response = skynet.call(slave,"lua","ping")--[[main ->  ping ->slave  //協程掛起slave -> pong ->main   //協程喚醒c/c++ 異步流程 callbcak 協程來實現通過協程的方式消除異步產生的回調我們就能寫同步的代碼]]print(response)
end)

slave

local skynet = require "skynet"local CMD = {}function CMD.ping()skynet.retpack("pong")
endskynet.start(function()
skynet.dispatch("lua",function(session,source,cmd,...)local func = assert(CMD[cmd])func(...)
end)
end)

?我們上述代碼通過callback回調的方式來驅動actor運行

?4.公平調度

?

actor 是抽象的用戶態進程,相對于 linux 內核 ,有進程調度,那么 skynet 也要實現 actor 調度
1. 將活躍的 actor 通過 全局隊列 組織起來; actor 當中的 消息隊列 有消息就是活躍的 actor
2. 線程池去 全局隊列中取出 actor 的消息隊列,接著運行 actor

二.lua/c 接口編程

skynet openresty 都是深度使用 lua 語言的典范;學習 lua 不僅僅要學習基本用法,還要學會使用 c lua 交互,這樣才學會了 lua 作為膠水語言的精髓;

?

?1.虛擬棧
①棧中只能存放 lua 類型的值,如果想用 c 的類型存儲在棧中,需要將 c 類型轉換為 lua 類型;
②lua 調用 c 的函數都得到一個 的棧,獨立于之前的棧;
③c 調用 lua ,每一個協程都有一個棧;
④c? 創建虛擬機時,伴隨創建了一個主協程,默認創建一個虛擬棧;
⑤無論何時 Lua 調用 C , 它都只保證至少有 LUA_MINSTACK 這么多的堆棧空間可以使用。
⑥LUA_MINSTACK 一般被定義為 20 , 因此,只要你不是不斷的把數據壓棧, 通常你不用關心堆棧 大小。

?2.C調用lua接口
#include <stdio.h>
#include <stdlib.h>#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>//lua嵌入在C語言里面的好處--->熱更新 不需要重新編譯lua代碼 重啟即可更新or文件變化重新加載/** 調用Lua全局函數,向其傳遞一個整數值1* L: Lua虛擬機指針* funcname: 要調用的Lua函數名稱*/
static void
call_func_0(lua_State *L, const char* funcname)
{// 從全局環境中獲取指定名稱的函數lua_getglobal(L, funcname);//向棧頂壓入整數參數1lua_pushinteger(L, 1);// 調用函數,1個參數,0個返回值lua_call(L, 1, 0);//C調lua必須維護棧的平衡 eg:--->lua_pop(L);
}int main(int argc, char** argv)
{//創建新的lua虛擬機lua_State *L = luaL_newstate();//打開lua標準庫luaL_openlibs(L);if (argc > 1) {lua_pushboolean(L, 1);lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV");if ( LUA_OK != luaL_dofile(L, argv[1]) ) {const char* err = lua_tostring(L, -1);fprintf(stderr, "err:\t%s\n", err);return 1;}// 依次調用Lua腳本中的Init、Loop、Release函數call_func_0(L, "Init");call_func_0(L, "Loop");call_func_0(L, "Release");lua_close(L);return 0;}return 0;
}
package.cpath = "luaclib/?.so"
local so = require "tbl.c"--a=1
--全局可見--local a =1
--當前文件可見-- 加了local相當于C/C++中的staticfunction Init(args)print("call [init] function", args)
endfunction Loop()print("call [loop] function")for k, v in ipairs({1,2,3,4,5}) do--so.echo(v)  -- 等于 調用 5 次  因為每次調用都是一個新的虛擬棧,所以沒有必要維護 --->C調用lua然后調Cprint("value = " .. v)  --->C調用luaendreturn 1
endfunction Release()print("call [release] function")
end

c->lua每個協程一個棧 我們需要維護棧的空間 彈出不需要的棧空間

?3.C調用lua接口-->lua再調用C接口
#include <stdio.h>
#include <stdlib.h>#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>//lua嵌入在C語言里面的好處--->熱更新 不需要重新編譯lua代碼 重啟即可更新or文件變化重新加載/** 調用Lua全局函數,向其傳遞一個整數值1* L: Lua虛擬機指針* funcname: 要調用的Lua函數名稱*/
static void
call_func_0(lua_State *L, const char* funcname)
{// 從全局環境中獲取指定名稱的函數lua_getglobal(L, funcname);//向棧頂壓入整數參數1lua_pushinteger(L, 1);// 調用函數,1個參數,0個返回值lua_call(L, 1, 0);//C調lua必須維護棧的平衡 eg:--->lua_pop(L);
}int main(int argc, char** argv)
{//創建新的lua虛擬機lua_State *L = luaL_newstate();//打開lua標準庫luaL_openlibs(L);if (argc > 1) {lua_pushboolean(L, 1);lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV");if ( LUA_OK != luaL_dofile(L, argv[1]) ) {const char* err = lua_tostring(L, -1);fprintf(stderr, "err:\t%s\n", err);return 1;}// 依次調用Lua腳本中的Init、Loop、Release函數call_func_0(L, "Init");call_func_0(L, "Loop");call_func_0(L, "Release");lua_close(L);return 0;}return 0;
}
package.cpath = "luaclib/?.so"
local so = require "tbl.c"--a=1
--全局可見--local a =1
--當前文件可見-- 加了local相當于C/C++中的staticfunction Init(args)print("call [init] function", args)
endfunction Loop()print("call [loop] function")for k, v in ipairs({1,2,3,4,5}) doso.echo(v)  -- 等于 調用 5 次  因為每次調用都是一個新的虛擬棧,所以沒有必要維護 --->C調用lua然后調C--print("value = " .. v)  --->C調用luaendreturn 1
endfunction Release()print("call [release] function")
end
#include <lua.h>#include <lauxlib.h>#include <lualib.h>#include <stdio.h>static int
lecho (lua_State *L) {const char* str = lua_tostring(L, -1);fprintf(stdout, "%s\n", str);return 0;
}static const luaL_Reg l[] = {// 導出給lua使用數組{"echo", lecho},{NULL, NULL},
};int
luaopen_tbl_c(lua_State *L) { // local tbl = require "tbl.c"// 創建一張新的表,并預分配足夠保存下數組 l 內容的空間// luaL_newlibtable(L, l);// luaL_setfuncs(L, l, 0);luaL_newlib(L, l);return 1;
}

4.運行lua/?src/lua 可執行程序讓lua調用C接口

#include <lua.h>#include <lauxlib.h>#include <lualib.h>#include <stdio.h>static int
lecho (lua_State *L) {const char* str = lua_tostring(L, -1);fprintf(stdout, "%s\n", str);return 0;
}static const luaL_Reg l[] = {// 導出給lua使用數組{"echo", lecho},{NULL, NULL},
};int
luaopen_tbl_c(lua_State *L) { // local tbl = require "tbl.c"// 創建一張新的表,并預分配足夠保存下數組 l 內容的空間// luaL_newlibtable(L, l);// luaL_setfuncs(L, l, 0);luaL_newlib(L, l);return 1;
}
package.cpath = "luaclib/?.so" --c庫的路徑local so = require "tbl.c"so.echo("hello world") -- 新的虛擬棧
so.echo("hello world1")-- 新的虛擬棧
so.echo("hello world2")-- 新的虛擬棧--[[1. c調用lua  c有多個協程 每個協程一個虛擬棧2. lua調用c  每次調用都有一個虛擬棧
]]

#include <lua.h>#include <lauxlib.h>#include <lualib.h>#include <stdio.h>// 閉包實現:  函數 + 上值  luaL_setfuncs
// lua_upvalueindex(1)
// lua_upvalueindex(2)
static int
lecho (lua_State *L) {lua_Integer n = lua_tointeger(L, lua_upvalueindex(1));n++;const char* str = lua_tostring(L, -1);fprintf(stdout, "[n=%lld]---%s\n", n, str);lua_pushinteger(L, n);lua_replace(L, lua_upvalueindex(1));return 0;
}static const luaL_Reg l[] = {{"echo", lecho},{NULL, NULL},
};int
luaopen_uv_c(lua_State *L) { // local tbl = require "tbl.c"luaL_newlibtable(L, l);// 1lua_pushinteger(L, 0);// 2luaL_setfuncs(L, l, 1);// 上值// luaL_newlib(L, l);return 1;
}

?

package.cpath = "luaclib/?.so"local so = require "uv.c"so.echo("hello world1")
so.echo("hello world2")
so.echo("hello world3")
so.echo("hello world4")
so.echo("hello world5")
so.echo("hello world6")
so.echo("hello world7")
so.echo("hello world8")
so.echo("hello world9")--./lua/src/lua test-uv.lua

#include <lua.h>#include <lauxlib.h>#include <lualib.h>#include <stdio.h>#include <stdlib.h>#include <string.h>struct log {int count;
};static int
lagain(lua_State *L) {struct log *p = (struct log *)luaL_checkudata(L, 1, "mk.ud.log");lua_getuservalue(L, -1);const char* str = lua_tostring(L, -1);fprintf(stdout, "ud[n=%d]----%s\n", p->count, str);return 0;
}static int
lecho(lua_State *L) {struct log *p = (struct log *)luaL_checkudata(L, 1, "mk.ud.log");const char* str = lua_tostring(L, -1);p->count++;lua_setuservalue(L, -2);fprintf(stdout, "ud[n=%d]----%s\n", p->count, str);return 0;
}static int
lnew (lua_State *L) {struct log *q = (struct log*)lua_newuserdata(L, sizeof(struct log));q->count = 0;lua_pushstring(L, "");lua_setuservalue(L, -2);if (luaL_newmetatable(L, "mk.ud.log")) {luaL_Reg m[] = {{"echo", lecho},{"again", lagain},{NULL, NULL},};luaL_newlib(L, m);lua_setfield(L, -2, "__index");lua_setmetatable(L, -2);}return 1;
}static const luaL_Reg l[] = {{"new", lnew},{NULL, NULL},
};int
luaopen_ud_c(lua_State *L) {luaL_newlib(L, l);return 1;
}

?

package.cpath = "luaclib/?.so"local so = require "ud.c"local ud = so.new()ud:echo("hello world1")
ud:again()
ud:echo("hello world2")
ud:again()
ud:echo("hello world3")
ud:again()
ud:echo("hello world4")
ud:again()
ud:echo("hello world5")
ud:again()
ud:echo("hello world6")
ud:again()
ud:echo("hello world7")
ud:again()
ud:echo("hello world8")
ud:again()
ud:echo("hello world9")
ud:again()--./lua/src/lua test-ud.lua

lua->c不需要維護棧空間 因為每次調用都會生成一個新棧

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/79815.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/79815.shtml
英文地址,請注明出處:http://en.pswp.cn/web/79815.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Oracle數據庫中,WITH..AS 子句用法解析

前言 在Oracle數據庫中&#xff0c;WITH子句&#xff08;也成為“子查詢因子化” 或“公共表表達式”&#xff0c;Common Table Expression, CTE)用于定義一個臨時命名的結果集&#xff0c;這個結果集可以在后續的查詢中引用。 一、基本語法 WITH sjtab AS &#xff08;SELE…

什么是衛星電話?衛星電話與普通電話有什么區別?

在信息時代&#xff0c;通信技術已滲透到人類生活的方方面面&#xff0c;但傳統地面通信網絡仍存在盲區&#xff0c;無論是海洋、沙漠、高山&#xff0c;還是災害現場&#xff0c;普通手機往往因基站信號無法覆蓋而失效。此時&#xff0c;衛星電話便成為連接世界的“生命線”。…

【C/C++】高階用法_筆記

1. 模板元編程&#xff08;TMP&#xff09;與編譯時計算 (1) 類型萃取與 SFINAE 類型萃取&#xff08;Type Traits&#xff09;&#xff1a;利用模板特化在編譯時推斷類型屬性。 template<typename T> struct is_pointer { static constexpr bool value false; };templ…

鴻蒙OSUniApp 實現一個精致的日歷組件#三方框架 #Uniapp

使用 UniApp 實現一個精致的日歷組件 前言 最近在開發一個約會小程序時&#xff0c;需要實現一個既美觀又實用的日歷組件。市面上雖然有不少現成的組件庫&#xff0c;但都不太符合我們的設計需求。于是&#xff0c;我決定從零開始&#xff0c;基于 UniApp 自己實現一個功能完…

PyQt5完整指南:從入門到實踐

引言 PyQt5是Python編程語言的一個GUI&#xff08;圖形用戶界面&#xff09;工具包&#xff0c;它是Qt5應用程序框架的Python綁定。Qt是一個跨平臺的C應用程序開發框架&#xff0c;被廣泛用于開發GUI程序和非GUI程序。PyQt5讓Python開發者能夠使用Python語言享受到Qt框架的強大…

Excel的詳細使用指南

### **一、Excel基礎操作** #### **1. 界面與基本概念** - **工作簿&#xff08;Workbook&#xff09;**&#xff1a;一個Excel文件&#xff08;擴展名.xlsx&#xff09;。 - **工作表&#xff08;Worksheet&#xff09;**&#xff1a;工作簿中的單個表格&#xff08;默認名…

Linux grep -r 查找依賴包是否存在依賴類 Class

方法一&#xff1a;通過 Linux &#xff0c;grep -r ClassPath 命令 grep -f org.apache.kafka.connect.source.SourceRecord在 jar 包所在 lib 或者 lib/plugins 目錄下執行&#xff0c;grep -r&#xff0c; flink-sql-connector-sqlserver-cdc-3.3.0.jar 中此 kafka Source…

碎片筆記|AI生成圖像溯源方法源碼復現經驗(持續更新中……)

前言&#xff1a;本篇博客分享一些溯源方法的復現經驗&#xff0c;希望能幫助到大家&#x1f389;。 目錄 1. Close-set AttributionRepmixDe-FakeDNA-Net 2. Open-set AttributionPOSE 3. Single-Model AttributionOCC-CLIPLatentTracer 1. Close-set Attribution Repmix 論…

SAP Fiori Elements Object Page

?? SAP Fiori Elements Object Page:魔法積木城堡的建造秘密 想象一下,你曾經去過一個神奇的樂高主題公園,在那里,城堡會根據你的設計圖紙自動搭建,而你只需要提供一張設計說明書,不必親自擺放每一塊積木!這就是SAP Fiori Elements Object Page的渲染魔法! ???♂…

Git 用戶名與郵箱配置全解析:精準配置——基于場景的參數選擇

目錄 一、配置查看&#xff1a;理解多層級配置體系二、精準配置&#xff1a;基于場景的參數選擇1. 倉庫級配置&#xff08;推薦&#xff09;2. 用戶級配置3. 系統級配置 三、歷史提交信息修改1. 修改最近一次提交2. 修改多個歷史提交&#xff08;危險操作&#xff09; 五、配置…

Fabric系列 - SoftHSM 軟件模擬HSM

在 fabric-ca-server 上使用軟件模擬的 HSM(密碼卡) 功能 安裝 SoftHSMv2 教程 SoftHSMv2 默認的配置文件 /etc/softhsm2.conf默認的token目錄 /var/lib/softhsm/tokens/ 初始化和啟動fabric-ca-server&#xff0c;需要設置一個管理員用戶的名稱和密碼 初始化令牌 # 初始…

醫學影像系統的集成與工作流優化

?? 博主簡介:CSDN博客專家、CSDN平臺優質創作者,高級開發工程師,數學專業,10年以上C/C++, C#, Java等多種編程語言開發經驗,擁有高級工程師證書;擅長C/C++、C#等開發語言,熟悉Java常用開發技術,能熟練應用常用數據庫SQL server,Oracle,mysql,postgresql等進行開發應用…

轉發多臺px4仿真UDP數據到地面站

轉發腳本的任務需求 仿真采用UDP通信方式&#xff0c;在 wsl 中仿真三臺飛機&#xff0c;項目需要將一臺電腦中的三臺飛機的數據打包發送到另一臺飛機的地面站&#xff0c;但地面站是無法直接訪問另一臺主機的 wsl 中的端口的&#xff0c;wsl 中的端口需要本機才能訪問&#x…

FPGA圖像處理(五)------ 圖片水平鏡像

利用bram形成雙緩沖&#xff0c;如下圖配置所示&#xff1a; wr_flag 表明 buffer0寫 還是 buffer1寫 rd_flag 表明 buffer0讀 還是 buffer1讀 通過寫入邏輯控制(結合wr_finish) 寫哪個buffer &#xff1b;寫地址 進而控制ip的寫使能 通過狀態緩存來跳轉buffer的…

微服務八股(自用)

微服務 SpringCloud 注冊中心&#xff1a;Eureka 負載均衡&#xff1a;Ribbon 遠程調用&#xff1a;Feign 服務熔斷&#xff1a;Hystrix 網關&#xff1a;Gateway/Zuul Alibaba 配置中心&#xff1a;Nacos 負載均衡&#xff1a;Ribbon 服務調用&#xff1a;Feign 服務…

ESP32_IDF_OTA_HTTP升級固件

ESP32_IDF_OTA_HTTP升級固件 前言&#xff1a;一個項目的主控使用的是ESP32&#xff0c;因為封裝外殼的原因&#xff0c;所以需要采用OTA的方式進行升級&#xff0c;因為之前有對WIFI的OTA有所了解&#xff0c;所以在此基礎上&#xff0c;使用官方提供的native_ota_example例程…

MySQL表結構化:數據類型與表生命周期詳解

引言 各位數據庫學習者大家好&#xff01;今天我們將深入探討MySQL中最核心的對象——表&#xff08;Table&#xff09;的各類操作 &#x1f3af;。表是存儲數據的基石&#xff0c;就像Excel中的工作表一樣&#xff0c;但功能要強大得多&#xff01;無論是電商網站的用戶信息&…

React中的狀態管理Dva總結

在 React 開發中&#xff0c;隨著應用的復雜度增加&#xff0c;如何高效地管理應用狀態成為了一個非常重要的問題。為了解決這一問題&#xff0c;很多開發者選擇了 Redux&#xff0c;然而 Redux 的學習曲線較陡&#xff0c;且需要配置較多的樣板代碼。為此&#xff0c;Ant Desi…

數據結構中的高級排序算法

希爾排序 你可以將希爾排序理解成——先通過幾次分組的、較小的組間插入排序將原數組變得有序&#xff0c;最后再進行一次序列基本有序的完整插入排序。 #include <stdio.h>#define ARR_LEN(arr) (sizeof(arr) / sizeof(arr[0]))void print_arr(int arr[], int len) {for…

計算機視覺最不卷的方向:三維重建學習路線梳理

提到計算機視覺&#xff08;CV&#xff09;&#xff0c;大多數人腦海中會立馬浮現出一個字&#xff1a;“卷”。卷到什么程度呢&#xff1f;2022年秋招CV工程師崗位數下降了16%&#xff0c;但求職人數增加了23%&#xff0c;求職人數與招聘崗位的比例達到了恐怖的15:1&#xff0…