客户端
基本原理
客户端(OpenHiva::ServiceClient)创建和发布消息的关键流程如图1所示。

- 对于同一个服务,同一个进程只能创建一个客户端,不同进程可以创建多个。
- 对于同一个服务,客户端和服务端的入参serviceName、reqDataSize、resDataSize、blockNum必须完全一致。
- reqDataSize、resDataSize、blockNum不能为0,消息大小msgSize=reqDataSize或resDataSize+消息头(64字节),单个消息大小msgSize不能超过30M,blockNum*msgSize需小于300M。
- 调用时必须确保入参正确,按约束使用,回调函数必须判断长度,不可越界。
- 一个客户端会起一个线程,用于接收响应数据。
- 由于涉及共享内存文件读写,服务端进程和客户端进程的用户权限必须相同,否则可能通信失败。
- 资源初始化。
用户在调用OpenHiva接口之前,需要先调用OpenHiva::Init接口进行初始化,返回值为0代表初始化成功,否则失败。
- 创建客户端。
- 构造客户端对象(ServiceClient)之前,必须先注册节点,即创建节点句柄OpenHiva::Node n。
- 然后调用OpenHiva::ServiceOptions里BuildShmOptions设置客户端参数。例如示例代码中的请求数据大小(reqDataSize)、响应数据大小(resDataSize)、数据缓存量(blockNum)。
- 最后使用创建的节点句柄n调用CreateClient接口创建CreateClient对象,设置要访问的服务名称(serviceName)、服务配置ServiceOptions等。
- 发送请求并接收响应。
创建OpenHiva::ServiceClient对象之后,通过OpenHiva::Ready检查框架节点是否正常。
如果正常,可以调用OpenHiva::ServiceClient::InvokeService接口发送请求(req)。服务端接收请求后,会调用回调函数处理,并将响应数据写入InvokeService接口入参res中,再返回给客户端。
调用OpenHiva::ServiceClient::InvokeService时注意返回值:
- 0:表示调用成功,响应数据成功写入参数res中。
- 1:表示调用失败,可能的原因包括:
- 服务端未使能或使能失败;
- 服务端回调处理过慢,导致超时;
- 同一个进程、同一个服务内创建了多个客户端;
- 客户端未使能或使能失败;
- 服务端、客户端入参不一致;
- 服务端、客户端进程的用户权限不一致。
此外,调用OpenHiva::ServiceClient::InvokeService还需注意:
- 调用过程中,不可以在其他线程对req和res的数据进行读写操作;
- 调用者必须保证指针对应的数据长度正确;
- 不支持并发调用,只能串行处理,InvokeService内部有锁保护。
- 资源释放。
进程结束前,依次释放ServiceClient对象和OpenHiva资源。资源释放后,定义的OpenHiva接口将无法使用。
示例代码
客户端创建和发送请求的关键步骤代码示例如下,仅供参考:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
#include <iostream>
#include <string>
#include <memory>
#include <unistd.h>
#include <sstream>
#include "open/init.h"
#include "open/node.h"
#include "open/service_client.h"
#include "open/service_options.h"
#include "std_msgs/include/StringMessage.h"
static constexpr uint32_t MAX_NAME_SIZE = 100;
static constexpr uint32_t CALL_TIMEOUT = 1000; // 单位ms
Hiva::StdMsgs::StringMessage req;
Hiva::StdMsgs::StringMessage res;
void Usage()
{
std::cout << "Usage: pmupload test_create_client nodeName topicName groupName bindType reqSize resSize blockNum msgCnt" << std::endl;
std::cout << "if argc==2, client will use default value: pmupload test_create_client testNodeClient testService testGroupClient 0 100 100 10 10" << std::endl;
std::cout << "bindType can be 0 1 2" << std::endl;
std::cout << "Pointing: req/res size must bigger than messageSize + 8, or serialize will fail" << std::endl;
}
int main(int argc, char **argv)
{
// 1. 资源初始化
std::string nodeName = "testNodeClient";
std::string groupName = "testGroupClient";
std::string serviceName = "testService";
OpenHiva::ScheduleType scheType = OpenHiva::ScheduleType(0);
int32_t reqDataSize = 100; // default size
int32_t resDataSize = 100; // default size
int32_t blockNum = 10; // default size
uint32_t callTimes = 10U;
int argNum = 9;
if (argc < argNum) {
Usage();
if (argc != 2) {
return 0;
}
} else {
nodeName = argv[1];
serviceName = argv[2];
groupName = argv[3];
scheType = (OpenHiva::ScheduleType)strtol(argv[4], NULL, 10);
reqDataSize = (int32_t)strtol(argv[5], NULL, 10);
resDataSize = (int32_t)strtol(argv[6], NULL, 10);
blockNum = (int32_t)strtol(argv[7], NULL, 10);
callTimes = (uint32_t)strtol(argv[8], NULL, 10);
}
// 定义线程组
std::vector<OpenHiva::ScheduleGroup> scheGrpVec;
OpenHiva::ScheduleGroup scheGrp;
scheGrp.groupName = groupName;
scheGrp.scheduleType = scheType;
scheGrpVec.push_back(scheGrp);
// 调用资源初始化接口
OpenHiva::Init(argc, argv, scheGrpVec);
HIVA_EVENT("service client init ok!");
HIVA_INFO("reqDataSize=%d.", reqDataSize);
HIVA_INFO("resDataSize=%d.", resDataSize);
HIVA_INFO("blockNum=%d.", blockNum);
// 2. 创建客户端
// 构造Node对象
OpenHiva::Node node(nodeName);
// 构造ServiceOptions
OpenHiva::ServiceOptions serOps;
serOps.BuildShmOptions(reqDataSize, resDataSize, blockNum);
// 通过NodeHandle对象调用CreateClient接口创建ServiceClient对象
std::shared_ptr<OpenHiva::ServiceClient> client = node.CreateClient(serviceName, serOps);
// 当构造ServiceClient对象失败时,调用Shutdown函数释放资源并退出
if (client == nullptr) {
HIVA_ERROR("create client failed");
OpenHiva::Shutdown();
return 0;
}
// 3. 发送请求并接收响应
uint32_t timeout = CALL_TIMEOUT;
// 判断Hiva节点状态。当节点是使能状态,返回true;当节点是shutdown或初始化失败状态,返回false,收发包均不能正确进行
for (uint32_t i = 0; i < callTimes && OpenHiva::Ready(); ++i) {
std::stringstream ss;
ss << "request: ==>Hello World " << i; // its size is 25 + i.size;
req.stringData = ss.str();
HIVA_WARN("%s", req.stringData.c_str());
timespec beginTime;
clock_gettime(CLOCK_MONOTONIC, &beginTime);
uint32_t ret = client->InvokeService(req, res, timeout);
HIVA_INFO("ans: [%s] ret[%d] ", res.stringData.c_str(), ret);
if (ret == 0U) {
timespec endTime;
clock_gettime(CLOCK_MONOTONIC, &endTime);
uint32_t useTime = ((endTime.tv_sec - beginTime.tv_sec) * 1000000) +
((endTime.tv_nsec - beginTime.tv_nsec) / 1000);
HIVA_INFO("InvokeService res_ans=%s, ret=%d, useTime=%u us",
res.stringData.c_str(), ret, useTime);
}
sleep(1);
}
// 4. 资源释放
client->Destroy();
OpenHiva::Shutdown();
return 0;
}
|
父主题: 消息请求-响应