平台开发——skynet——⑤网关服务gate

曾经终败给现在 2023-03-03 15:28 92阅读 0赞

skynet外界通讯简介:

有两种
①是游戏客互端使用 TCP 连接接入 skynet 节点。如果你用 skynet 实现一个 web 服务器的话,游戏客户端就可以等价于一个浏览器请求。

称为:gate 服务
特征:监听一个 TCP 端口,接受连入的 TCP 连接,并把连接上获得的数据转发到 skynet 内部。
介绍:Gate 可以用来消除外部数据包和 skynet 内部消息包的不一致性。外部 TCP 流的分包问题,是 Gate实现上的约定。Gate 会接受外部连接,并把连接相关信息转发给另一个服务去处理。它自己不做数据处理是因为我们需要保持 gate实现的简洁高效。
外部信息:
①连接本身的接入和断开消息(控制信息包),
②连接上的数据包
gate三种工作模式:
①watchdog 模式 ,由gate 加上包头,同时处理控制信息和数据信息的所有数据;
②agent 模式 ,让每个 agent 处理独立连接;
③broker模式 ,由一个 broker 服务处理不同连接上的所有数据包。
①一开始,Gate无条件转发这两类消息到同一个处理服务。但对于连接数据包,添加一个包头无疑有性能上的开销。
②所以 Gate 还接收另一种工作模式:把每个不同连接上的数据包转发给不同的独立服务上。每个独立服务处理单一连接上的数据包。
③或者,我们也可以选择把不同连接上的数据包从控制信息包(建立/断开连接)中分离开,但不区分不同连接而转发给同一数据处理服务(对数据来源不敏感,只对数据内容敏感的场合)。

无论是哪种模式,控制信息都是交给 watchdog 去处理的,而数据包如果不发给 watchdog 而是发送给 agent 或 broker 的话,则不会有额外的数据头(也减少了数据拷贝)。识别这些包是从外部发送进来的方法是检查消息包的类型是否为 PTYPE_CLIENT。当然,你也可以自己定制消息类型让 gate 通知你。
注意:Gate只负责读取外部数据,但不负责回写。也就是说,向这些连接发送数据不是它的职责范畴。

②是第三方的服务,比如数据库服务,它接受一个或多个 TCP 连接。你需要从 skynet 内部建立一个 TCP 连接出去使用。

skynet网关服务简介

  1. 网关服务 (GateSever) 是游戏的接入层, 基本功能是管理客户端的连接, 分割完整的数据包, 转发给逻辑处理服务.
  2. skynet 提供了一个通用模板 lualib/snax/gateserver.lua. 同时基于 gateserver.lua, 实现了一个网关服务 gate.lua.
  3. TCP 是面向字节流的协议,我们需要把字节流流切割成数据包, 具体的方式见[分包][Link 1].

skynet网络服务器一般是用 gate / watchdog / agent 三剑客
为什么有了socket还要用这个?这一点还没完全搞明白,留作以后解决。

其中 watchdog.lua在文件夹 skynet/examples 下 gate.lua 在 skynet/service 文件夹下,gateserver.lua 在 skynet/lualib/snax 文件夹下。

我们写网关服务的时候,gate直接用example里面的,因为它已经被标准化了。
watchdog也直接用,不用修改。
只有agent服务需要自己写。

gate、watchdog、agent协作方式

  1. 首先一个连接进来,先到gategate会给watchdog发一个请求。watchdog就会启动一个agentagent启动以后会给gate发个请求forwardgate就会给连接加上agent属性。当这个连接再有数据进来的时候,还是经过gate,但是gate检查到这个连接已经有agent属性以后,数据就直接发给agent了,不会再发给watchdog

在这里插入图片描述
简化图
在这里插入图片描述

