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

相关文章

SpringBoot中HTTP连接池的配置与优化

《SpringBoot中HTTP连接池的配置与优化》这篇文章主要为大家详细介绍了SpringBoot中HTTP连接池的配置与优化的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一... 目录一、HTTP连接池的核心价值二、Spring Boot集成方案方案1:Apache HttpCl

Maven 插件配置分层架构深度解析

《Maven插件配置分层架构深度解析》:本文主要介绍Maven插件配置分层架构深度解析,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录Maven 插件配置分层架构深度解析引言:当构建逻辑遇上复杂配置第一章 Maven插件配置的三重境界1.1 插件配置的拓扑

Spring Boot集成Logback终极指南之从基础到高级配置实战指南

《SpringBoot集成Logback终极指南之从基础到高级配置实战指南》Logback是一个可靠、通用且快速的Java日志框架,作为Log4j的继承者,由Log4j创始人设计,:本文主要介绍... 目录一、Logback简介与Spring Boot集成基础1.1 Logback是什么?1.2 Sprin

Java对接Dify API接口的完整流程

《Java对接DifyAPI接口的完整流程》Dify是一款AI应用开发平台,提供多种自然语言处理能力,通过调用Dify开放API,开发者可以快速集成智能对话、文本生成等功能到自己的Java应用中,本... 目录Java对接Dify API接口完整指南一、Dify API简介二、准备工作三、基础对接实现1.

VSCode中配置node.js的实现示例

《VSCode中配置node.js的实现示例》本文主要介绍了VSCode中配置node.js的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着... 目录一.node.js下载安装教程二.配置npm三.配置环境变量四.VSCode配置五.心得一.no

Java 如何创建和使用ExecutorService

《Java如何创建和使用ExecutorService》ExecutorService是Java中用来管理和执行多线程任务的一种高级工具,可以有效地管理线程的生命周期和任务的执行过程,特别是在需要处... 目录一、什么是ExecutorService?二、ExecutorService的核心功能三、如何创建

Gradle在国内配置镜像加速的实现步骤

《Gradle在国内配置镜像加速的实现步骤》在国内使用Gradle构建项目时,最大的痛点就是依赖下载贼慢,甚至卡死,下面教你如何配置国内镜像加速Gradle下载依赖,主要是通过改写repositori... 目录引言一、修改 build.gradle 或 settings.gradle 的 reposito

使用easy connect之后,maven无法使用,原来需要配置-Djava.net.preferIPv4Stack=true问题

《使用easyconnect之后,maven无法使用,原来需要配置-Djava.net.preferIPv4Stack=true问题》:本文主要介绍使用easyconnect之后,maven无法... 目录使用easGWowCy connect之后,maven无法使用,原来需要配置-DJava.net.pr

史上最全nginx详细参数配置

《史上最全nginx详细参数配置》Nginx是一个轻量级高性能的HTTP和反向代理服务器,同时也是一个通用代理服务器(TCP/UDP/IMAP/POP3/SMTP),最初由俄罗斯人IgorSyso... 目录基本命令默认配置搭建站点根据文件类型设置过期时间禁止文件缓存防盗链静态文件压缩指定定错误页面跨域问题

nginx负载均衡及详细配置方法

《nginx负载均衡及详细配置方法》Nginx作为一种高效的Web服务器和反向代理服务器,广泛应用于网站的负载均衡中,:本文主要介绍nginx负载均衡及详细配置,需要的朋友可以参考下... 目录一、 nginx负载均衡策略1.1 基本负载均衡策略1.2 第三方策略1.3 策略对比二、 nginx配置2.1