skynet——服务发现学习

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

单进程

本地服务管理

lua 复制代码
local service = {}  -- 存储所有注册的本地服务

function cmd.LAUNCH(service_name, subname, ...)
    local realname = read_name(service_name)
    return waitfor(service_name, skynet.newservice, realname, subname, ...)
end

function cmd.QUERY(service_name, subname)
    return waitfor(service_name)
end

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

服务注册

lua 复制代码
local function globalname(name, handle)
	local c = string.sub(name,1,1)
	assert(c ~= ':')
	if c == '.' then
		return false
	end

	assert(#name < 16)	-- GLOBALNAME_LENGTH is 16, defined in skynet_harbor.h
	assert(tonumber(name) == nil)	-- global name can't be number

	local harbor = require "skynet.harbor"

	harbor.globalname(name, handle)

	return true
end

function skynet.register(name)
    if not globalname(name) then
        c.command("REG", name)  -- 调用C层注册
    end
end

function skynet.name(name, handle)
    if not globalname(name, handle) then
        c.command("NAME", name .. " " .. skynet.address(handle))
    end
end

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

集群

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

名字服务注册

lua 复制代码
--cluster.lua
function cluster.register(name, addr)
    assert(type(name) == "string")
    assert(addr == nil or type(addr) == "number")
    return skynet.call(clusterd, "lua", "register", name, addr)
end

function cluster.unregister(name)
    assert(type(name) == "string")
    return skynet.call(clusterd, "lua", "unregister", name)
end

--clusterd.lua
local register_name = {}  -- 本节点的名字注册表
local node_address = {}   -- 节点地址配置

function command.register(source, name, addr)
    assert(register_name[name] == nil)
    addr = addr or source
    local old_name = register_name[addr]
    if old_name then
        register_name[old_name] = nil
    end
    register_name[addr] = name
    register_name[name] = addr
    skynet.error(string.format("Register [%s] :%08x", name, addr))
end

function command.queryname(source, name)
    skynet.ret(skynet.pack(register_name[name]))
end

跨节点地址发现

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

lua 复制代码
-- clusterd.lua 第85-95行
local function loadconfig(tmp)
    if tmp == nil then
        tmp = {}
        if config_name then  -- 从环境变量读取配置文件路径
            local f = assert(io.open(config_name))
            local source = f:read "*a"
            f:close()
            assert(load(source, "@"..config_name, "t", tmp))()  -- 执行配置文件
        end
    end
    -- ...
    for name, address in pairs(tmp) do
        if name:sub(1,2) == "__" then  -- __开头的是特殊配置
            local opt = name:sub(3)
            config[opt] = address
        else
            node_address[name] = address  -- name -> IP:Port 映射
        end
    end
end

大致加载流程如下:

复制代码
┌─────────────────────────────────────────────────────────────────┐
│                      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 的调用协程被唤醒,获取结果

启动skynet时,如果是集群,他的流程是怎样的?

相关推荐
-To be number.wan8 小时前
算法日记 | C++ 结构体
数据结构·学习·算法
大连好光景9 小时前
Skills索引大全
学习·ai编程
Cat_Rocky9 小时前
Linux学习-ansible自动化
linux·学习·ansible
IronMurphy9 小时前
AI Agent 学习笔记 Day 1:大模型基础、API 调用与 Prompt 工程
人工智能·笔记·学习
承渊政道9 小时前
【MySQL数据库学习】(MySQL数据库基础)
数据库·学习·mysql·ubuntu·bash·数据库架构·数据库系统
爱学习的章鱼哥9 小时前
AI编程学习笔记(I)
人工智能·笔记·学习·ai编程
·醉挽清风·9 小时前
学习笔记—MySQL—索引
笔记·学习·mysql
我想我不够好。10 小时前
2026.5.25学习 1.5hour
学习
red_redemption10 小时前
自由学习记录(192)
学习