分包问题说明
由于tcp网络缓冲区大小的限制等因素,当响应包超过256KB,tcaplus后端会触发分包。但保证分包不会切分单个记录(不管这个记录多大),例如,响应包的3个记录分别是10KB、251KB、1MB,会分为2个包返回,即(10KB、251KB)和(1MB)。
1. 分包命令字
一般多包返回的命令字(一次读多条记录,或者一次写多条记录同时设置了ResultFlag返回旧记录)都有可能触发分包,所以需要判断分包场景
- BatchGet,批量查询,超过256KB必然分包
- BatchUpdate、BatchReplace、BatchDelete,批量修改或删除,设置返回旧记录的flag时,会返回批量的旧记录,默认不分包
- GetByPartKey,通过本地索引查询,返回批量记录,超过256KB必然分包
- UpdateByPartKey,DeleteByPartKey,会返回更新或删除的key,超过256KB必然分包
- ListGetAll,List表查询List Key下所有的记录,默认不分包
- ListGetBatch,List表查询List key下的批量记录,默认不分包
- ListReplaceBatch、ListDeletBatch、ListAddAfterBatch修改或删除ListKey下的批量元素,设置返回旧记录的flag时,会返回批量的旧记录,默认不分包
- 全局索引查询,当表配置了全局索引,可通过SQL查询返回批量的记录,超过256KB必然分包
总之方便记忆:
- 要分包,request显示调用pstRequest->SetMultiResponseFlag(1);
- 不要分包,request显示调用pstRequest->SetMultiResponseFlag(0); //BatchGet GetByPartKey UpdateByPartKey DeleteByPartKey 全局索引查询,必然分包,设置无效
2. 分包处理
对于分包返回的命令字需要特殊处理
请求处理,在发送请求时需要设置下允许分包标记,有些请求默认没有设置分包标记,所以用户需要手动调用允许分包,否则不会分包,超过256k的包会丢掉(必然分包的除外)
TcaplusService::TcaplusServiceRequest* pstRequest = g_stTcapSvr.GetRequest(TABLE_NAME); if (NULL == pstRequest) { tlog_error(g_pstLogHandler, 0, 0, "g_stTcapSvr.GetRequest(%s) failed.", TABLE_NAME); return -1; } int32_t iRet = pstRequest->Init(TCAPLUS_API_BATCH_GET_REQ, NULL, 0, 0, 0, 0); if(0 != iRet) { tlog_error(g_pstLogHandler, 0, 0, "pstRequest->Init(TCAPLUS_API_BATCH_GET_REQ) failed, iRet: %d.", iRet); return iRet; } //可以设置异步ID,响应带回 uint64_t asyncId = 99; pstRequest->SetAsyncID(asyncId); //request在init之后设置分包标记,1允许分包,否则不允许 pstRequest->SetMultiResponseFlag(1);
响应处理。由于C++的api是异步模式,一般用户通过异步id将响应和请求一一对应,对于分包场景下的响应处理,需要通过响应中的标记(HaveMoreResPkgs 是否还有后续的包)来判断这个id的请求是否完成
uint64_t asyncId = pstTcapRspRcved->GetAsynID(); int32_t dwResult = pstTcapRspRcved->GetResult(); // 响应失败 if( 0 != dwResult) { //该异步id的任务失败了,可以直接做失败处理 //比如从等待队列删除该异步id return; } //响应成功,读取该响应中的记录 int32_t dwRecordCount = pstTcapRspRcved->GetRecordCount();//获取结果中记录的条数 const TcaplusServiceRecord * pstTcapRecord = NULL; int32_t dwCount = 0; while(dwCount++ < dwRecordCount) { int32_t iRet = pstTcapRspRcved->FetchRecord(pstTcapRecord); if(0 != iRet || NULL == pstTcapRecord) { printf( "FetchRecord failed, iRet: %d \n", iRet); continue; } PLAYERONLINECNT stPLAYERONLINECNT; memset(&stPLAYERONLINECNT, 0, sizeof(stPLAYERONLINECNT)); // 获得基于TDR描述的record数据 iRet = pstTcapRecord->GetData(&stPLAYERONLINECNT, sizeof(stPLAYERONLINECNT)); if(0 != iRet) { tlog_error(g_pstLogHandler, 0, 0, "GetData failed, iRet: %d .", iRet); return -4; } //业务处理这条记录stPLAYERONLINECNT } //后续包判断 if (1 == pstTcapRspRcved->HaveMoreResPkgs()) { //注意此时并不表示该异步ID的任务完成了!!! //触发了分包,还有后续的包,继续收包,等待下一个响应 } else { //没有后续的包了,该异步ID的任务完成了 //比如可以从等待队列中完成该ID的任务 }