skynet 集群cluster初探

由于本人对集群这块应用的不多,现只对其内部调用机制作简单的剖析。

集群源码包括两个文件cluster.lua和clusterd.lua。cluster.lua其实是对clustered.lua的api的封装。我们用到集群时只需要使用cluster.lua的api即可。

使用集群时必须引用cluster.lua文件,他会单例启用clusterd服务。而clusterd服务最开始就会调用loadconfig()加载配置。配置形如:

db = "192.168.0.183:2528"

db2 = "192.168.0.136:2529"

将配置表解析为名称对应节点地址端口号表。

要想使用集群,首先得打开集群的地址,即监听集群的地址端口号。使用API cluster.open("name")即可。对应的cludterd是command.listen:

function command.listen(source, addr, port)
	local gate = skynet.newservice("gate")
	if port == nil then
		addr, port = string.match(node_address[addr], "([^:]+):(.*)$")
	end
	skynet.call(gate, "lua", "open", { address = addr, port = port })
	skynet.ret(skynet.pack(nil))
end

source是调用服务的handle,这里没有用。可以看到这段代码的作用是启动一个gate服务,然后监听地址和端口号。gate服务前面已经讲过。

调用远程集群的api是:

function cluster.call(node, address, ...)
	-- skynet.pack(...) will free by cluster.core.packrequest
	return skynet.call(clusterd, "lua", "req", node, address, skynet.pack(...))
end

我们看其实现:

local function send_request(source, node, addr, msg, sz)
	local session = node_session[node] or 1
	-- msg is a local pointer, cluster.packrequest will free it
	local request, new_session, padding = cluster.packrequest(addr, session, msg, sz)
	node_session[node] = new_session

	-- node_channel[node] may yield or throw error
	local c = node_channel[node]

	return c:request(request, session, padding)
end

function command.req(...)
	local ok, msg, sz = pcall(send_request, ...)
	if ok then
		if type(msg) == "table" then
			skynet.ret(cluster.concat(msg))
		else
			skynet.ret(msg)
		end
	else
		skynet.error(msg)
		skynet.response()(false)
	end
end

在send_request中,addr是想要调用服务的地址,字符串和handle都可以。msg,sz 是经skynet.pack打包过后的数据。刚开始没有session,则为1。

关于session,在集群cluster中是非常重要的概念。为什么这么说呢?集群的目的是为了远程调用服务,意图和本地调用服务差不多。以下是云风大大的说法:

在与外部服务交互式时,请求回应模式是最常用模式之一。通常的协议设计方式有两种。每个请求包对应一个回应包,由 TCP 协议保证时序。

发起每个请求时带一个唯一 session 标识,在发送回应时,带上这个标识。这样设计可以不要求每个请求都一定要有回应,且不必遵循先提出的请求先回应的时序。对于第一种模式,用 skynet 的 Socket API 很容易实现,但如果在一个 coroutine 中读写一个 socket 的话,由于读的过程是阻塞的,这会导致吞吐量下降。

​对于第二种模式,需要用 skynet.fork 开启一个新线程来收取回响应包,并自行和请求对应起来,实现比较繁琐。所以skynet 提供了一个更高层的封装:socket channel。

socket channel有这两种模式的实现。cluster使用了第二种模式。因为第一种模式虽然发送消息不受限制,但是消息是严格按照顺序收到的,如果前面消息处理时间很长,势必会影响本次消息的接收。

关于这个socket channel,大致看了下源码,还是有点小复杂,有时间再来剖析。

cluster发送数据时都会带上session,最后会根据session匹配收到的消息。

由于这里的网络数据是在gate服务中监听的,当收到消息时会command.socket会被调用(具体分析请看skynet watchdog,gate源码分析),在这里他会给目的服务发送消息。待得到回应后会发送给自己,这样cluster服务就可以收到响应的值了。















猜你喜欢

转载自blog.csdn.net/zxm342698145/article/details/80570579