从gate基础说起

  1. skynet 提供了一个通用模板 lualib/snax/gateserver.lua 来启动一个网关服务器,通过 TCP 连接和客户端交换数据。
  2. TCP 基于数据流,但一般我们需要以带长度信息的数据包的结构来做数据交换。gateserver做的就是这个工作,把数据流切割成包的形式转发到可以处理它的地址。

gate服务基础

  1. local gateserver = require "snax.gateserver"
  2. local handler = { } --必须提供一张表,表里面定义connectmessage等相关回调函数
  3. -- 注册各种handler函数接口,比如:handler.connecthandler.disconnecthandler.message...
  4. gateserver.start(handler) --网关服务的入口函数

gate服务实例

  1. local skynet = require "skynet"
  2. local gateserver = require "snax.gateserver"
  3. local handler = { }
  4. --当一个客户端链接进来,gateserver自动处理链接,并且调用该函数,必须要有
  5. function handler.connect(fd, ipaddr)
  6. skynet.error("ipaddr:",ipaddr,"fd:",fd,"connect")
  7. gateserver.openclient(fd) --链接成功不代表马上可以读到数据,需要打开这个套接字,允许fd接收数据
  8. end
  9. --当一个客户端断开链接后调用该函数,必须要有
  10. function handler.disconnect(fd)
  11. skynet.error("fd:", fd, "disconnect")
  12. end
  13. --当fd有数据到达了,会调用这个函数,前提是fd需要调用gateserver.openclient打开
  14. function handler.message(fd, msg, sz)
  15. skynet.error("recv message from fd:", fd)
  16. end
  17. gateserver.start(handler)

启动gate服务 并 需要告诉它监听端口

  1. local gateserver = skynet.newservice("mygateserver")
  2. skynet.call(gateserver, "lua", "open", { --需要给网关服务发送open消息,来启动监听
  3. port = 8002, --监听的端口
  4. maxclient = 64, --客户端最大连接数
  5. nodelay = true, --是否延迟TCP
  6. })

关闭gate服务

  1. local skynet = require "skynet"
  2. local gateserver = ...
  3. skynet.start(function()
  4. skynet.call(gateserver, "lua", "close")
  5. skynet.exit()
  6. end)

客户端tcp连接gate监听端口
注意两点:
①snax.gateserver 基于TCP协议包装了一个两字节数据长度协议

前两个字节表示数据包的长度len(不计算这两个表示长度的字节),高字节在前低字节在后(大端序),后面紧跟len字节数的数据。

也就说,你给gate服务发消息,消息格式必须符合该协议,不然不会识别该消息!
消息打包方法:

  1. local netpack = require "skynet.netpack" --使用netpack
  2. --(发送端)打包数据str,返回一个C指针msg,sz,申请内存
  3. netpack.pack(str)
  4. --(接收端)解包数据,返回一个lua的字符串,会释放内存
  5. netpack.tostring(msg, sz)

客户端实例

  1. local skynet = require "skynet"
  2. local socket = require "skynet.socket"
  3. local netpack = require "skynet.netpack"
  4. skynet.start(function()
  5. local addr = "127.0.0.1:8002"
  6. skynet.error("connect ".. addr)
  7. local id = socket.open(addr)
  8. assert(id)
  9. socket.write(id, netpack.pack("Hello world"))
  10. end)

介绍gete服务的那些handle们

