skynet.newservice简介:服务的启动

skynet是一个轻量级的游戏服务器框架。

简介

skynet的体系中,服务是一个基础概念。通常,我们使用skynet.newservice来启动一个snlua服务。
那么,当我们写下local addr = skynet.newservice("test")这行代码时,系统是怎么运作的呢?
思考一下这些问题:

  • 调用skynet.newservice会不会发生阻塞?
  • 如果test服务在skynet.start时调用了skynet.exitaddr会是什么值?
  • 如果test服务在skynet.start时出现错误,addr又会是什么值?
  • test服务是不是一定要调用skynet.start
  • 如果要传一些复杂的参数,又要怎么做?

skynet.newservice的代码实现

--skynet/lualib/skynet.lua
function skynet.newservice(name, ...)return skynet.call(".launcher", "lua" , "LAUNCH", "snlua", name, ...)
end

skynet.newservice的代码很简单,只调用了一个skynet.call,而skynet.call是阻塞的,所以skynet.newservice也是阻塞的。
这个call发了一条lua消息给.launcher服务,接下来看看.launcher服务相关的代码:

--skynet/service/launcher.lua
local function launch_service(service, ...)local param = table.concat({...}, " ")local inst = skynet.launch(service, param)local session = skynet.context()local response = skynet.response()if inst thenservices[inst] = service .. " " .. paraminstance[inst] = responselaunch_session[inst] = sessionelseresponse(false)returnendreturn inst
endfunction command.LAUNCH(_, service, ...)launch_service(service, ...)return NORET
end

这里又调用到skynet.launch,实际上是发了个LAUNCH指令到底层,这里创建了一个snlua服务:

--skynet/lualib/skynet/manager.lua
function skynet.launch(...)local addr = c.command("LAUNCH", table.concat({...}," "))if addr thenreturn tonumber(string.sub(addr , 2), 16)end
end

要注意前面的.launcher服务的command.LAUNCH函数是忽略返回的,所以此时skynet.newservice还处于阻塞状态,等待.launcher的返回。
那什么时候会返回响应呢?

回到前面的launch_service函数,可以看到skynet.launch成功后并没有直接返回,而是生成一个响应函数response,存储在表instance中。
搜索这个instance,我们可以在command.LAUNCHOK中找到它的使用:

--skynet/service/launcher.lua
function command.LAUNCHOK(address)-- init noticelocal response = instance[address]if response thenresponse(true, address)instance[address] = nillaunch_session[address] = nilendreturn NORET
end

也就是说,要等到.launcher服务收到LAUNCHOK的指令之后,才会返回给newservice的调用者。
问题又来了,什么时候发送LAUNCHOK呢?答案是在skynet.init_service中。

而调用skynet.init_service的,一共有三个函数:

  • skynet.start
  • skynet.forward_type
  • skynet.filter

所以,在服务的启动脚本中,我们必须调用这三个函数中的其中一个(通常都是skynet.start),否则的话,调用方永远都收不到返回的数据。
以在main服务中,创建新服务test为例,流程如下图所示:
skynet_service

新服务启动时,调用skynet.exit,调用者收到的addr是什么?

我们看一下skynet.exit:

--skynet/lualib/skynet.lua
function skynet.exit()fork_queue = { h = 1, t = 0 }	-- no fork coroutine can be execute after skynet.exitskynet.send(".launcher","lua","REMOVE",skynet.self(), false)--其他代码...--...
end

这里看到,新服务发送了REMOVE指令到.launcher服务,而.launcherREMOVE的处理如下:

--skynet/service/launcher.lua
function command.REMOVE(_, handle, kill)services[handle] = nillocal response = instance[handle]if response then-- instance is deadresponse(not kill)	-- return nil to caller of newservice, when kill == falseinstance[handle] = nillaunch_session[handle] = nilend-- don't return (skynet.ret) because the handle may exitreturn NORET
end

对于刚启动的服务来说,这里会调用到对应的responseresponse需要两个参数,这里第一个参数是true,第二个参数为nil,而第二个参数是返回地址,也就是说,调用者收到的addrnil值。
skynet_service_exit

新服务启动报错的话,又返回什么呢

新服务启动的时候,无论是用skynet.start还是skynet.forward_type,最终都是调用skynet.init_service,来看看代码:

--skynet/lualib/skynet.lua
function skynet.init_service(start)local function main()skynet_require.init_all()start()endlocal ok, err = xpcall(main, traceback)if not ok thenskynet.error("init service failed: " .. tostring(err))skynet.send(".launcher","lua", "ERROR")skynet.exit()elseskynet.send(".launcher","lua", "LAUNCHOK")end
end

可以看到,对start函数的调用,是通过xpcall来调用的,如果报错的话,会发送ERROR.launcher服务。

--skynet/service/launcher.lua
function command.ERROR(address)-- see serivce-src/service_lua.c-- init failedlocal response = instance[address]if response thenresponse(false)launch_session[address] = nilinstance[address] = nilendservices[address] = nilreturn NORET
end

这里response参数是falseresponseskynet.response生成的一个函数,相关代码如下:

