skynet 配置中lua服务创建流程

2024-01-03 07:20

本文主要是介绍skynet 配置中lua服务创建流程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

众所周知,skynet必须配置启动脚本,比如说如下配置

thread=8
logger=nil
harbor=0
start="main"
lua_path="./skynet/lualib/?.lua;./skynet/lualib/?/init.lua;"
luaservice="./skynet/service/?.lua;./app/?.lua;"
lualoader="./skynet/lualib/loader.lua"
cpath="./skynet/cservice/?.so"
lua_cpath="./skynet/luaclib/?.so"

那么 start=“main” 就是skynet启动的第一个业务脚本。那么这个脚本启动的流程是什么样的呢?
首先需要通过 c服务 snlua去加载 loader.lua 脚本,传递给 loader.lua脚本的参数为 bootstrap。

void 
skynet_start(struct skynet_config * config) {...bootstrap(ctx, config->bootstrap);...
}

由默认配置可知, config->bootstrap = “snlua bootstrap”

static void
bootstrap(struct skynet_context * logger, const char * cmdline) {int sz = strlen(cmdline);char name[sz+1];char args[sz+1];sscanf(cmdline, "%s %s", name, args);struct skynet_context *ctx = skynet_context_new(name, args);...
}

这里 name = “snlua",args = “bootstrap”,snlua就是一个c服务,编译成了 snlua.so,可在cservice文件夹中查看到,具体实现在 service_snlua.c中。下面来看看skynet_context_new(…)做了什么

struct skynet_context * 
skynet_context_new(const char * name, const char *param) {struct skynet_module * mod = skynet_module_query(name);void *inst = skynet_module_instance_create(mod);if (inst == NULL)return NULL;struct skynet_context * ctx = skynet_malloc(sizeof(*ctx));CHECKCALLING_INIT(ctx)ctx->mod = mod;ctx->instance = inst;ctx->ref = 2;ctx->cb = NULL;ctx->cb_ud = NULL;ctx->session_id = 0;// Should set to 0 first to avoid skynet_handle_retireall get an uninitialized handlectx->handle = 0;	ctx->handle = skynet_handle_register(ctx);struct message_queue * queue = ctx->queue = skynet_mq_create(ctx->handle);// init function maybe use ctx->handle, so it must init at lastcontext_inc();CHECKCALLING_BEGIN(ctx)int r = skynet_module_instance_init(mod, inst, ctx, param);...
}
  1. skynet_module_instance_create(mod) 会调用到 service_snlua.c中的 snlua_create()创建一个 snlua对象,这个对象上有一个 Luastate对象
struct snlua *
snlua_create(void) {struct snlua * l = skynet_malloc(sizeof(*l));memset(l,0,sizeof(*l));l->mem_report = MEMORY_WARNING_REPORT;l->mem_limit = 0;l->L = lua_newstate(lalloc, l);return l;
}
  1. skynet_module_instance_init(mod, inst, ctx, param) 会调用到 service_snlua.c中的 snlua_init()函数,然后将 launch_cb 设置为回调,并向自己发了一条消息,工作线程检测到有消息时,会触发到这个回调
int
snlua_init(struct snlua *l, struct skynet_context *ctx, const char * args) {int sz = strlen(args);//在内存中准备一个空间(动态内存分配)char * tmp = skynet_malloc(sz);memcpy(tmp, args, sz);//注册回调函数为launch_cb这个函数,有消息传入时会调用回调函数并处理skynet_callback(ctx, l , launch_cb);const char * self = skynet_command(ctx, "REG", NULL);uint32_t handle_id = strtoul(self+1, NULL, 16);// it must be first message// 给自己发送一条消息,内容为args字符串skynet_send(ctx, 0, handle_id, PTYPE_TAG_DONTCOPY,0, tmp, sz);return 0;
}
  1. 看看这个回调如何处理,调用到 init_cb函数,准备去调用lua脚本