详细代码参考

  1. --当一个新客户端被accept后,connect 方法会被回调。 fd socket句柄 (不是系统fd). ipaddr是客户端地址, 例如 "127.0.0.1:8000".
  2. function handler.connect(fd, ipaddr)
  3. --当一个客户端断开链接后调用该函数,必须要有
  4. function handler.disconnect(fd)
  5. --当fd有数据到达了,会调用这个函数,前提是fd需要调用gateserver.openclient打开
  6. function handler.message(fd, msg, sz)
  7. --如果你希望在监听端口打开的时候,做一些初始化操作,可以提供 open 这个方法。
  8. --source 是请求来源地址,conf 是开启 gate 服务的参数表(端口,连接数,是否延迟)。
  9. function handler.open(source, conf)
  10. --当一个连接异常(通常意味着断开),error 被调用,除了 fd ,还会拿到错误信息 msg(通常用于 log 输出)。
  11. function handler.error(fd, msg)
  12. --当 fd 上待发送的数据累积超过 1M 字节后,将回调这个方法。你也可以忽略这个消息。
  13. function handler.warning(fd, size)
  14. --gateserver除了能接收socket消息以为,当然也是可以接受skynetlua消息,并且gateserver还对lua消息注册函数进行了封装,只需提供handler.command回调函数就能处理lua消息,不需要我们自己调用skynet.dispatch来注册。
  15. function CMD.kick(source, fd)
  16. function handler.command(cmd, source, ...)
  17. local f = assert(CMD[cmd])
  18. return f(source, ...)
  19. end

具体的实现参考

其中 watchdog.lua 在文件夹 skynet/examples 下 gate.lua 在
skynet/service文件夹下,gateserver.lua 在 skynet/lualib/snax 文件夹下。

gate服务里面启动一个agent

gate服务写法

  1. --注册client消息专门用来将接收到的网络数据转发给agent,不需要解包,也不需要打包
  2. skynet.register_protocol {
  3. name = "client",
  4. id = skynet.PTYPE_CLIENT,
  5. }
  6. function handler.connect(fd, ipaddr)
  7. gateserver.openclient(fd)
  8. local agent = skynet.newservice("myagent", fd) --连接成功就启动一个agent来代理
  9. agents[fd] = agent
  10. end
  11. function handler.disconnect(fd) --断开连接后,agent服务退出
  12. local agent = agents[fd]
  13. if(agent) then
  14. --通过发送消息的方式来退出不要使用skynet.kill(agent)
  15. skynet.send(agent, "lua", "quit")
  16. agents[fd] = nil
  17. end
  18. end
  19. function handler.message(fd, msg, sz)
  20. local agent = agents[fd]
  21. skynet.redirect(agent, 0, "client", 0, msg, sz) --收到消息就转发给agent
  22. end

agent写法

  1. skynet.start(function()
  2. --注册client消息专门用来接收网络数据
  3. skynet.dispatch("client", function(_,_, msg)
  4. func1(msg)
  5. end)
  6. skynet.dispatch("lua", function(_,_, cmd) --注册lua消息,来退出服务
  7. if cmd == "quit" then
  8. skynet.error(fd,"agent quit")
  9. skynet.exit()
  10. end
  11. end)
  12. end)

小结

个人看法:skynet网关服务这一块主要是为了解决网络通信的问题,处理网络请求,目前了解的还不够深,如果想更好的理解,应该仔细研究
注: 这个模板不可以和 Socket 库一起使用。因为这个模板接管了 socket 类的消息。







watchdog.lua 在文件夹 skynet/examples 下






gate.lua 在 skynet/service 文件夹下






gateserver.lua 在 skynet/lualib/snax 文件夹下

本篇博客参考:
https://blog.csdn.net/hp\_cpp/article/details/107364207
https://blog.csdn.net/qq769651718/article/details/79435075
https://github.com/cloudwu/skynet/wiki/GateServer

发表评论

表情:
评论列表 (有 0 条评论,92人围观)

还没有评论,来说两句吧...

相关阅读

    相关 GateWay 服务

    GateWay简介 ⽹关(翻译过来就叫做GateWay):微服务架构中的重要组成部分。局域⽹中就有⽹关这个概念,局域⽹接收或者发送数据出去通过这个⽹关,⽐如⽤ Vmwar

    相关 Zuul服务

    微服务调用过程     前面的博客文章已经介绍过spring cloud的服务的相关内容信息,那么我们回顾一下多个微服务的调用过程。微服务一般会由不同的团队去维护,,那么