当前位置: 首页 > news >正文

skynet——服务发现学习

skynet创建一个新服务,其他服务怎么知道他的地址,并和他通信呢?

单进程

本地服务管理

localservice={}-- 存储所有注册的本地服务functioncmd.LAUNCH(service_name,subname,...)localrealname=read_name(service_name)returnwaitfor(service_name,skynet.newservice,realname,subname,...)endfunctioncmd.QUERY(service_name,subname)returnwaitfor(service_name)end

这里service表维护了名字到句柄的映射。当服务启动时,它会被注册到这个表中,后续其他服务可以通过名字查询到对应的句柄。

服务注册

localfunctionglobalname(name,handle)localc=string.sub(name,1,1)assert(c~=':')ifc=='.'thenreturnfalseendassert(#name<16)-- GLOBALNAME_LENGTH is 16, defined in skynet_harbor.hassert(tonumber(name)==nil)-- global name can't be numberlocalharbor=require"skynet.harbor"harbor.globalname(name,handle)returntrueendfunctionskynet.register(name)ifnotglobalname(name)thenc.command("REG",name)-- 调用C层注册endendfunctionskynet.name(name,handle)ifnotglobalname(name,handle)thenc.command("NAME",name.." "..skynet.address(handle))endend

Skynet将服务名字分为两类——本地名字(以.开头)全局名字(不以.和:开头)。本地名字通过C层的本地注册机制存储,适合单进程内使用;全局名字需要通过Harbor机制同步到其他节点。

集群

Cluster机制采用了去中心化的设计理念,每个节点只需要配置其他节点的地址即可直接通信。

名字服务注册

--cluster.luafunctioncluster.register(name,addr)assert(type(name)=="string")assert(addr==nilortype(addr)=="number")returnskynet.call(clusterd,"lua","register",name,addr)endfunctioncluster.unregister(name)assert(type(name)=="string")returnskynet.call(clusterd,"lua","unregister",name)end--clusterd.lualocalregister_name={}-- 本节点的名字注册表localnode_address={}-- 节点地址配置functioncommand.register(source,name,addr)assert(register_name[name]==nil)addr=addrorsourcelocalold_name=register_name[addr]ifold_namethenregister_name[old_name]=nilendregister_name[addr]=name register_name[name]=addr skynet.error(string.format("Register [%s] :%08x",name,addr))endfunctioncommand.queryname(source,name)skynet.ret(skynet.pack(register_name[name]))end

跨节点地址发现

Cluster 采用静态配置方式解决"节点地址"问题。每个节点在启动时读取配置文件

-- clusterd.lua 第85-95行localfunctionloadconfig(tmp)iftmp==nilthentmp={}ifconfig_namethen-- 从环境变量读取配置文件路径localf=assert(io.open(config_name))localsource=f:read"*a"f:close()assert(load(source,"@"..config_name,"t",tmp))()-- 执行配置文件endend-- ...forname,addressinpairs(tmp)doifname:sub(1,2)=="__"then-- __开头的是特殊配置localopt=name:sub(3)config[opt]=addresselsenode_address[name]=address-- name -> IP:Port 映射endendend

大致加载流程如下:

┌─────────────────────────────────────────────────────────────────┐ │ Node1 启动 │ ├─────────────────────────────────────────────────────────────────┤ │ skynet.init() │ │ ↓ │ │ clusterd 服务启动 │ │ ↓ │ │ loadconfig("cluster.conf") │ │ ↓ │ │ 加载配置: │ │ node_address = { │ │ ["node1"] = "127.0.0.1:7001", │ │ ["node2"] = "127.0.0.1:7002", │ │ ["node3"] = "192.168.1.100:7003", │ │ } │ └─────────────────────────────────────────────────────────────────┘

完整服务调用流程

┌─────────────────────────────────────────────────────────────────────────────┐ │ Cluster 服务调用完整流程 │ └─────────────────────────────────────────────────────────────────────────────┘ 1. 配置阶段(启动时) ├── 所有节点加载 cluster.conf └── 每个节点知道所有其他节点的 IP:Port 2. 连接阶段(首次通信时) ├── Node1 调用 cluster.call("node2", "gate", "open", ...) ├── 发现 sender["node2"] == nil ├── 创建 clustersender("node2") 服务 ├── clustersender 发起 TCP 连接到 127.0.0.1:7002 └── 保存 sender["node2"] = clustersender 3. 服务查询阶段 ├── 首次调用时,通过 sender 发送名字查询 ├── Node2 的 clusterd 查询本地 register_name 表 └── 返回服务的内部句柄(如 :00000005) 4. 消息发送阶段 ├── 序列化请求(skynet.pack) ├── 通过 TCP 发送到 Node2 ├── Node2 的 clustersender 接收 ├── 转发给目标服务 :00000005 (gate) └── gate 处理请求并返回结果 5. 结果返回阶段 ├── gate 返回结果给 clustersender ├── clustersender 通过 TCP 发回 Node1 └── Node1 的调用协程被唤醒,获取结果

如何动态发现新增的服务

结合cluster,新增一个中继集群,他负责维护全部服务的。

┌─────────────────────────────────────────────────────────────────────────────┐ │ 中继集群架构图 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ Client A │ │ Client B │ │ Client C │ │ │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │ │ │ │ │ │ │ cluster.call │ │ │ │ └────────────────────┼────────────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────────────────────────────────────────────────────────┐ │ │ │ 中继集群 (Relay Cluster) │ │ │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ │ │ 服务注册表 (Registry) │ │ │ │ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ gate │ node1:001234 │ node2:005678 │ 3个 │ │ │ │ │ │ │ │ worker │ node1:003456 │ node2:007890 │ 5个 │ │ │ │ │ │ │ │ db_mgr │ node3:009ABC │ │ 1个 │ │ │ │ │ │ │ └──────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ │ │ │ │ 负载均衡器 (Load Balancer) │ │ │ │ │ │ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │ │ │ │ │ │ │Round │ │Least │ │Hash │ │Random │ │ │ │ │ │ │ │Robin │ │Load │ │ │ │ │ │ │ │ │ │ │ └────────┘ └────────┘ └────────┘ └────────┘ │ │ │ │ │ └────────────────────────────────────────────────────────────┘ │ │ │ └──────────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ cluster.call (转发) │ │ ┌────────────────────┼────────────────────┐ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ Node 1 │ │ Node 2 │ │ Node 3 │ │ │ │ │ │ │ │ │ │ │ │ gate │ │ gate │ │ db_mgr │ │ │ │ worker x 3 │ │ worker x 2 │ │ │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘

设计方案

┌─────────────────────────────────────────────────────────────────────────────┐ │ 服务同步模式对比 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ 模式 │ 示意图 │ 特点 │ │ ───────────────────────────────────────────────────────────────────────── │ │ │ │ │ │ 集中式 │ ┌────────┐ │ 简单,但中继节点压力大 │ │ (单中继) │ │ Master │ 所有注册 │ 单点风险 │ │ │ └───┬────┘ │ │ │ │ │ │ │ │ │ ┌────┴────┐ │ │ │ │ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ │ │ 节点1 节点2 节点3 │ │ │ │ │ │ │ ───────────────────────────────────────────────────────────────────────── │ │ │ │ │ │ 全复制 │ ┌────┐ ┌────┐ ┌────┐ │ 强一致性,每个节点完整副本 │ │ (每个节点 │ │ 1 │ │ 2 │ │ 3 │ │ 但同步开销大 │ │ 完整副本) │ │全量│ │全量│ │全量│ │ │ │ │ └────┘ └────┘ └────┘ │ │ │ │ 同步──→同步──→同步 │ │ │ │ │ │ │ ───────────────────────────────────────────────────────────────────────── │ │ │ │ │ │ 分区 │ ┌────┐ ┌────┐ ┌────┐ │ 扩展性好,负载分散 │ │ (按服务名 │ │ A │ │ B │ │ C │ │ 但查询复杂,可能需要多跳 │ │ 分片) │ │-G │ │-W │ │-M │ │ │ │ │ └────┘ └────┘ └────┘ │ │ │ │ │ │ │ │ 节点1 节点2 节点3 │ │ │ │ │ │ │ ───────────────────────────────────────────────────────────────────────── │ │ │ │ │ │ Gossip │ ┌────┐ ┌────┐ ┌────┐ │ 最终一致,适合大规模 │ │ (最终一致) │ │节点1│←→│节点2│←→│节点3│ │ 实现复杂,有延迟 │ │ │ │ ↕ │ ↕ │ ↕ │ │ │ │ │ └────┘ └────┘ └────┘ │ │ │ │ 随机同步,慢慢扩散 │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
http://www.jsqmd.com/news/895485/

相关文章:

  • AI重塑税务文档处理:从OCR到智能分类的自动化实践
  • 阴阳师自动化脚本:20+任务智能托管,解放双手的终极解决方案
  • 嵌入式学习之路->stm32篇->(16)高级定时器
  • 20253921 2025-2026-2 《网络攻防实践》第九周作业
  • Windows Subsystem for Android 技术架构深度解析与高级配置指南
  • Windows驱动管理终极指南:用RAPR工具实现系统驱动的快速清理与优化
  • 5分钟掌握AMD Ryzen隐藏性能:SMUDebugTool实战指南
  • 混合CMOS-忆阻器仲裁器PUF设计与硬件安全应用
  • 实战经验:如何修复 MariaDB 因 InnoDB 损坏导致的启动失败 (status=6/ABRT)
  • ThinkPad风扇控制终极指南:如何用TPFanCtrl2实现完美散热
  • Zotero与Scholaread协同的AI文献阅读系统:联动设置、对照式翻译与文献高效管理 - nut-king
  • 构建AI智能体信任基础设施:从技能验证到支付结算的完整方案
  • 终极指南:如何快速逆向Wallpaper Engine资源并提取TEX纹理
  • 业务接 AI 前,先别急着调模型,先做输入脱敏层
  • 5分钟掌握Mermaid Live Editor:免费在线图表编辑器的终极指南
  • 终极微信聊天记录导出指南:三步永久保存你的珍贵对话
  • 从DOM定位器到计算机视觉:构建更健壮的端到端测试体系
  • 基于OCR与LLM的终端智能助手:让AI在屏幕上行走的工程实践
  • 研究生必备|8款文献翻译免费软件深度测评,Scholaread免费版竟然能做到这个程度 - nut-king
  • 游标分页(Cursor-based Pagination)
  • Lattice LFCPNX-100 HSB+Fpga开发详解: 2.1 MAC+PCS以太网SFP光口传输
  • GEE数据集:全球森林变化数据集Hansen Global Forest Change v1.13 (2000-2025)
  • WSL2 吃掉我 25GB C 盘空间:一次完整的排查与回收记录
  • 革命性AI视频字幕去除工具:Video-subtitle-remover一站式解决方案
  • video-subtitle-extractor:如何让AI看懂视频中的“隐形文字“并精准提取?
  • 向量数据库与RAG管道:从核心组件到系统工程的关键认知
  • Linux入门到实战·学习笔记系列——10.计算机网络基础概论
  • 如何快速掌握OBS多平台直播:obs-multi-rtmp插件完整教程
  • 用Unity和C#实现人群疏散模拟:手把手教你搭建社会力模型(附完整代码)
  • 终极指南:5分钟快速上手AzurLaneAutoScript,彻底解放你的碧蓝航线游戏时间