Client

Principles

Figure 1 Client creation and request process shows the process of creating and publishing a message on the client (OpenHiva::ServiceClient).

Figure 1 Client creation and request process

  • For the same service, only one client can be created for the same process, and multiple clients can be created for different processes.
  • For the same service, the input parameters such as serviceName, reqDataSize, resDataSize, and blockNum on the client must be the same as those on the server.
  • The values of reqDataSize, resDataSize, and blockNum cannot be 0. The message size is calculated as follows: msgSize = reqDataSize or resDataSize + Message header (64 bytes). The size of a single message cannot exceed 30 MB. The product of blockNum and msgSize must be less than 300 MB.
  • Ensure that the input parameters are correct and used based on constraints. The callback function must check the length, which cannot exceed the threshold.
  • A client starts a thread to receive response data.
  • The server process and client process must have the same user permission because shared memory file read and write are involved.
  1. Initialize resources.

    Before invoking the OpenHiva interface, invoke the OpenHiva::Init interface for initialization. If the return value is 0, the initialization is successful. Otherwise, the initialization fails.

  2. Create a client.
    1. Before constructing a client object (ServiceClient), you must register a node, that is, create a node handle OpenHiva::Node n.
    2. Invoke BuildShmOptions in OpenHiva::ServiceOptions to set client parameters, such as, reqDataSize, resDataSize, and blockNum in Sample Code.
    3. Use the created node handle n to invoke the CreateClient interface to create the CreateClient object, set the name of the service to be accessed (serviceName), and configure ServiceOptions.
  3. Send a request and receive the response.

    After the OpenHiva::ServiceClient object is created, check whether the node is normal by checking whether OpenHiva::Ready() is true.

    If the node is normal, invoke the InvokeService interface to send a request (req). After receiving the request, the server invokes the callback function to process the request, writes the response data to the input parameter res of the InvokeService interface, and returns the response data to the client.

    Pay attention to the return value when invoking the InvokeService interface.

    • 0: The invoking is successful, and the response data is successfully written to the res parameter.
    • 1: The invoking fails. The possible causes are as follows:
      • The server is not enabled or fails to be enabled.
      • The callback processing on the server is too slow. As a result, the callback times out.
      • Multiple clients are created in the same process and service.
      • The client is not enabled or fails to be enabled.
      • The input parameters of the server and client are inconsistent.
      • The user rights of the server process are inconsistent with those of the client process.

    In addition, pay attention to the following points when invoking the InvokeService interface:

    • During the invoking process, other threads cannot read or write the req and res data.
    • The invoker must ensure that the length of the data corresponding to the pointer is correct.
    • Concurrent invoking is not supported. Only serial processing is supported. InvokeService has internal lock protection.
  4. Release resources.

    Before the process ends, the ServiceClient object and OpenHiva resources are released in sequence. After resources are released, the defined OpenHiva interfaces cannot be used.

Sample Code

The following is a code example of key steps for creating and sending a request on the client, which is for reference only:

#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;                      // unit: 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. Initialize resources.
    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);
    }
    // Define a thread group.
    std::vector<OpenHiva::ScheduleGroup> scheGrpVec;
    OpenHiva::ScheduleGroup scheGrp;
    scheGrp.groupName = groupName;
    scheGrp.scheduleType = scheType;
    scheGrpVec.push_back(scheGrp);
    // Invoke the resource initialization interface.
    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. Create a client.
    // Construct the Node object.
    OpenHiva::Node node(nodeName);
    // Construct ServiceOptions.
    OpenHiva::ServiceOptions serOps;
    serOps.BuildShmOptions(reqDataSize, resDataSize, blockNum);
    // Invoke the CreateClient interface through the NodeHandle object to create the ServiceClient object.
    std::shared_ptr<OpenHiva::ServiceClient> client = node.CreateClient(serviceName, serOps);
    // When constructing the ServiceClient object fails, invoke the Shutdown function to release resources and exit.
    if (client == nullptr) {
        HIVA_ERROR("create client failed");
        OpenHiva::Shutdown();
        return 0;
    }

    // 3. Send the request and receive the response.
    uint32_t timeout = CALL_TIMEOUT;
    // Check the Hiva node status. If the node is enabled, true is returned. If the node is shut down or fails to be initialized, false is returned. In this case, packets cannot be sent or received correctly.
    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. Release resources.
    client->Destroy();
    OpenHiva::Shutdown();
    return 0;
}