static int
launch_cb(struct skynet_context * context, void *ud, int type, int session, uint32_t source , const void * msg, size_t sz) {assert(type == 0 && session == 0);struct snlua *l = ud;//将服务原本绑定的句柄和回调函数清空skynet_callback(context, NULL, NULL);//设置各项资源路径参数,并加载loader.luaint err = init_cb(l, context, msg, sz);if (err) {skynet_command(context, "EXIT", NULL);}return 0;
}
  1. init_cb 将诸多变量设置到全局,并编译执行 loader.lua脚本
static int //初始化 cb
init_cb(struct snlua *l, struct skynet_context *ctx, const char * args, size_t sz) {...const char * loader = optstring(ctx, "lualoader", "./lualib/loader.lua");//编译 loader.luaint r = luaL_loadfile(L,loader);...lua_pushlstring(L, args, sz);//执行 loader.lua 并将参数 bootstrap 传入r = lua_pcall(L,1,0,1);...return 0;
}

由上面代码可以,首先会编译 loader.lua脚本,然后执行,并传入 bootstrap
5. 看看 loader.lua脚本怎么调用到 bootstrap.lua 的

local args = {}
for word in string.gmatch(..., "%S+") dotable.insert(args, word)print("word:", word)
endSERVICE_NAME = args[1]
local main, patternlocal err = {}
for pat in string.gmatch(LUA_SERVICE, "([^;]+);*") dolocal filename = string.gsub(pat, "?", SERVICE_NAME)local f, msg = loadfile(filename)if not f thentable.insert(err, msg)elsepattern = patmain = fbreakend
end...main(select(2, table.unpack(args)))

此时可以知道,会调用到 bootstrap.lua中
6. bootstrap.lua 中会创建多个 lua服务,包括 main,创建完毕后将自己退出

local skynet = require "skynet"
local harbor = require "skynet.harbor"
require "skynet.manager"	-- import skynet.launch, ...
local memory = require "skynet.memory"skynet.start(function()...local launcher = assert(skynet.launch("snlua","launcher"))skynet.name(".launcher", launcher)skynet.newservice "service_mgr"pcall(skynet.newservice,skynet.getenv "start" or "main")skynet.exit()
end)

到这里可以看出,先启动了一个 launcher服务,然后会让这个 launcher服务启动第一个业务服务,也就是配置中的 start服务
具体流程如下:
launcher 服务的创建,在文件 manager.lua中可以看到 调用到底层接口,追下去可知,调用了 cmd_launch 接口

function skynet.launch(...)local addr = c.command("LAUNCH", table.concat({...}," "))if addr thenreturn tonumber("0x" .. string.sub(addr , 2))end
end
static struct command_func cmd_funcs[] = {...{ "LAUNCH", cmd_launch },...{ NULL, NULL },
};static const char *
cmd_launch(struct skynet_context * context, const char * param) {size_t sz = strlen(param);char tmp[sz+1];strcpy(tmp,param);char * args = tmp;char * mod = strsep(&args, " \t\r\n");args = strsep(&args, "\r\n");struct skynet_context * inst = skynet_context_new(mod,args);if (inst == NULL) {return NULL;} else {id_to_hex(context->result, inst->handle);return context->result;}
}

由上述可知,创建了一个 actor,同样依靠 loader.lua脚本,参数为 launcher,调用 launcher.lua脚本。
而创建 main服务通过如下方式调用

skynet.newservice,skynet.getenv "start" or "main"function skynet.newservice(name, ...)return skynet.call(".launcher", "lua" , "LAUNCH", "snlua", name, ...)
endfunction skynet.call(addr, typename, ...)...local p = proto[typename]local session = c.send(addr, p.id , nil , p.pack(...))if session == nil thenerror("call to invalid address " .. skynet.address(addr))endreturn p.unpack(yield_call(addr, session))
end

上述代码可知,向 launcher服务发送一个消息,消息类型为 PTYPE_LUA,然后会被 launcher服务接收到,然后调用到 launcher.lua 中的 command.LAUNCH 接口,然后以同时的 skynet.launch(service, param)接口 创建 main.lua服务。

这篇关于skynet 配置中lua服务创建流程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/564915

