Skip to content

shuai132/rpc_core

Repository files navigation

rpc_core

Build Status Build Status Release License

a tiny C++14 rpc library, supports all platforms (macOS, Linux, Windows, iOS, Android, etc.) and most microchips ( Arduino, STM32, ESP32/ESP8266, etc.)

Recommend TCP-based implementation: asio_net

Introduction

Full-feature rpc frameworks (e.g. gRPC and bRPC) are very complex and not suitable for use on embedded platforms.

This project offers a lightweight and user-friend rpc library that is better suited for one-to-one rpc calls. It supports all platforms and a wide range of microchips, including Arduino, STM32, ESP32/ESP8266, and more.

Note: This library only offers the protocol layer and API, it does not include the implementation of the transport layer. For TCP-based implementation: asio_net

Features

  • Header-Only
  • No-Schema
  • Support performance-limited platforms including microchips
  • Support any connection type (tcp socket, serial port, etc.)
  • High Performance Serialization, support most STL containers and user type
  • Serialization plugins implementations for flatbuffers and nlohmann::json
  • Support co_await, depend on C++20 and asio, or custom implementation
  • Support subscribe async callback, async coroutine, and custom scheduler
  • RAII-based dispose for automatic cancel request
  • Support timeout, retry, cancel api
  • Comprehensive unittests

TCP-based implementations

  • C++

    • asio_net: based on asio
      Support macOS, Linux, Windows, iOS, Android, etc. and can be used on MCUs that support asio, such as ESP32.
  • Rust

Requirements

  • C++14
  • Provide your connection implementation: connection
    NOTICE: complete data packets are required for data transmission, such as websocket.
    If using tcp socket, serial port, etc., message pack and unpack need to be implemented. Or you can use stream_connection.
  • Optional: C++20 (for coroutine api, co_await async_call)

Usage

  • async callback:
// The Receiver
rpc->subscribe("cmd", [](const std::string& msg) -> std::string {
    assert(msg == "hello");
    return "world";
});

// The Sender
rpc->cmd("cmd")
    ->msg(std::string("hello"))
    ->rsp([](const std::string& rsp) {
      assert(rsp == "world");
    })
    ->call();
  • async coroutine:
// The Receiver
rpc->subscribe("cmd", [&](request_response<std::string, std::string> rr) -> asio::awaitable<void> {
    assert(rr->req == "hello");
    asio::steady_timer timer(context);
    timer.expires_after(std::chrono::seconds(1));
    co_await timer.async_wait();
    rr->rsp("world");
}, scheduler_asio_coroutine);

// The Sender
// use C++20 co_await with asio, or you can use custom async implementation, and co_await it!
auto rsp = co_await rpc->cmd("cmd")->msg(std::string("hello"))->async_call<std::string>();
assert(rsp.data == "world");

Inspect the code for more details: rpc_s_coroutine.cpp and rpc_c_coroutine.cpp

  • Addition:
  1. msg and rsp support any serializable type, refer to Serialization.
  2. Detailed usages and unittests can be found here: rpc_test.cpp
  3. There is an example shows custom async impl: rpc_c_coroutine.hpp

Serialization

High-performance and memory-saving binary serialization.

  • api is very simple to use: include/rpc_core/serialize_api.hpp
  • usage and comprehensive unittest: test/test_serialize.cpp
  • the design balance cpu and memory usage, and zero-copy if possible.
  • std::string is used as inner data container, it's serialize/deserialize is zero-overhead. so, it is recommended to use std::string whenever possible, using it to store binary data is also a good choice.

Why design a new serialization

Fist of all, I want to keep rpc_core library standalone, without any dependencies, except for STL.

Moreover, these serialization libraries do not align with my design goals:

  • protobuf library is too large for some platforms, and it's Varint, Zigzag, and GZIP will use a lot of cpu.
  • msgpack has similarity reason to protobuf.
  • flatbuffers serialized data is too large.

Of course, when communicating across languages, it is recommended to use the above serialization libraries!

Finally, it also provides a way to use thirdparty serialization libraries directly, refer to Serialization Plugins.

Usage

For example, user data:

struct Type {
  uint8_t id = 1;
  uint8_t age = 18;
  std::string name = "test";
};

json: {"id":1,"age":18,"name":"test"}

library bytes
json 31
flatbuffers 44
protobuf 10
msgpack 8
rpc_core 8

Serialization Plugins

  • flatbuffers.hpp
    Supports using types generated by flatbuffers directly as message
    (add the option --gen-object-api when using flatc)

  • json_msg.hpp
    Supports using types supported by nlohmann/json directly as message
    (the to_json/from_json rules in nlohmann/json need to be satisfied, and use DEFINE_JSON_CLASS).

  • json.hpp
    A flexible way to use nlohmann/json

License

This project is licensed under the MIT license.

Links

  • Implementation based on asio: asio_net

  • Implementation suitable for ESP8266: esp_rpc