1、概述
LoginServer 承担登录请求分发器的角色,核心职责是为客户端匹配并返回当前负载最小的可用 MsgServer连接信息,同时实时维护集群中所有 MsgServer 的运行负载数据(含最大连接容量、当前在线用户数);当客户端发起登录请求时,若集群中无可用 MsgServer(如全部满载、无 MsgServer 接入),则向客户端返回约定的错误码,明确告知请求失败原因。
2、客户端与login_server的连接
8080端口监听客户端的连接
1、客户端点击登录
2、先连接,再发数据如下
发送http请求,发送的报文如下:
GET /msg_server HTTP/1.1
Host:192.168.62.150:8080
Accept: */*
Connection: Keep-Alive
3、login_server,判断请求路径/msg_server,执行响应函CHttpConn::_HandleMsgServRequest
1、如果无可用msg_server,则回复客户端指定错误码
2、如果msg_server连接,找到msg_server在线用户数最少(负载最少),告诉客户端,回复数据如下:
$2 = 0x75b080 "HTTP/1.1 200 OK\r\nConnection:close\r\nContent-Length:268\r\nContent-Type:text/html;chars
et=utf-8\r\n\r\n{\n \"backupIP\" : \"192.168.62.150\",\n \"code\" : 0,\n \"discovery\" : \"http://
127.0.0.1/api/discovery\",\n \"msfsBackup\" : \"http://192.168.62.150:8700/\",\n \"msfsPrior\" : \"h
ttp://192.168.62.150:8700/\",\n \"msg\" : \"\",\n \"port\" : \"8001\",\n \"priorIP\" : \"192.168.6
2.150\"\n}\n"
4、回复客户端数据后,立刻关闭连接
说明与客户端的http连接是短连接
3、login_server与msg_server的连接
8100端口:监听msg_server的连接
1、msg_server启动
1、init_login_serv_conn连接login_server
2、连接成功,向 LoginServer 注册自身服务信息(IP、端口、连接容量、当前在线数)
3、login_server将相关信息,保存到全局对象中
static ConnMap_t g_msg_serv_conn_map;
2、用户上下线,msg_server会通知login_server,login_server会更新msg_server在线人数
msg_server对应的代码
cpp
if (user_status == ::IM::BaseDefine::USER_STATUS_ONLINE) {
IM::Server::IMUserCntUpdate msg;
msg.set_user_action(USER_CNT_INC);
msg.set_user_id(pImUser->GetUserId());
CImPdu pdu;
pdu.SetPBMsg(&msg);
pdu.SetServiceId(SID_OTHER);
pdu.SetCommandId(CID_OTHER_USER_CNT_UPDATE);
send_to_all_login_server(&pdu);
Login_server对应的代码
cpp
void CLoginConn::_HandleUserCntUpdate(CImPdu* pPdu)
{
map<uint32_t, msg_serv_info_t*>::iterator it = g_msg_serv_info.find(m_handle);
if (it != g_msg_serv_info.end()) {
msg_serv_info_t* pMsgServInfo = it->second;
IM::Server::IMUserCntUpdate msg;
msg.ParseFromArray(pPdu->GetBodyData(), pPdu->GetBodyLength());
uint32_t action = msg.user_action();
if (action == USER_CNT_INC) {
pMsgServInfo->cur_conn_cnt++;
g_total_online_user_cnt++;
} else {
pMsgServInfo->cur_conn_cnt--;
g_total_online_user_cnt--;
}
log("%s:%d, cur_cnt=%u, total_cnt=%u ", pMsgServInfo->hostname.c_str(),
pMsgServInfo->port, pMsgServInfo->cur_conn_cnt, g_total_online_user_cnt);
}
}
3、其他
3.1、心跳包发送触发条件
当前时间 > 最后一次发送数据时间(m_last_send_tick) + 心跳间隔(SERVER_HEARTBEAT_INTERVAL),立即向对端发送心跳包。
3.2、连接超时关闭触发条件
当前时间 > 最后一次发送数据时间(m_last_send_tick) + 超时间隔(SERVER_TIMEOUT)
关闭连接。
cpp
void CLoginConn::OnTimer(uint64_t curr_tick)
{
if (m_conn_type == LOGIN_CONN_TYPE_CLIENT) {
if (curr_tick > m_last_recv_tick + CLIENT_TIMEOUT) {
Close();
}
} else {
if (curr_tick > m_last_send_tick + SERVER_HEARTBEAT_INTERVAL) {
IM::Other::IMHeartBeat msg;
CImPdu pdu;
pdu.SetPBMsg(&msg);
pdu.SetServiceId(SID_OTHER);
pdu.SetCommandId(CID_OTHER_HEARTBEAT);
SendPdu(&pdu);
}
if (curr_tick > m_last_recv_tick + SERVER_TIMEOUT) {
log("connection to MsgServer timeout ");
Close();
}
}
}
3.3、心跳包设计
其实看源码,代码中并没有处理心跳包消息CID_OTHER_HEARTBEAT。
-
若满足心跳发送条件,LoginServer 向 MsgServer 发送心跳包,发送成功后自动更新基类
CImConn的m_last_send_tick(最后一次发送数据时间) -
MsgServer 侧遵循完全相同的逻辑:定时器触发后,满足条件则向 LoginServer 发送心跳包,同时更新自身基类的
m_last_send_tick -
对端收到心跳包(合法 PDU)后,会自动刷新基类
CImConn的m_last_recv_tick(最后一次接收数据时间) -
定时器持续执行超时检查:若当前时间超过
m_last_recv_tick + SERVER_TIMEOUT,即判定对端超长时间未发送任何数据(含心跳包、业务包),直接关闭连接m_last_send_tick:本端发送任意数据后自动更新 (无论发送的是心跳包、业务包)m_last_recv_tick:本端收到对端任意合法 PDU 后自动更新(无论接收的是心跳包、业务包)