【从零开始学Skynet】实战篇《球球大作战》(三):封装常用的API

news2024/9/25 23:19:02

        为什么要封装?封装可以减少一些重复代码,提高我们的工作效率。

1、定义属性

        新建文件lualib/service.lua,定义模块的属性, service模块是对Skynet服务的一种封装,代码如下所示:

local skynet = require "skynet"
local cluster = require "skynet.cluster"
local M = {
    --类型和id
    name = "",
    id = 0,
    --回调函数
    exit = nil,
    init = nil,
    --分发方法
    resp = {},
}
return M
  • name代表服务的类型id代表服务编号。

        如下图中的gateway1,它的namegatewayid1;对于agentmgr,它的nameagentmgrid0(全局唯一)

  • initexit是回调方法,在服务初始化和退出时会被调用(本篇暂不实现exit的功能)。
  • resp表会存放着消息处理方法。

 2、启动逻辑

        给service模块添加如下所示的start方法,用于开启服务。当外部调用start方法时,它先给nameid赋值,再调用skynet.start开启服务。服务启动后,Skynet会调用init方法,由它调用skynet.dispatch实现消息的路由,再调用上层的M.init()

function init()
    skynet.dispatch("lua", dispatch)
    if M.init then
        M.init()
    end
end

function M.start(name, id, ...)
    M.name = name
    M.id = tonumber(id)
    skynet.start(init)
end

        调用流程如下图所示,从服务脚本调用s.start开始(图中阶段①),一直到服务脚本的init方法被调用(图中阶段⑤)。图中的服务脚本是服务的Lua代码,封装层代表service模块,skynet代表Skynet原生API

3、消息分发 

         消息分发方法dispatch如下代码所示,它会查找消息方法表resp[cmd],如果没定义处理方法(if not fun then),则直接返回;如果定义了处理方法,使用xpcall安全地调用处理方法,再根据返回值做出不同处理。如果返回值为空(if not isok then),则会直接返回,否则把返回结果发回给发送方(skynet.retpack)。

function traceback(err)
	skynet.error(tostring(err))
	skynet.error(debug.traceback())
end

local dispatch = function(session, address, cmd, ...)
	local fun = M.resp[cmd]
	if not fun then
		skynet.ret()
		return
	end
	
	local ret = table.pack(xpcall(fun, traceback, address, ...))
	local isok = ret[1]
	
	if not isok then
		skynet.ret()
		return
	end

	skynet.retpack(table.unpack(ret,2))
end
参数说明
address代表消息发送方
cmd代表消息名的字符串
fun消息处理方法
xpcall

安全的调用fun方法:

        如果fun方法报错,程序不会中断,而是会把错误信息转交给第2个参数的traceback

        如果程序报错,xpcall会返回false

        如果程序正常执行,xpcall返回的第一个值为true,从第2个值开始才是fun的返回值。xpcall会把第3个及后面的参数传给fun,即fun的第1参数是address,从第2个参数开始是可变参数“...”。

traceback作为xpcall的第2个参数,功能是打印出错误提示和堆栈
ret

xpcall返回值的打包:

        如果fun方法报错,那ret[1]将是false,否则为true;

        如果为false,调用skynet.ret()直接返回。

skynet.retpackfun方法的真正返回值从ret[2]开始,用table.unpack解出ret[2]、ret[3]……,并返回给发送方。

调用流程如下图所示:

         在阶段①,login1agentmgr发送reqlogin请求,agentmgr收到后,经由Skynet调用封装层(即service模块,阶段②),再调用服务脚本的s.resp.reqlogin实现分发(阶段③)。图中reqlogin处理完消息后,返回true(阶段④),返回值经由封装层(阶段⑤)最终发回给login1(阶段⑦)。

4、辅助方法

        service模块还会提供一些辅助方法,以减少服务脚本的代码量。封装了callsend方法,用于抹平节点差异(在理解节点间通信代价后使用)。代码如下所示:

function M.call(node, srv, ...)
	local mynode = skynet.getenv("node")
	if node == mynode then
		return skynet.call(srv, "lua", ...)
	else
		return cluster.call(node, srv, ...)
	end
end

