浅谈通信服务基类设计

  • Post author:
  • Post category:其他


互联网时代,通信服务非常常见,看过一些开源工程、工作过公司的工程,对于应用层基础类的设计相对于通信库而言,欠缺很多,导致应用层代码相对逻辑不够不清晰,也给以后的维护带来一定的难度。本篇就是讲述通信服务在上层应用基类设计的一些个人观点和看法,仅起到抛砖引玉,给出一个不同的设计,各位读者如有不同的设计思路,可以留言讨论。

一、基类设计范畴。

基类设计范畴较广,所以在本篇开启前,需要确定设计范畴,以免丢失主要目标。一般通信服务工作处理流程都是从底层通信库接收业务数据包开始,经由底层回调应用层进行逻辑处理,最后再由底层向业务发起者返回相关业务数据包,结束整个处理流程。底层通信库一般独立封装,本人博客有相关介绍篇章,有意向者可自行查找阅读。本篇讲述就是应用层基类的设计,看过很多通信服务代码,而应用层基类设计基本是空白状态,有直接用通信类中函数实现的,也有用函数指针实现的,各种方式都有,我个人认为逻辑都不是很清晰,对于代码阅读和后续维护都不是太友好,所以个人认为需要设计几个框架式的基类来统一和规范这种情况。

二、设计原则。

1. 既然是因为逻辑不清晰带来的问题,那么设计原则,肯定就需要解决这个不清晰的问题。通信服务业务处理逻辑不尽相同,既然是框架性质的基类,那么肯定是不能包含具体业务逻辑,只能体现通信服务通用的逻辑处理流程。

2. 通信服务有其相应的各种指标,其中性能指标是衡量通信服务处理性能最为重要的指标,在设计框架性质基类时,需要考虑如何便利的将服务处理性能信息进行收集和统一处理。本人供职过得公司,对于服务性能信息收集,及统一处理,基本都处于缺失状态,本篇针对该问题,仅仅引入概念,不做深入解决方案设计及处理,性能收集及处理将会有专门的独立篇章来讲解。

三、设计前的分析。

1. 如上所述通信服务的一般处理流程,底层通信库,已经独立封装设计好,无需再进行设计,也不可能有机会再设计,那么所以的设计将集中在应用层的逻辑处理中,但是基类肯定不能涉及具体业务逻辑,否则将失去通用性而失去扩展能力。这似乎进入了一个死胡同,面对的该环节好像只能处于这种现状。但是深入地想想,应用层处理,一般流程是怎样的呢?首先是解析业务数据包,然后进行专门的业务逻辑处理,最后进行数据包的发送,完成整个处理流程。根据上述,我们至少可以拆分成两个部分,业务逻辑处理,和处理后的返回数据包发送。拆分为两个环节,总比合成在一个大函数内,混合在一个通信类中要清晰的多吧。

2. 既然需要进行性能数据的统一处理,那么必要需要一个所有数据包都需要经过的一个集中处理关口,才能办到。这个好像很多方法都可以办到,比如调用一个共同的方法,就能够很方便地实现了,这是最为本质的实现方式,至于具体的实现形式,其实不重要了,只要付出的性能代价不高,基本都可以接受。

四、本人的解决方案。

1. 根据上述的分析,我们可以设计两个基类将应用层处理切分成两个部分,使得处理逻辑更进一步清晰。一部分针对应用逻辑处理,一部分针对逻辑处理后的数据返回发送。根据基类功能划分,强制将逻辑处理代码放在逻辑处理类中,数据返回发送代码放在发送基类中,使得代码结构更加清晰,那部分出问题,就修改那部分代码,降低维护成本。同时也能很好的将代码进一步按照功能模块进行切割,相同处理,相同模块,在设计时稍作复用考虑,既能轻松实现复用目的。这是进一步分解降低代码粒度带来的好处。现在有了逻辑处理基类、数据发送处理基类,好像缺了点什么,依然是两个毫无关联,散乱的基类,缺少连接的纽带。本人在实际的设计时,也感觉到这一点,同时又需要考虑,不同业务处理不同应用场景,使用的数据都不尽相同,所以需要设计一个数据基类用来容纳接收的数据,和业务处理,数据发送过程中需要使用到的数据,用该数据基类充当中间层和粘合剂,将逻辑处理和数据发送连接起来。

2. 本人对于Template的理解,是这么认为的。模板分为数据模板和逻辑模板两类,数据模板着重在于数据的存储和维护,着重在数据结构的维护使用上。逻辑模板着重于逻辑处理流程,所有逻辑处理一致的都可复用逻辑模板实现处理。所以本人使用模板来实现统一的集中处理。

五、代码片段。

本小节,就不做过多描述,将直接给出大致的代码轮廓。

1. 数据基类

class CDealDataBase
{
public:
	
	CDealDataBase(Context *pContext, PACKDATA * pPack,
		int PackID):m_pContext(pContext), m_pPack(pPack), m_PackID(PackID){};
	~CDealDataBase(void);

    //设置时间戳
    void SetDebugTime(int aidx);
protected:
    
    //通信对象指针
	CNetContext * m_pContext;

    //处理业务数据包
	PACKDATA * m_pPack;

private:
	CDealDataBase(void);
    
    //时间戳存储结构
    INT64 m_i64Time[5];
};

2. 逻辑处理基类

class CLogicDealBase
{
public:
	CLogicDealBase(void);
	virtual ~CLogicDealBase(void);

    //逻辑处理方法,需子类继承实现
	virtual void DealLogic(CDealDataBase * pData);
};

3. 发送处理基类

class CSendDealBase
{
public:
	CSendDealBase(void);
	virtual ~CSendDealBase(void);

    //发送处理方法,需子类继承实现
	virtual bool DealSendMsg(CDealDataBase * pData);
};

4. 集中处理模板

template<typename logic, typename data, typename send>
bool DealMsg(logic * pLogic, data * pData, send *pSend)
{
	//逻辑处理
    pData->SetDebugTime(0);
	pLogic->DealLogic(pData);
    pData->SetDebugTime(2);
	
    //发送处理
	bool ret = pSend->DealSendMsg(pData);
    pData->SetDebugTime(4);
	
    //性能信息统一处理
	CStatisticsMgr::GetStatisticsObj()->UpdateMsgData(pData->m_PackID, pData->m_SubID, pData->GetDealTime());
	return ret;
}

5. 使用示例片段

	STRU_BASE_PACK pack((char*)aoPackData.m_pData);

	switch (pack.m_MsgID)
	{
	//心跳数据包
	case DEF_CL_MSGID_HEARTBEAT:
		{
			CHeartBeatDealData data(this, aoPackData, pack.m_MsgID);
			CHeartBeatDealLogic logic;
			CHeartBeatDealSend send;
			DealMsg(&logic, &data, &send);
		}
		break;
	}

六、总结。

主要代码和示例片段,如上以给出,代码并不复杂,其实并无实质上的处理代码,设计更多是体现一种理念和编码时需要注意遵守的原则。本篇已经完成主要工作,但还是有几点需要再次复述:

1. 再次重申,本篇是个人在通信服务开发中,觉得有必要规范的一部分,仅代表个人的看法和观点。如有更好的建议,请留言沟通,本篇也旨在抛砖引玉,欢迎各位进行讨论。

2. 关于集中处理模板中的性能统一处理模块,本文并未一并给出,该部分将在后续单独进行分析,设计,和讲解,如有对该部分感兴趣的读者,请关注本博主相关篇章,同样欢迎讨论。



版权声明:本文为wyc7610原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。