--skynet/lualib/skynet.lua
function skynet.response(pack)--其他代码...--...local function response(ok, ...)--其他代码...--...if ok thenret = c.send(co_address, skynet.PTYPE_RESPONSE, co_session, pack(...))if ret == false then-- If the package is too large, returns false. so we should report error backc.send(co_address, skynet.PTYPE_ERROR, co_session, "")endelseret = c.send(co_address, skynet.PTYPE_ERROR, co_session, "")end--其他代码...--...end--其他代码...--...return response
end

可以看到,当传入的okfalse的时候,会发送一个PTYPE_ERROR类型的消息给调用者。
而当我们require"skynet"时,对PTYPE_ERROR默认的处理函数是_error_dispatch,具体的流程可以看看源码,这里简而言之,就是调用call的那条协程会触发一个call failerror

所以,当新服务的启动函数出错时,在新服务中会报错,中断,而调用者在skynet.call()中也会报call fail的错,从而中断执行,也就不会有addr的返回了。
skynet_service_error

如果启动服务要传比较复杂的参数,要怎么做比较好

skynet.newservice(service_name, ...)后面是可以带多个参数的,但这些参数只能是数字或字符串,回看前面的skynet.launch的代码,里面是调用了c.command("LAUNCH", table.concat({...}," ")),这里可以看到,传递的参数通过table.concat打包成字符串,以空格隔开。如果我们的参数中带有空格,或者我们想要传个table,那就不支持了。
通常来说,我们可以先启动服务,在skynet.start中做些简单的功能,调用skynet.dispatch("lua", ...)来处理lua消息,通过lua消息来做初始化,这样就能传送复杂的参数了:

local addr  = skynet.newservice("test")
skynet.send(addr, "lua", "start", {address='0.0.0.0',port=8888,nodelay=true})

总结

现在,我们可以回答最初的问题了:

  • 调用skynet.newservice会不会发生阻塞?

    • 会阻塞,如果服务没启动完,会一直等待下去。
  • 如果test服务在start时调用了exitaddr会是什么值?

    • nil
  • 如果test服务在start时出现错误,addr又会是什么值?

    • skynet.newservice会报错,没有返回值
  • test服务是不是一定要调用skynet.start

    • 不一定,也可以调用skynet.forward_typeskynet.filter
  • 如果要传一些复杂的参数,又要怎么做?

    • 将服务的创建和启动分开,创建后发送lua消息初始化服务。

最后再思考一个问题:启动系统的时候,第一个服务又是什么时候启动的呢?答案可以看看这里:skynet 之 main 服务的启动

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/331655.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Java Web】前端利用 form 表单传多项数据,后端 Servlet 取出的各项数据均为空

前端利用 form 表单传多项数据,后端 Servlet 取出的各项数据均为空 文章目录 1.问题引入2.问题解决 1.问题引入 最近在写一个 java web 项目时,遇到一个让我头疼了一下午的问题:前端通过 post 提交的 form 表单数据可以传到后端&#xff0c…

Windows远程连接命令?

Windows操作系统提供了多种远程连接命令,使用户可以通过网络连接到远程计算机,并在远程操作系统上执行操作。远程连接命令可方便实现远程工作、故障排查和系统维护等任务。本文将介绍几种常见的Windows远程连接命令及其基本使用方法。 远程连接命令 Win…

心链2---前端开发(整合路由,搜索页面,用户信息页开发)

心链——伙伴匹配系统 接口调试 说书人📖:上回书说到用了两种方法查询标签1.SQL查询,2.内存查询;两种查询效率是部分上下,打的是难解难分,是时大地皴裂,天色聚变,老祖斟酌再三最后决…

仪器校准中移液器的使用规范,应当注意哪些细节?

校准行业中,移液器的使用是非常多的,尤其是理化室,经常需要借助到移液器来校准。作为常规的溶液定量转移器具,其在校准过程中的使用也需要遵守规范,既是保证校准结果准确低误差,也是为了规范实验室校准人员…

2024.5.25晚训题解

这套题挺简单的。。。 A题 AC率差不多100% B题 AC率差不多75% C题 AC率也差不多75% D题 AC率 50% E题 AC率 25% 向着top 1%出发 A题题解 Stair, Peak, or Neither? 简单判断题&#xff0c;自己写 #include<bits/stdc.h> using namespace std; int A[5]; int main() {…

llama-factory学习个人记录

框架、模型、数据集准备 1.llama-factory部署 # 克隆仓库 git clone https://github.com/hiyouga/LLaMA-Factory.git # 创建虚拟环境 conda create --name llama_factory python3.10 # 激活虚拟环境 conda activate llama_factory # 安装依赖 cd LLaMA-Factory pip install -…

线性回归模型

目录 1.概述 2.线性回归模型的定义 3.线性回归模型的优缺点 4.线性回归模型的应用场景 5.线性回归模型的未来展望 6.小结 1.概述 线性回归是一种广泛应用于统计学和机器学习的技术&#xff0c;用于研究两个或多个变量之间的线性关系。在本文中&#xff0c;我们将深入探讨…

