mirror of
https://github.com/cloudwu/skynet.git
synced 2026-07-25 04:33:05 +00:00
Redesign harbor/master/dummy service
This commit is contained in:
@@ -3,29 +3,36 @@ local harbor = require "skynet.harbor"
|
||||
|
||||
skynet.start(function()
|
||||
local standalone = skynet.getenv "standalone"
|
||||
|
||||
local launcher = assert(skynet.launch("snlua","launcher"))
|
||||
skynet.name(".launcher", launcher)
|
||||
|
||||
local harbor_id = tonumber(skynet.getenv "harbor")
|
||||
if harbor_id == 0 then
|
||||
assert(standalone == nil)
|
||||
standalone = true
|
||||
skynet.setenv("standalone", "true")
|
||||
local dummy = assert(skynet.launch("dummy"))
|
||||
harbor.init(dummy)
|
||||
else
|
||||
local master_addr = skynet.getenv "master"
|
||||
|
||||
local slave = skynet.newservice "cdummy"
|
||||
if slave == nil then
|
||||
skynet.abort()
|
||||
end
|
||||
skynet.name(".slave", slave)
|
||||
|
||||
else
|
||||
if standalone then
|
||||
assert(skynet.launch("master", standalone))
|
||||
if not skynet.newservice "cmaster" then
|
||||
skynet.abort()
|
||||
end
|
||||
end
|
||||
|
||||
local local_addr = skynet.getenv "address"
|
||||
|
||||
local h = assert(skynet.launch("harbor",master_addr, local_addr, harbor_id))
|
||||
harbor.init(h)
|
||||
local slave = skynet.newservice "cslave"
|
||||
if slave == nil then
|
||||
skynet.abort()
|
||||
end
|
||||
skynet.name(".slave", slave)
|
||||
end
|
||||
|
||||
local launcher = assert(skynet.launch("snlua","launcher"))
|
||||
skynet.name(".launcher", launcher)
|
||||
|
||||
if standalone then
|
||||
local datacenter = assert(skynet.newservice "datacenterd")
|
||||
skynet.name("DATACENTER", datacenter)
|
||||
|
||||
47
service/cdummy.lua
Normal file
47
service/cdummy.lua
Normal file
@@ -0,0 +1,47 @@
|
||||
local skynet = require "skynet"
|
||||
|
||||
local globalname = {}
|
||||
local harbor = {}
|
||||
|
||||
skynet.register_protocol {
|
||||
name = "harbor",
|
||||
id = skynet.PTYPE_HARBOR,
|
||||
pack = function(...) return ... end,
|
||||
unpack = skynet.tostring,
|
||||
}
|
||||
|
||||
skynet.register_protocol {
|
||||
name = "text",
|
||||
id = skynet.PTYPE_TEXT,
|
||||
pack = function(...) return ... end,
|
||||
unpack = skynet.tostring,
|
||||
}
|
||||
|
||||
function harbor.REGISTER(name, handle)
|
||||
assert(globalname[name] == nil)
|
||||
globalname[name] = handle
|
||||
skynet.redirect(harbor_service, handle, "harbor", 0, "N " .. name)
|
||||
end
|
||||
|
||||
function harbor.LINK(id)
|
||||
skynet.ret()
|
||||
end
|
||||
|
||||
function harbor.CONNECT(id)
|
||||
skynet.error("Can't connect to other harbor in single node mode")
|
||||
end
|
||||
|
||||
skynet.start(function()
|
||||
local harbor_id = tonumber(skynet.getenv "harbor")
|
||||
assert(harbor_id == 0)
|
||||
|
||||
skynet.dispatch("lua", function (session,source,command,...)
|
||||
local f = assert(harbor[command])
|
||||
f(...)
|
||||
end)
|
||||
skynet.dispatch("text", function(session,source,command)
|
||||
-- ignore all the command
|
||||
end)
|
||||
|
||||
harbor_service = assert(skynet.launch("harbor", harbor_id, skynet.self()))
|
||||
end)
|
||||
123
service/cmaster.lua
Normal file
123
service/cmaster.lua
Normal file
@@ -0,0 +1,123 @@
|
||||
local skynet = require "skynet"
|
||||
local socket = require "socket"
|
||||
|
||||
--[[
|
||||
master manage data :
|
||||
1. all the slaves address : id -> ipaddr:port
|
||||
2. all the global names : name -> address
|
||||
|
||||
master hold connections from slaves .
|
||||
|
||||
protocol slave->master :
|
||||
package size 1 byte
|
||||
type 1 byte :
|
||||
'H' : HANDSHAKE, report slave id, and address.
|
||||
'R' : REGISTER name address
|
||||
'Q' : QUERY name
|
||||
|
||||
|
||||
protocol master->slave:
|
||||
package size 1 byte
|
||||
type 1 byte :
|
||||
'W' : WAIT n
|
||||
'C' : CONNECT slave_id slave_address
|
||||
'N' : NAME globalname address
|
||||
'D' : DISCONNECT slave_id
|
||||
]]
|
||||
|
||||
local slave_node = {}
|
||||
local global_name = {}
|
||||
|
||||
local function read_package(fd)
|
||||
local sz = socket.read(fd, 1)
|
||||
assert(sz, "closed")
|
||||
sz = string.byte(sz)
|
||||
local content = socket.read(fd, sz)
|
||||
return skynet.unpack(content)
|
||||
end
|
||||
|
||||
local function pack_package(...)
|
||||
local message = skynet.packstring(...)
|
||||
local size = #message
|
||||
assert(size <= 255 , "too long")
|
||||
return string.char(size) .. message
|
||||
end
|
||||
|
||||
local function report_slave(fd, slave_id, slave_addr)
|
||||
local message = pack_package("C", slave_id, slave_addr)
|
||||
local n = 0
|
||||
for k,v in pairs(slave_node) do
|
||||
socket.write(v.fd, message)
|
||||
n = n + 1
|
||||
end
|
||||
socket.write(fd, pack_package("W", n))
|
||||
end
|
||||
|
||||
local function handshake(fd)
|
||||
local t, slave_id, slave_addr = read_package(fd)
|
||||
assert(t=='H', "Invalid handshake type " .. t)
|
||||
assert(slave_id ~= 0 , "Invalid slave id 0")
|
||||
if slave_node[slave_id] then
|
||||
error(string.format("Slave %d already register on %s", slave_id, slave_node[slave_id].addr))
|
||||
end
|
||||
report_slave(fd, slave_id, slave_addr)
|
||||
slave_node[slave_id] = {
|
||||
fd = fd,
|
||||
id = slave_id,
|
||||
addr = slave_addr,
|
||||
}
|
||||
return slave_id , slave_addr
|
||||
end
|
||||
|
||||
local function dispatch_slave(fd)
|
||||
local t, name, address = read_package(fd)
|
||||
if t == 'R' then
|
||||
-- register name
|
||||
assert(type(address)=="number", "Invalid request")
|
||||
if not global_name[name] then
|
||||
global_name[name] = address
|
||||
end
|
||||
local message = pack_package("N", name, address)
|
||||
for k,v in pairs(slave_node) do
|
||||
socket.write(v.fd, message)
|
||||
end
|
||||
elseif t == 'Q' then
|
||||
-- query name
|
||||
local address = global_name[name]
|
||||
if address then
|
||||
socket.write(fd, pack_package("N", name, address))
|
||||
end
|
||||
else
|
||||
skynet.error("Invalid slave message type " .. t)
|
||||
end
|
||||
end
|
||||
|
||||
local function monitor_slave(slave_id, slave_address)
|
||||
local fd = slave_node[slave_id].fd
|
||||
skynet.error(string.format("Harbor %d (fd=%d) report %s", slave_id, fd, slave_address))
|
||||
while pcall(dispatch_slave, fd) do end
|
||||
skynet.error("slave " ..slave_id .. " is down")
|
||||
local message = pack_package("D", slave_id)
|
||||
slave_node[slave_id].fd = 0
|
||||
for k,v in pairs(slave_node) do
|
||||
socket.write(v.fd, message)
|
||||
end
|
||||
socket.close(fd)
|
||||
end
|
||||
|
||||
skynet.start(function()
|
||||
local master_addr = skynet.getenv "standalone"
|
||||
skynet.error("master listen socket " .. tostring(master_addr))
|
||||
local fd = socket.listen(master_addr)
|
||||
socket.start(fd , function(id, addr)
|
||||
skynet.error("connect from " .. addr .. " " .. id)
|
||||
socket.start(id)
|
||||
local ok, slave, slave_addr = pcall(handshake, id)
|
||||
if ok then
|
||||
skynet.fork(monitor_slave, slave, slave_addr)
|
||||
else
|
||||
skynet.error(string.format("disconnect fd = %d, error = %s", id, slave))
|
||||
socket.close(id)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
222
service/cslave.lua
Normal file
222
service/cslave.lua
Normal file
@@ -0,0 +1,222 @@
|
||||
local skynet = require "skynet"
|
||||
local socket = require "socket"
|
||||
|
||||
local slaves = {}
|
||||
local connect_queue = {}
|
||||
local globalname = {}
|
||||
local harbor = {}
|
||||
local harbor_service
|
||||
local monitor = {}
|
||||
|
||||
local function read_package(fd)
|
||||
local sz = socket.read(fd, 1)
|
||||
assert(sz, "closed")
|
||||
sz = string.byte(sz)
|
||||
local content = socket.read(fd, sz)
|
||||
return skynet.unpack(content)
|
||||
end
|
||||
|
||||
local function pack_package(...)
|
||||
local message = skynet.packstring(...)
|
||||
local size = #message
|
||||
assert(size <= 255 , "too long")
|
||||
return string.char(size) .. message
|
||||
end
|
||||
|
||||
local function monitor_clear(id)
|
||||
local v = monitor[id]
|
||||
if v then
|
||||
monitor[id] = nil
|
||||
for _, v in ipairs(v) do
|
||||
skynet.redirect(v.address, 0, "response", v.session, "")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function connect_slave(slave_id, address)
|
||||
local ok, err = pcall(function()
|
||||
if slaves[slave_id] == nil then
|
||||
local fd = socket.open(address)
|
||||
skynet.error(string.format("Connect to harbor %d (fd=%d), %s", slave_id, fd, address))
|
||||
slaves[slave_id] = fd
|
||||
monitor_clear(slave_id)
|
||||
socket.abandon(fd)
|
||||
skynet.send(harbor_service, "harbor", string.format("S %d %d",fd,slave_id))
|
||||
end
|
||||
end)
|
||||
if not ok then
|
||||
skynet.error(err)
|
||||
end
|
||||
end
|
||||
|
||||
local function ready()
|
||||
local queue = connect_queue
|
||||
connect_queue = nil
|
||||
for k,v in pairs(queue) do
|
||||
connect_slave(k,v)
|
||||
end
|
||||
for name,address in pairs(globalname) do
|
||||
skynet.redirect(harbor_service, address, "harbor", "N " .. name)
|
||||
end
|
||||
end
|
||||
|
||||
local function monitor_master(master_fd)
|
||||
while true do
|
||||
local ok, t, id_name, address = pcall(read_package,master_fd)
|
||||
if ok then
|
||||
if t == 'C' then
|
||||
if connect_queue then
|
||||
connect_queue[id_name] = address
|
||||
else
|
||||
connect_slave(id_name, address)
|
||||
end
|
||||
elseif t == 'N' then
|
||||
globalname[id_name] = address
|
||||
if connect_queue == nil then
|
||||
skynet.redirect(harbor_service, address, "harbor", 0, "N " .. id_name)
|
||||
end
|
||||
elseif t == 'D' then
|
||||
local fd = slaves[id_name]
|
||||
slaves[id_name] = false
|
||||
if fd then
|
||||
socket.close(fd)
|
||||
end
|
||||
end
|
||||
else
|
||||
skynet.error("Master disconnect")
|
||||
socket.close(master_fd)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function accept_slave(fd)
|
||||
socket.start(fd)
|
||||
local id = socket.read(fd, 1)
|
||||
if not id then
|
||||
skynet.error(string.format("Connection (fd =%d) closed", fd))
|
||||
socket.close(fd)
|
||||
return
|
||||
end
|
||||
id = string.byte(id)
|
||||
if slaves[id] ~= nil then
|
||||
skynet.error(string.format("Slave %d exist (fd =%d)", id, fd))
|
||||
socket.close(fd)
|
||||
return
|
||||
end
|
||||
slaves[id] = fd
|
||||
monitor_clear(id)
|
||||
socket.abandon(fd)
|
||||
skynet.error(string.format("Harbor %d connected (fd = %d)", id, fd))
|
||||
skynet.send(harbor_service, "harbor", string.format("A %d %d", fd, id))
|
||||
end
|
||||
|
||||
skynet.register_protocol {
|
||||
name = "harbor",
|
||||
id = skynet.PTYPE_HARBOR,
|
||||
pack = function(...) return ... end,
|
||||
unpack = skynet.tostring,
|
||||
}
|
||||
|
||||
skynet.register_protocol {
|
||||
name = "text",
|
||||
id = skynet.PTYPE_TEXT,
|
||||
pack = function(...) return ... end,
|
||||
unpack = skynet.tostring,
|
||||
}
|
||||
|
||||
local function monitor_harbor(master_fd)
|
||||
return function(session, source, command)
|
||||
local t = string.sub(command, 1, 1)
|
||||
local arg = string.sub(command, 3)
|
||||
if t == "Q" then
|
||||
-- query name
|
||||
if globalname[arg] then
|
||||
skynet.redirect(harbor_service, globalname[arg], "harbor", "N " .. arg)
|
||||
else
|
||||
socket.write(master_fd, pack_package("Q", arg))
|
||||
end
|
||||
elseif t == "D" then
|
||||
-- harbor down
|
||||
local id = tonumber(arg)
|
||||
if slaves[id] then
|
||||
monitor_clear(id)
|
||||
end
|
||||
slaves[id] = false
|
||||
else
|
||||
skynet.error("Unknown command ", command)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function harbor.REGISTER(_,_, fd, name, handle)
|
||||
assert(globalname[name] == nil)
|
||||
globalname[name] = handle
|
||||
socket.write(fd, pack_package("R", name, handle))
|
||||
skynet.redirect(harbor_service, handle, "harbor", 0, "N " .. name)
|
||||
end
|
||||
|
||||
function harbor.LINK(session, source, fd, id)
|
||||
if slaves[id] then
|
||||
if monitor[id] == nil then
|
||||
monitor[id] = {}
|
||||
end
|
||||
table.insert(monitor[id], { address = source, session = session })
|
||||
else
|
||||
skynet.ret()
|
||||
end
|
||||
end
|
||||
|
||||
function harbor.CONNECT(session, source, fd, id)
|
||||
if not slaves[id] then
|
||||
if monitor[id] == nil then
|
||||
monitor[id] = {}
|
||||
end
|
||||
table.insert(monitor[id], { address = source, session = session })
|
||||
else
|
||||
skynet.ret()
|
||||
end
|
||||
end
|
||||
|
||||
skynet.start(function()
|
||||
local master_addr = skynet.getenv "master"
|
||||
local harbor_id = tonumber(skynet.getenv "harbor")
|
||||
local slave_address = assert(skynet.getenv "address")
|
||||
local slave_fd = socket.listen(slave_address)
|
||||
skynet.error("slave connect to master " .. tostring(master_addr))
|
||||
local master_fd = socket.open(master_addr)
|
||||
|
||||
skynet.dispatch("lua", function (session,source,command,...)
|
||||
local f = assert(harbor[command])
|
||||
f(session, source, master_fd, ...)
|
||||
end)
|
||||
skynet.dispatch("text", monitor_harbor(master_fd))
|
||||
|
||||
harbor_service = assert(skynet.launch("harbor", harbor_id, skynet.self()))
|
||||
|
||||
local hs_message = pack_package("H", harbor_id, slave_address)
|
||||
socket.write(master_fd, hs_message)
|
||||
local t, n = read_package(master_fd)
|
||||
assert(t == "W" and type(n) == "number", "slave shakehand failed")
|
||||
skynet.error(string.format("Waiting for %d harbors", n))
|
||||
skynet.fork(monitor_master, master_fd)
|
||||
if n > 0 then
|
||||
local co = coroutine.running()
|
||||
socket.start(slave_fd, function(fd, addr)
|
||||
skynet.error(string.format("New connection (fd = %d, %s)",fd, addr))
|
||||
if pcall(accept_slave,fd) then
|
||||
local s = 0
|
||||
for k,v in pairs(slaves) do
|
||||
s = s + 1
|
||||
end
|
||||
if s >= n then
|
||||
skynet.wakeup(co)
|
||||
end
|
||||
end
|
||||
end)
|
||||
skynet.wait()
|
||||
end
|
||||
socket.close(slave_fd)
|
||||
skynet.error("Shakehand ready")
|
||||
skynet.fork(ready)
|
||||
end)
|
||||
@@ -88,7 +88,7 @@ end
|
||||
|
||||
skynet.start(function()
|
||||
local listen_socket = socket.listen ("127.0.0.1", port)
|
||||
print("Start debug console at 127.0.0.1",port)
|
||||
skynet.error("Start debug console at 127.0.0.1 " .. port)
|
||||
socket.start(listen_socket , function(id, addr)
|
||||
local function print(...)
|
||||
local t = { ... }
|
||||
|
||||
Reference in New Issue
Block a user