首页 > 其他分享 >游戏开发:rpc protocol demo

游戏开发:rpc protocol demo

时间:2024-05-27 23:12:10浏览次数:23  
标签:function protocol name demo rpc tag local compiler

看好项目的源码总是会有重复造轮子的冲动。最近对比测了下我们业务使用的数据序列化协议的性能,review了社区上几个常用库的源码,尝试做了点优化,有些想法。浅浅写了个demo,这里记录下实现的思路,供后面查阅和反思。

协议的实现分为几个部分:

schema text:协议描述文件
compiler :解析器,负责将描述文件解析成FIX驱动层语言类型的协议配置
driver:驱动层,维护编译器生成的协议配置,具备查询、编码解码以及热更新等能力
adapter:适配器,提供多种编程语言链接驱动层的API

  1. 协议描述文件(schema text)
# TAG:
#   @TYPE:struct definition
#   @RPC:rpc definition

TYPE 1001 tinyuser [[
    int id = 0
    str name = 1
    bool online = 3
    list friend = 4
]]

RPC 1001 svr_view_tinyuser [[
    request [[
        int id = 0
    ]]
    response [[
        # allow nesting
        TYPE 1 ret [[
            bool ok = 0
            str msg = 1
        ]]
        type ret result = 0 # use keyword `type` when field is not built in
        type tinyuser user = 1
    ]]
]]

协议格式支持:
1)通用数据格式类型;
2)自定义结构体类型;
3)结构体类型嵌套;
4)请求回应模式的定义(将请求和回应看成是单独的自定义类型)

  1. 解析器(compiler)

解析器将描述文件解析为兼容驱动层语言的配置内容(以lua为例):

local types = {
    [1001] = {
        name = "tinyuser",
        tag = 1001,
        fields = {
            { name = "id", tag = 0, tp = SERIALIZE_TYPE_INT, buildin = true },
            { name = "name", tag = 1, tp = SERIALIZE_TYPE_STR, buildin = true },
            { name = "online", tag = 2, tp = SERIALIZE_TYPE_BOOL, buildin = true },
            { name = "friend", tag = 3, tp = SERIALIZE_TYPE_LIST, buildin = true }
        }
    },
    [1001.1] = {
        name = "svr_view_tinyuser.request",
        tag = 1001.1,
        fields = {
            { name = "id", tag = 0, tp = SERIALIZE_TYPE_INT, buildin = true }
        }
    },
    [1001.2.1] = {
        name = "svr_view_tinyuser.response.ret",
        tag = 1001.2.1,
        fields = {
            { name = "ok", tag = 0, tp = SERIALIZE_TYPE_BOOL, buildin = true },
            { name = "msg", tag = 1, tp = SERIALIZE_TYPE_STR, buildin = true },

        }
    },
    [1001.2] = {
        name = "svr_view_tinyuser.response",
        tag = 1001.2,
        fields = {
            { name = "result", tag = 0, tp = "svr_view_tinyuser.response.ret", buildin = false },
            { name = "player", tag = 1, tp = "tinyuser", buildin = false }
        }
    },
}
local protocols = {
    [1001] = {
        name = "svr_view_tinyuser",
        tag = 1001,
    		nesting = true
    }
}

local group = { types, protocols }

解析器提供两种结果获取方式:1. API实时获取 2. 生成解析文件(支持加密)

function compiler.dumpproto(fname)
  	fname = string.format("%s.pb", fname)
    dump2file(fname, group)
end

function compiler.getproto()
    return group
end

解析器提供解析文件的加载(解密)接口

function compiler.loadbin(bin)
	-- ... decryption
  return proto
end

function compiler.loadproto(pbfile)
		pbfile = string.format("%s.pb", pbfile)
  	local f = assert(io.open(pbfile), "Can't open " .. pbfile)
    local bin = f:read("a")
    f:close()
  	return compiler.loadbin(bin)
end
  1. 驱动层(driver)

驱动层用于维护和驱动协议解析配置相关的信息:

local compiler = require("complier")

local host = {}

local function loadproto(schematext)
		local proto = compiler.loadproto(schematext)
    return setmetatable({_proto = proto}, {__index = host})
end

function host:queryproto(protocol)
    -- search in proto.protocols
end

function host:querytype(tpname)
    -- search in proto.types
end

local serializefuncs = { --[[ SERIALIZE_TYPE = opfunc --]] }
local unserializefuncs = { --[[ SERIALIZE_TYPE = opfunc --]] }

function host:encodeproto(tpname, data)
	-- ... encode lua table to bin by proto formation
  return bins
end

function host:decodeproto(tpname, msg)
		-- decode bin msg to lua by proto formation
    return result
end

local driver = {
    loadproto = loadproto,
  	-- ...
}

local rpc = {
  	send = function(sp, rpc_name, data)
        local typename = string.format("%s.request", rpc_name)
        return sp:encodeproto(typename, data)
    end,
    recv = function(sp, rpc_name, msg)
        local typename = string.format("%s.request", rpc_name)
        return sp:decodeproto(typename, msg)
    end,
    retsend = function(sp, rpc_name, retdata)
        local typename = string.format("%s.response", rpc_name)
        return sp:encodeproto(typename, retdata)
    end,
    retrecv = function(sp, rpc_name, retmsg)
        local typename = string.format("%s.response", rpc_name)
        return sp:decodeproto(typename, retmsg)
    end
}
  1. 适配器(adapter)