会声会影调速怎么用 会声会影如何调整音频速度

会声会影是一款功能强大的视频编辑软件&#xff0c;可以帮助我们轻松的实现剪辑。 会声会影的操作简单易懂&#xff0c;界面简洁明快。适合家庭使用&#xff0c; 我们使用会声会影可以在家就能将视频剪辑成好莱坞大片。但是在使用的过程中&#xff0c;仍然会遇到一些操作上的问…

微信小程序预览图片和H5使用canvas实现图片+蒙层+文字

1、效果 2.H5实现 <!--* Author: limingfang* Date: 2024-05-20 10:26:51* LastEditors: limingfang* LastEditTime: 2024-05-21 16:31:11* Description: --> <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8&q…

【小程序 按钮 表单 】

按钮 代码演示 xxx.wxml <view class"boss" hover-class"box"hover-start-time"2000"hover-stay-time"5000">测试文本<view hover-stop-propagation"true">子集</view><view>子集2</view>…

Capture One Studio for Mac:打造完美影像的利器

对于摄影师而言&#xff0c;每一次按下快门都是一次对完美影像的追求。而Capture One Studio for Mac正是这样一款能够帮助你实现这一追求的利器。 Capture One Studio for Mac v16.4.2.1中文直装版下载 首先&#xff0c;Capture One Studio for Mac拥有出色的图像处理能力。它…

Java—二分查找

介绍 二分查找&#xff08;Binary Search&#xff09;是一种在有序数组中查找特定元素的搜索算法。其基本思想是将目标值与数组中间的元素进行比较&#xff1a; 如果目标值等于中间元素&#xff0c;则查找成功。如果目标值小于中间元素&#xff0c;则在数组左半部分继续进行二…

2024年汉字小达人活动4个多月开赛:18道历年选择题和答案、解析

根据近年的安排&#xff0c;2024年第11届汉字小达人比赛还有4个多月就启动&#xff0c;那么孩子们如何利用这段时间有条不紊地备考呢&#xff1f;我的建议是两手准备&#xff1a;①把小学1-5年级的语文课本上的知识点熟悉&#xff0c;重点是字、词、成语、古诗。②把历年真题刷…

VTK 数据处理:特征边提取

VTK 数据处理&#xff1a;特征边提取 VTK 数据处理&#xff1a;特征边提取原理实例 1&#xff1a;边界边提取实例 2&#xff1a;模型特征边提取实例 3&#xff1a;利用 vtkFeatureEdges 提取的边界补洞实例 4&#xff1a;利用 vtkFillHolesFilter 补洞 VTK 数据处理&#xff1a…

全局平均池化笔记

全局平均池化&#xff08;Global Average Pooling, GAP&#xff09;是一种用于卷积神经网络&#xff08;CNN&#xff09;中的池化操作&#xff0c;其主要作用和优点包括&#xff1a; 减少参数数量&#xff1a;全局平均池化层将每个特征图通过取其所有元素的平均值&#xff0c;压…

初识Spring Boot

初识Spring Boot SpringBoot是建立在Spring框架之上的一个项目,它的目标是简化Spring应用程序的初始搭建以及开发过程。 对比Spring Spring Boot作为Spring框架的一个模块&#xff0c;旨在简化Spring应用程序的初始搭建和开发过程&#xff0c;以下是Spring Boot相对于传统Spri…

[datawhale202405]从零手搓大模型实战:TinyAgent

结论速递 TinyAgent项目实现了一个简单的Agent智能体&#xff0c;主要是实现了ReAct策略&#xff08;推理调用工具的能力&#xff09;&#xff0c;及封装了一个Tool。 项目实现有一定的疏漏。为了正确运行代码&#xff0c;本次对代码Agent部分进行了简单修改&#xff08;完善…

【Linux】Linux的安装

文章目录 一、Linux环境的安装虚拟机 镜像文件云服务器&#xff08;可能需要花钱&#xff09; 未完待续 一、Linux环境的安装 我们往后的学习用的Linux版本为——CentOs 7 &#xff0c;使用 Ubuntu 也可以 。这里提供几个安装方法&#xff1a; 电脑安装双系统&#xff08;不…

关于burp的intruder返回包空白问题

记录一下被自己蠢笑的问题 burp返回包为空怎么办&#xff0c;在查询无果后经过多次试验&#xff0c;确实没有效果 看那三个点还以为加载呢&#xff0c;攻击完了怎么一个显示没有 于是…… 鼠标到三个点&#xff0c;往下一拉 哈哈哈哈哈哈哈&#xff0c;真是被自己给蠢到了

基于地理坐标的高阶几何编辑工具算法(2)——相交面裁剪

文章目录 工具步骤应用场景算法输入算法输出算法示意图算法原理后处理 工具步骤 选中一个需要裁剪的面&#xff0c;点击“相交面裁剪”工具&#xff0c;多选裁剪模板面&#xff0c;空格执行。 应用场景 常用于基于遥感影像的建筑物几何面编辑。 算法输入 一个待裁剪的面&a…