function M.send(node, srv, ...)
	local mynode = skynet.getenv("node")
	if node == mynode then
		return skynet.send(srv, "lua", ...)
	else
		return cluster.send(node, srv, ...)
	end
end

        参数node代表接收方所在的节点,srv代表接收方的服务名。程序先用skynet.getenv获取当前节点,如果接收方在同个节点,则调用skynet.call;如果在不同节点,则调用cluster.call

 5、编写空服务

        (1)现在试一试使用刚刚完成的service模块写一个空的gateway服务,并启动它。新建
sevice/gateway/init.lua,编写如下所示的代码:

local skynet = require "skynet"
local s = require "service"
function s.init()
    skynet.error("[start]" .. s.name .. " " .. s.id)
end

s.start(...)

        s.start(...)中的“...”代表可变参数,在用skynet.newservice启动服务时,可以传递参数给它。service模块将会把第1个参数赋值给s.name,第2个参数赋值给s.id。空服务没有任何功能,仅在启动时打印一条日志。

         (2)修改service/main.lua,创建一个gateway服务,代码如下所示:

local skynet = require "skynet"

skynet.start(function()
    --初始化
    skynet.error("[start main]")
    skynet.newservice("gateway", "gateway", 1)

    --退出自身
    skynet.exit()
end)

         skynet.newservice("gateway", "gateway", 1)的第1个参数gateway代表着要启动的服务类型,第2个和第3个参数则会被传进s.start(...)的可变参数。

(3)运行结果如下图所示,节点先启动主服务main,main启动gateway1,最后main服务调用skynet.exit()退出。

完整代码地址:https://gitee.com/frank-yangyu/ball-server

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux 下编译 thrift

thrift编译需要依赖 openssl,首先按照文章《Openssl在Linux下编译/交叉编译》编译openssl。 网上有文章说thrift编译还需要依赖Boost,libevent,但是我发现不依赖这两个库也能把thrift编译出来。在 https://github.com/apache/thrift/releases…

R -- 二分类问题的分类+预测

brief 分类大致分为有监督分类和无监督分类,这里学习有监督分类。有监督分类一般包括逻辑回归、决策树、随机森林、支持向量机、神经网络等。 有监督学习基于一组包含预测变量值和输出变量值的样本单元。然后可以将全部数据分为一个训练数据集和一个验证数据集&…

【好刊推荐】知名出版社影响因子7+被踢出SCI,投稿前如何选期刊?

今年3月Hindawi旗下的19本期刊被SCIE剔除,其中有一本影响因子7,以下从期刊各个指标方面分析一下具体原因: 期刊剔除:影响因子7 期刊简介 期刊名称: OXIDATIVE MEDICINE AND CELLULAR LONGEVITY ISSN / eISSN&#…

Stacking算法预测银行客户流失率

Stacking算法预测银行客户流失率 描述 为了防止银行的客户流失,通过数据分析,识别并可视化哪些因素导致了客户流失,并通过建立一个预测模型,识别客户是否会流失,流失的概率有多大。以便银行的客户服务部门更加有针对…

Android桌面长按图标快捷方式——Shortcuts

简介 当我们在长按Android应用的桌面图标时,一般回弹出一个列表,上面一般有应用信息、卸载应用等功能,并且部分应用在这里还添加了自己的快捷方式,今天主要介绍如何添加自定义的快捷方式。 长按桌面显示的快捷方式在Android中叫…

中小企业面临怎样的数字化转型局面

当前,我国经济长期向好的基本面没有改变,但承受着“需求收缩、供给冲击、预期减弱”的三重压力,中小企业的数字化转型之路较之以往更加艰难、曲折。为帮助中小企业纾困解难、平稳渡过危机,需进一步优化政策“组合拳”,…

单片机中常用的轻量级校验算法

UART有一个奇偶校验,CAN通信有CRC校验。Modbus、MAVlink、USB等通信协议也有校验信息。 在自定义数据存储时,有经验的工程师都会添加一定校验信息。 你平时通信,或者数据存储时,你有用到校验信息吗?下面就介绍几种常见…

Java面试题总结 | Java面试题总结3-JVM模块(持续更新)

