消息转发

1.消息中转

th项目利用交换服务器为跨服务器通信提供消息中转服务.

交换服务器负责:
.接受服务器登记
.登记信息保存在mysql中,以便所有服务器共享访问.
.负责转发
.处理有限的协议


实现时以用户上线通知为例。
用户A在S1服务器上登录,需要把该用户上线的消息发送给在其它服务器登录的同事和好友.

S1确定用户A的同事和好友信息:
。在S1上登录的
。在其它服务器上登录的

转发消息包含2部分:消息本身以及目标信息。

目标信息用于转发处理,包括服务器行集,而服务器记录包含用户行集。


转发消息(905-Request)

。交换服务器转发的消息

。目标为交换服务器(3,0)
。原始消息的目标设置为(0,0):对于接收的客户端而言,未指定目标就是客户端自己
。服务器行集,每条记录为该服务器上的用户行集

转发消息(906-Request)

。应用服务器转发的消息

。目标为服务器

。用户行集:记录该服务器上需要发送的客户端



当一个消息需要发送给跨多个服务器上的用户时,需要通过交换服务器中转.
IIM接口的Send(CMsg *msg,USER_LIST &user_list)方法为此提供统一的入口.
避免消息多个副本.
只向交换服务器发送一个用户消息转发请求(905-Request).消息中包含了需要转发的服务器列表和每个服务器上的用户,服务器和用户都包含了通道信息.
交换服务器和接收服务器直接利用通道信息发送.

交换服务器把消息(906-Request)发送给目标服务器.目标服务器接收后还原原始消息发送给客户端.
转发消息具有和原始消息相同的时序控制属性.

。多目标用户消息发送:

通过IIM接口为其它插件使用.

取得user_list可优化,查询时已经得到服务器上用户的分布和通道信息,提高性能.

int CIMPlugin::Send(CMsg *msg,USER_LIST &user_list) {
	CQQ_SERVERID this_svr_id = mem_data_->GetLocalServer()->GetServerID();
	CWrappedMsg<> *pwm = new CWrappedMsg<>;
	pwm->msg = msg;
	CAutoMap<CQQ_SERVERID,vector<IUSERINFO*>* > svr_list; ///< 需要经由转发的服务器
	USER_LIST::iterator user_iter = user_list.begin();
	for(; user_iter != user_list.end(); user_iter++) {
		IUSERINFO *user = *user_iter;
		if (user->GetSvrID()==this_svr_id) { ///< 登录在当前服务器上的用户
			if((msg->GetMsgAttr()&EXTEND1_MASK)||user->IsReady()){
				pwm->connid.push_back(user->GetHandle());
			}
		}
		else {
			///< 需要转发的消息
			CQQ_SERVERID svr_id = user->GetSvrID();
			CAutoMap<CQQ_SERVERID,vector<IUSERINFO*>* >::iterator iter = svr_list.find(svr_id);
			if (iter==svr_list.end()) {
				svr_list.insert(make_pair(svr_id,new vector<IUSERINFO*>));
				iter = svr_list.find(svr_id);
			}
			iter->second->push_back(user);
		}
	}

	if (!svr_list.empty()) {
		CMsg *tmsg = msg->Clone();
		tmsg->SetDest64(0,0);
		///< 封装消息,发送给交换服务器

		CMsg *nmsg = new CMsg;
		if (msg->IsSeqCtrl()) {
			nmsg->SetMsgAttr(SEQ_CTRL_MASK);
			nmsg->SetSeqCtrlKeyValue(msg->GetSeqCtrlKeyValue());
		}
		nmsg->SetMsgType(MT_REQUEST);
		nmsg->SetMsgID(905);
		nmsg->SetSource(4,this_svr_id);
		nmsg->SetDest(3,0); ///< 

		CUMXHelper umxhelper;
		umxhelper.Attach(tmsg);
		umxhelper.Serialize();
		char *buffer = umxhelper.GetBuffer(true);
		unsigned long len = umxhelper.GetLength();
		nmsg->AddParam("msg",buffer,len);

		CRowset *rs = new CRowset;
		rs->SetFields("svr_id","handle","user_list",0);
		nmsg->AddRowset(rs);
		FieldDescriptor *fd3 = rs->GetFieldInfo(2);
		fd3->type = dtLongRowset;

		CAutoMap<CQQ_SERVERID,vector<IUSERINFO*>* >::iterator iter = svr_list.begin();
		int k = 0;
		while(iter!=svr_list.end()) {
			CQQ_SERVERID svrid = iter->first;
			ISERVERINFO *server = mem_data_->GetServer(svrid);
			if (server==0) {
				iter++;
				continue;
			}
			vector<IUSERINFO*> *v = iter->second;
			rs->AddRecord();
			char sz_svr_id[10],sz_handle[16];
			sprintf(sz_svr_id,"%lu",svrid);
			sprintf(sz_handle,"%lu",server->GetHandle());
			rs->SetFieldValue(k,0,sz_svr_id);
			rs->SetFieldValue(k,1,sz_handle);


			CDataBlock *data = new CDataBlock;
			data->type_ = dtLongRowset;
			CRowset *nrs = new CRowset;
			nrs->SetFields("orgid","userid","handle",0);
			vector<IUSERINFO*>::iterator it = v->begin();
			while(it!=v->end()) {
				IUSERINFO *user = *it;
				char sz_orgid[16],sz_user_id[16],sz_handle[16];
				sprintf(sz_orgid,"%lu",user->GetOrgID());
				sprintf(sz_user_id,"%lu",user->GetUserSerial());
				sprintf(sz_handle,"%lu",user->GetHandle());
				nrs->AddRecord(sz_orgid,sz_user_id,sz_handle,0);
				it++;
			}
			data->AttachRowset(nrs);
			rs->SetFieldValue(k,2,data);
			iter++;
		}
		network_->SendMsg(nmsg);
	}

	if (pwm->connid.size()==0) {
		delete pwm;
	}
	else 
		SendMsg(pwm);

	return 0;
}