import lupa  

lua = lupa.LuaRuntime()

# load comoiler
lua.execute(loadfile(compiler))
compiler_core, compiler_rpc = lua.globals.core, lua.globals.rpc

class Adapter:
    def __init__(self, compiler_core, compiler_rpc, schematext):  
        self._ccompiler_core = compiler_core
        self._compiler_rpc = compiler_rpc
        self.proto = self._compiler_core.loadproto(schematext)
    def rpc_send():
        self._compiler_rpc.send()
    # ...

标签:function,protocol,name,demo,rpc,tag,local,compiler
From: https://www.cnblogs.com/linxx-/p/18216818

相关文章

  • 云原生周刊:K8s 上的 gRPC 名称解析和负载平衡
    开源项目推荐KrakenKraken是一个基于P2P的Docker注册表,专注于可扩展性和可用性。它专为混合云环境中的Docker镜像管理、复制和分发而设计。借助可插拔的后端支持,Kraken可以轻松集成到现有的Docker注册表设置中作为分发层。E2EFramework这个项目是一个专门用于Kube......
  • [AIGC] flink sql 消费kafka消息,然后写到mysql中的demo
    这是一个使用FlinkSQL从Kafka中消费数据并写入MySQL的示例。在这个示例中,我们将假设有一个Kafka主题“input_topic”,它产生格式为(user_id:int,item_id:int,behavior:string,timestamp:long)的数据,我们需要把这些数据写入名为"output_table"的MySQL表......
  • 使用 firewall-cmd --list-all 命令查看防火墙策略信息显示不全,缺少protocols选项
      出现这个问题的原因是我们当前Linux系统的防火墙的版本太低导致的。 需要升级一下防火墙。对于RedHat、CentOS或Fedora系统:sudoyumupdatesudoyuminstalliptables或者,如果你想使用firewalld:sudoyumupdatesudoyuminstallfirewalld 再次进行查看......
  • PTP(Precision Time Protocol)是一种用于精确时间同步的网络协议。它旨在使网络设备能够
    PTP(PrecisionTimeProtocol)是一种用于精确时间同步的网络协议。它旨在使网络设备能够以极高的精度和准确性进行时间同步,通常用于需要时间同步的应用领域,如金融交易、工业自动化和无线电通信等。PTP的工作原理是通过在网络中的主时钟和从时钟之间进行时间戳的传递和比较来实现精......
  • 详细分析crontab定时执行任务(附Demo | 定时清空Tomcat的实战)
    目录前言1.基本知识2.Demo3.实战3.1错误版本3.2正确版本前言由于用户量大,且导出的日志以及缓存特别多,急需定期删除文件1.基本知识crontab是一个用于定时执行任务的命令行工具,通常在Unix和类Unix系统中可用,表示一个包含需要定时执行的任务列表的表格......
  • IceRPC之多路复用传输>快乐的RPC
    作者引言很高兴啊,我们来到了IceRPC之多路复用传输>快乐的RPC,基础引导,打好基础,才能让自已不在迷茫,快乐的畅游世界。icerpc和多路复用传输了解icerpc协议和多路复用传输icerpc协议当创建到服务器地址icerpc://hello.zeroc.com的客户端连接时,指示IceRPC建立使用ic......
  • IceRPC之深入理解调度管道->快乐的RPC
    作者引言很高兴啊,我们来到了IceRPC之深入理解调度管道->快乐的RPC,为上篇的续篇,深入理解常见的调度类型,基础引导,有点小压力,打好基础,才能让自已不在迷茫,快乐的畅游世界。传入请求了解如何处理传入的请求接收传入的请求调度器的调度方法接受传入的请求。该传入请求是由连......
  • IceRPC之调度管道->快乐的RPC
    作者引言很高兴啊,我们来到了IceRPC之调度管道->快乐的RPC,基础引导,有点小压力,打好基础,才能让自已不在迷茫,快乐的畅游世界。调度管道Dispatchpipeline了解如何接受请求并返回响应。定义接受/完成请求,并返回响应的过程称为调度。调度通常由服务器连接创建:服务器连接,......
  • C#串口通讯 源码Demo
    在C#中进行串口通讯主要涉及到以下几个步骤:引入命名空间usingSystem.IO.Ports;创建SerialPort对象SerialPortport=newSerialPort();设置串口属性//设置串口名:port.PortName="COM1";//设置波特率:port.BaudRate=9600;//设置校验位:port.Parity=Parity.None;//......
  • LocalDateTimeDemo 日期 时间 字符串转换
    packagedemo;importjava.time.LocalDate;importjava.time.LocalDateTime;importjava.time.format.DateTimeFormatter;publicclassLocalDateTimeDemo{ publicstaticvoidmain(String[]args){ LocalDateld1=LocalDate.now(); DateTimeFormatterdtf1......