zoukankan      html  css  js  c++  java
  • 业务思考-异步业务组织

    在最近的开发过程中,遇到这样一个问题,细想蛮有意思的,在此记录一下。

    在进行具体讨论前,先将业务抽象,方便后续讨论。比方说,有一个业务Business,它依赖三个子命令ABC,每个命令都是异步收发且有先后顺序要求,暂且以A->B->C为依赖关系,每个指令都可能出错(响应超时或者响应错误),并且只要有一个出错,后续请求不必再发送,直接返回错误。

    以上是业务背景,下面来看下如何组织上述逻辑关系。

    代码组织方式

    按照异步收发框架以及需求,这块逻辑,可以这样写:

    void Business()
    {      // 第一步,发送命令A
    	SendCommandA();
    }
    
    // 底层异步响应回调函数
    void OnReply(CReq* pReq)
    {
    	switch(pReq)
    	{
    		case CommandA:
    			{     // 收到A响应后,正确则继续发送B命令,错误则回复错误。
    				pReq->isRespRight() ? SendCommandB() : OnFinish(ECheckResult_Error, "指令A的错误信息");  
    			}
    			break;
    		case CommandB:
    			{
    				pReq->isRespRight() ? SendCommandC()  : OnFinish(ECheckResult_Error, "指令B的错误信息");
    			}
    			break;
    		case CommandC:
    			{
    			     pReq->isRespRight() ? OnFinish(ECheckResult_OK, "正确结果")  : OnFinish(ECheckResult_Error, "指令B的错误信息");		
    			}
    			break;
    		default:
    			assert(0);	// 未知情况
    			break;
    	}
    }
    
    

    上述写法,直观明了,前一个指令响应成功,再发下一个指令,否则提示错误。这种写法不好之处在于指令与指令之间耦合太紧。改进方案是利用消息来拆分调用关系。例如收到CommandA的正确响应,发出一个类似OnReceiveCmdA的异步消息。在消息中,带上CommandA指令响应的相关信息。业务自身订阅该消息并进行后续处理。这样一来,可降低命令之间的依赖,增加灵活性。

    这里,我想提到的是另外一点,在工程中,看到其他同事有写这样的响应处理:

    // 其他的省略
    	case CommandA:
    	{
    		if (pReq->isRespRight())
    		{
    			OnFinish(EQryResult_CommandA_OK, pReq->GetRightRet());
    		}
    		else
    		{
    			OnFinish(EQryResult_CommandA_SysError, pReq->GetErrorMsg());
    		}
    	}
    	break; 	
    }
    
    enum EQryResult
    {
    	EQryResult_CommandA_OK,
    	EQryResult_CommandB_OK,
    	// ...
    	EQryResult_CommandA_SysError
    }
    
    void OnFinish(EQryResult eRet, string& strMsg)
    {
    	switch(eRet)
    	{
    		case EQryResult_CommandA_OK:
    			{
    				SendCommandB();
    			}
    			break;
    		case EQryResult_CommandA_SysError:
    			{
    				// tip user 
    			}
    			breakl
    	}
    }
    
    

    上面这种组织逻辑的思路,是针对每种情况,无论是正确的还是错误的,都定义对应的类型码,提供一个统一的OnFinish处理函数入口。通过入参,配合类型码来完成后续的操作。

    这种组织逻辑的方法,笔者感觉不好,思考了下,有以下几点不好的地方:

    • 混淆职责。这种将正确处理和错误处理放在同一个函数中,混淆不同的职责。笔者认为,在这种异步处理,错误可以统一处理,但正确情况,各个指令有各自的应用场景,不适合统一处理。
    • 可扩展性差。后续随着业务的变动,可能会修改已有分支或者新增分支,不管如何,这样的修改,对于测试、运维或者产品来说,属于黑盒修改,即使在修改说明中明确了改动方法,这也只稍微缓解了测试人员的测试焦虑,增加后续的测试成本。
    • 可读性差。在异步消息处理时,上下文信息很重要,上面这种做法,在响应时又封装一层,看似方便了调用方,实则苦了实现方和阅读方。这种只简化了调用方的做法,隐藏后续紧密关联的业务处理,让阅读者要跳入到OnFinish函数,再根据具体入参,找到对应的处理分支,才能检查正确性,这种封装,降低代码可读性,不推荐。

    小结

    这种异步处理,建议错误情况统一处理,正确情况,通过消息来隔离各个具体的处理流程,每一个具体的处理,用类似OnRespCommandA这样的函数包裹起来,明确表明职责,这是较为稳妥的组织方法。

    作者:浩天之家
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
    Top
    收藏
    关注
    评论
  • 相关阅读:
    O(1)时间求出栈内元素最小值
    静态查找>顺序、折半、分块查找
    字符串的最大重复数
    数据结构>栈
    排序>归并排序
    动态查找>二叉查找树(Binary Search Tree)
    数据结构>图的存储结构
    数据结构>图的连通性和最小生成树
    图片的轮廓
    数据结构>队列
  • 原文地址:https://www.cnblogs.com/cherishui/p/14864411.html
Copyright © 2011-2022 走看看