。交换服务器中转

int CXSwitch::OnForwardMsg(CWrappedMsg<> *in,vector<CWrappedMsg<> *> &out,DISPATCH_RESULT &or) {
	or.auto_resp_ = false;

	CMsg *msg = in->msg;
	char *buffer = 0;
	unsigned long len = 0;
	msg->GetParam("msg",&buffer,len);
	if (buffer==0) {
		return -1;
	}
	CRowset *rs = msg->GetRowset(0);
	if (rs==0)
		return -1;
	int row = rs->GetRSMeta(RST_ROW_CNT);
	if (row==0)
		return 0;

	for (int i=0;i<row;i++) {
		const char *svr_id = rs->GetFieldValue(i,0);
		const char *handle = rs->GetFieldValue(i,1);
		CDataBlock *data = 0;
		rs->GetFieldValue(i,2,&data);
		if (data==0) 
			continue;
		CRowset *prs = data->rs_;
		if (prs==0)
			continue;

		CWrappedMsg<> *pwm = new CWrappedMsg<>;
		CMsg *nmsg = new CMsg;
		nmsg->SetMsgType(MT_REQUEST);
		nmsg->SetMsgID(906);
		nmsg->AddParam("msg",buffer,len);
		nmsg->AddRowset(prs);
		data->AttachRowset(0); 
		nmsg->SetMsgAttr(SEQ_CTRL_MASK);
		nmsg->SetSeqCtrlKeyValue(msg->GetSeqCtrlKeyValue());	
		pwm->msg = nmsg;
		pwm->connid.push_back(atol(handle));
		network_->SendMsg(pwm);

	}


	return 0;
}


。应用服务器中转

int CIMPlugin::OnForwardMsg(CWrappedMsg<> *in,vector<CWrappedMsg<> *> &out,DISPATCH_RESULT &or) {
	or.auto_resp_ = false;

	CMsg *msg = in->msg;
	char *buffer = 0;
	unsigned long len = 0;
	msg->GetParam("msg",&buffer,len);
	if (buffer==0) {
		return -1;
	}
	CUMXHelper umxhelper;
	int ret = umxhelper.Structuralize(buffer,len);
	if (ret) {
		return -1;
	}
	CMsg *tmsg = umxhelper.PopMsg();

	CRowset *rs = msg->GetRowset(0);
	if (rs==0)
		return -1;
	int row = rs->GetRSMeta(RST_ROW_CNT);
	if (row==0)
		return 0;

	CWrappedMsg<> *pwm = new CWrappedMsg<>;
	pwm->msg = tmsg;
	for (int i=0;i<row;i++) {
		const char *sz_handle = rs->GetFieldValue(i,2);
		CHANNEL_ID handle = atol(sz_handle);
		pwm->connid.push_back(handle);
	}
	network_->SendMsg(pwm);

	return 0;
}

 

2.备忘

2.1用户状态信息

用户状态运行时动态改变,需要反映到同事和好友的客户端上。
短时间的不一致不是问题,但必须有一种机制保证最终可以处于一致的状态.

通知消息的多段传输过程中,由于进程或网络原因,消息不一定会发送到用户的所有在线好友和同事.

数据库中记录的用户状态时准确的,新上线的同事和好友所得到的用户状态是正确的.


有2种解决方法:
。利用以内存数据库作为存储的SEMQ:在发送服务器和接受服务器之间确认,相关信息全部保存在内存中.
服务器负责自己写入的记录,重启时删除历史记录.
。利用事件中心:这是后话. 利用内存表作为存储可提高性能.

或者等待下一次用户状态改变. ***目前没有对此进行处理.

2.2聊天消息

从可靠性角度,在线聊天的消息也应该写入数据库后再发送,由SEMQ负责送达和控制时序.
否则,多段传输中的环节进程或网络故障都可能导致消息不能被送达。

但这种设计可靠但增加了负载和延迟.对于在线聊天消息,应该在客户端之间确认.

 

2.3UMX的一个缺陷

今天发现一个缺陷.

UMX支持参数为消息类型.
但是,消息不能是UMXT的消息.
这是因为UMXT没有重载CalcSize方法,导致序列化失败.

UMXT在Stream时才把传输属性加入消息包中.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值