JVM 文章目录JVMJVM的内存组成模型java的内存模型定义了什么java的内存分布情况程序计数器是什么?堆、栈、方法区都存放的是什么堆和栈的区别类加载JMM主内存和本地内存交互操作volatile如何保证可见性volatile如何保证有序性happen-before了解过吗?内存…

【JS】BOM 详解(工作必备)

文章目录BOM一、History (浏览器记录)1.1、history.go(指定页)1.2、history.back(上一页)1.3、history.forword(下一页)二、Location(浏览器地址)2.1、操作属…

基于OpenCV的图片和视频人脸识别

目录 🥩前言 🍖环境使用 🍖模块使用 🍖模块介绍 🍖模块安装问题: 🥩人脸检测 🍖Haar 级联的概念 🍖获取 Haar 级联数据 🍗 1.下载所需版本 🍗 2.安…

前后端不分离项目如何使用elementUI

首先,去官网下载element 的js和css和字体等文件 其次,分别将js和css 引入到项目 然后就可以使用了,使用方法和vue中使用element方法一致、

5款最新最实用的小软件,让你的工作和生活更轻松

我喜欢发现和分享一些好用的软件,我觉得它们可以让我们的工作和生活更加轻松和快乐。今天给大家介绍五款我最近发现的软件, GIF录制工具——Screen To Gif Screen To Gif是一款完全免费的GIF录制神器,可以让你轻松地录制屏幕、摄像头或画板…

学生信息管理案例

效果图: 业务模块: 点击录入按钮可以录入数据点击删除可以删除当前的数据 注意:本次案例,我们尽量减少dom操作,采用操作数据的形式。增加和删除都是针对数组的操作,然后根据数组数据渲染页面 核心思路:…

5款办公神器软件推荐:提高效率,享受分享

给大家分享一些优秀的软件工具,是一件让人很愉悦的事情,今天继续带来5款优质软件。 图床管理——PicGo PicGo是一款图床管理工具,可以快速上传图片到各种图床,并生成链接。你可以使用PicGo来管理你的图片资源,或者作为Markdown编…

STM32F4_定时器输入捕获详解

目录 1. 输入捕获简介 2. 输入捕获框图 3. 输入捕获模式 4. 相关寄存器 4.1 TIMx_ARR、TIMx_PSC 4.2 捕获/比较寄存器1:TIMx_CCMR1 4.3 捕获/比较使能寄存器 TIMx_CCER 4.4 中断使能寄存器 TIMx_DIER 5. 库函数配置输入捕获高电平脉冲宽度 6. 实验程序 6…

【4.14】今天讲两道TCP面试题

文章目录TCP Keepalive 和HTTP Keep-Alive是一个东西吗?TCP协议有什么缺陷TCP Keepalive 和HTTP Keep-Alive是一个东西吗? 对于这个问题,我们要先知道这两个KeepAlive分别代表什么? TCP的Keepalive是由TCP层(内核层&a…

移远通信率先通过ISO/SAE 21434汽车网络安全管理体系认证

近日,移远通信车载前装BU获得了由国际独立第三方检测、检验和认证机构TV NORD颁发的ISO/SAE 21434汽车网络安全管理体系认证证书。 ISO/SAE 21434标准认证的通过,表明移远通信车载前装BU的网络安全风险管理满足了产品从概念设计、开发、生产、运营到售后…

我的面试八股(数据库)

数据库范式? 数据库范式有三种 1NF 第一范式,属性(对应表中的字段)不能再分割,就是这个字段只能是一个值,不能再分为多个其它字段了,1NF是所有关系型数据库的最基本要求。 2NF 第二范式&am…

Spring Security 02 搭建环境

搭建环境 导入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency><groupId>org.springframework.boot</groupId><artifa…

第31天-贪心-第八章 ● 122.买卖股票的最佳时机II ● 55. 跳跃游戏 ● 45.跳跃游戏II

文章目录1. 买卖股票的最佳时机2. 跳跃游戏3. 跳跃游戏 ||1. 买卖股票的最佳时机 - LeetCode链接 给你一个整数数组 prices &#xff0c;其中 prices[i] 表示某支股票第 i 天的价格。 在每一天&#xff0c;你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 …