相关文章

Java使用ANTLR4对Lua脚本语法校验详解

《Java使用ANTLR4对Lua脚本语法校验详解》ANTLR是一个强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件,下面就跟随小编一起看看Java如何使用ANTLR4对Lua脚本... 目录什么是ANTLR?第一个例子ANTLR4 的工作流程Lua脚本语法校验准备一个Lua Gramm

Linux内核参数配置与验证详细指南

《Linux内核参数配置与验证详细指南》在Linux系统运维和性能优化中,内核参数(sysctl)的配置至关重要,本文主要来聊聊如何配置与验证这些Linux内核参数,希望对大家有一定的帮助... 目录1. 引言2. 内核参数的作用3. 如何设置内核参数3.1 临时设置(重启失效)3.2 永久设置(重启仍生效

IDEA自动生成注释模板的配置教程

《IDEA自动生成注释模板的配置教程》本文介绍了如何在IntelliJIDEA中配置类和方法的注释模板,包括自动生成项目名称、包名、日期和时间等内容,以及如何定制参数和返回值的注释格式,需要的朋友可以... 目录项目场景配置方法类注释模板定义类开头的注释步骤类注释效果方法注释模板定义方法开头的注释步骤方法注

如何在Mac上安装并配置JDK环境变量详细步骤

《如何在Mac上安装并配置JDK环境变量详细步骤》:本文主要介绍如何在Mac上安装并配置JDK环境变量详细步骤,包括下载JDK、安装JDK、配置环境变量、验证JDK配置以及可选地设置PowerSh... 目录步骤 1:下载JDK步骤 2:安装JDK步骤 3:配置环境变量1. 编辑~/.zshrc(对于zsh

售价599元起! 华为路由器X1/Pro发布 配置与区别一览

《售价599元起!华为路由器X1/Pro发布配置与区别一览》华为路由器X1/Pro发布,有朋友留言问华为路由X1和X1Pro怎么选择,关于这个问题,本期图文将对这二款路由器做了期参数对比,大家看... 华为路由 X1 系列已经正式发布并开启预售,将在 4 月 25 日 10:08 正式开售,两款产品分别为华

SQL server配置管理器找不到如何打开它

《SQLserver配置管理器找不到如何打开它》最近遇到了SQLserver配置管理器打不开的问题,尝试在开始菜单栏搜SQLServerManager无果,于是将自己找到的方法总结分享给大家,对SQ... 目录方法一:桌面图标进入方法二:运行窗口进入方法三:查找文件路径方法四:检查 SQL Server 安

Python Transformer 库安装配置及使用方法

《PythonTransformer库安装配置及使用方法》HuggingFaceTransformers是自然语言处理(NLP)领域最流行的开源库之一,支持基于Transformer架构的预训练模... 目录python 中的 Transformer 库及使用方法一、库的概述二、安装与配置三、基础使用:Pi

使用Node.js制作图片上传服务的详细教程

《使用Node.js制作图片上传服务的详细教程》在现代Web应用开发中,图片上传是一项常见且重要的功能,借助Node.js强大的生态系统,我们可以轻松搭建高效的图片上传服务,本文将深入探讨如何使用No... 目录准备工作搭建 Express 服务器配置 multer 进行图片上传处理图片上传请求完整代码示例

Spring LDAP目录服务的使用示例

《SpringLDAP目录服务的使用示例》本文主要介绍了SpringLDAP目录服务的使用示例... 目录引言一、Spring LDAP基础二、LdapTemplate详解三、LDAP对象映射四、基本LDAP操作4.1 查询操作4.2 添加操作4.3 修改操作4.4 删除操作五、认证与授权六、高级特性与最佳

SpringQuartz定时任务核心组件JobDetail与Trigger配置

《SpringQuartz定时任务核心组件JobDetail与Trigger配置》Spring框架与Quartz调度器的集成提供了强大而灵活的定时任务解决方案,本文主要介绍了SpringQuartz定... 目录引言一、Spring Quartz基础架构1.1 核心组件概述1.2 Spring集成优势二、J