用Zmq实现网关与游戏服全互连
(金庆的专栏)
Zeromq无需bind, connect次序,可以在对方bind之前就connect, 可实现服务器以任意顺序启动。
Zmq有自动重连功能,可实现服务器故障重启不影响整个系统。
万神服务器内部连接采用了Zmq, 并可能在未来将客户端服务器之间的连接也改用为Zmq。
万神的网关(Gtw,Gateway)与游戏服(Gs, Game Server)之间是M*N的全互连关系。
即M个网关,N个游戏服,每个Gtw连接所有Gs, 每个Gs连接所有Gtw.
采用Zmq的Router-Router模式,Gtw和Gs分别只需一个zmq::socket_t, 就可以实现M*N互连。
Router-Router互连的决窍是至少其中一方须设置ZMQ_IDENTITY.
因为Gs是稳定的服务器,数量固定,所以设为bind方,并设置 ZMQ_IDENTITY.
Gtw是可任意添加的服务器,所以设为connect方,使用自动ZMQ_IDENTITY.
ZMQ_IDENTITY就是socket的名字。
Gtw向Gs发送消息时,须先指定对方的名字,然后才能发送到指定的Gs.
接收时,也会附带获取对方的名字。
Gtw是自动ZMQ_IDENTITY,每次连接Gs, Gs就会自动为其设定一个随机的名字。
Gtw重启后,希望其名字与前次运行的名字不相同,所以设为自动ZMQ_IDENTITY.
Gs端只需一次bind:
zmq::socket_t sktGs(context, ZMQ_ROUTER);
sktGs.setsockopt(ZMQ_IDENTITY, ...);
sktGs.bind("tcp://*:12345");
Gtw端须连接多个Gs, Gs的IP和端口是配置的:
zmq::socket_t sktGtw(context, ZMQ_ROUTER);
BOOST_FOREACH(int nGsId, setGsIds);
{
sktGtw.connect(Fmt("tcp://%s:%u",
config.GetGsHost(nGsId),
config.GetGsPort(nGsId)));
}
Gtw重启后,应该假设此时Gs可能尚未存在,须不断向Gs发Init消息。
Gs应答InitAck之后就可以确认与Gs之间的连接建立成功。
Gs的名字是从配置文件读取的。
Gs重启后,将等待Gtw的消息,如果是Init, 则应答InitAck.
如果是其他消息,则发送Reset, 让Gtw重置。
Gtw接到Reset后,将发送Init, 并等待InitAck.
如此,Gs与Gtw将不存在启动顺序关系,并且服务器可任意重启而不影响其他服务器。