关于rpc:杂记

53次阅读

共计 4209 个字符,预计需要花费 11 分钟才能阅读完成。

这几天看一个 rpc 库 (https://github.com/rpclib/rpc…,server 端的注册函数返回类型以及参数类型能够任意,client 端只有填充对应类型的参数即可实现调用:
Server:

#include <iostream>
#include "rpc/server.h"

void foo() {std::cout << "foo was called!" << std::endl;}

int main(int argc, char *argv[]) {
    // Creating a server that listens on port 8080
    rpc::server srv(8080);

    // Binding the name "foo" to free function foo.
    // note: the signature is automatically captured
    srv.bind("foo", &foo);

    // Binding a lambda function to the name "add".
    srv.bind("add", [](int a, int b) {return a + b;});

    // Run the server loop.
    srv.run();

    return 0;
}

Client:

#include <iostream>
#include "rpc/client.h"

int main() {
    // Creating a client that connects to the localhost on port 8080
    rpc::client client("127.0.0.1", 8080);

    // Calling a function with paramters and converting the result to int
    auto result = client.call("add", 2, 3).as<int>();
    std::cout << "The result is:" << result << std::endl;
    return 0;
}

这在 c ++ 中想要实现是不容易的,因为 c ++ 没有运行时反射,而 server 显然只有在运行时能力晓得 client 想要执行哪个函数。并且 server 如何存储注册的参数类型以及返回类型各不相同的函数呢? 尽管有 std::function,但函数参数类型和返回类型决定了这个函数的动态类型,c++ 容器寄存的对象必须具备雷同类型(std::any 相似机制不能解决这个问题,因为当你想要获取真正的类型时,仍然须要编译期就指定,换言之 c ++ 提供的类型变换都是在编译期指定,但 rpc 调用中只有在运行期能力取得类型信息,这是基本的矛盾)。
一次 rpc 调用须要以下步骤:
client 指定函数名称,以及各个参数 —–rpc 库将函数名称与各个参数序列化,通过网络发送给 server 端 —–server 承受到网络数据,反序列化失去调用函数的名称和参数序列 —– 通过函数名称找到注册的函数对象,并传递参数序列,调用对应函数,拿到函数返回值并序列化,通过网络发送给 client 端 —–client 收到网络数据,反序列化并返回给 rpcc 使用者。
利用可变参数列表将 client 端的函数名称和参数生成 std::tuple,std::tuple-> 二进制数据须要序列化库提供,失去序列化后的二进制数据,通过网络 io 发送给 server,server 同样通过序列化库将二进制数据反序列化为 std::tuple,利用 c ++14 的 std::integer_sequence 开展 std::tuple 作为函数参数实现调用(https://www.cnblogs.com/abeli…),利用 lambda 作类型擦除,记录不同的函数对象(之前看过一篇文章将 c ++ 的几种类型擦除办法,说 lambda 是最强的类型擦除办法,明天终于领会到什么意思了,参考:https://github.com/rpclib/rpc… 如何将不同类型的函数存储在同一动态类型中)。

上面是一些记录的尝试的代码,很乱,次要是尝试上述办法的应用:

#include <tuple>

#include <iostream>

#include <string>

#include <utility>

#include <unordered_map>

class Test {

public:

 int age;

 explicit Test(int a) : age(a) {}

 Test(const Test& t) {

 age = t.age;

 std::cout << "copy" << std::endl;

 }

 Test(Test&& t) noexcept {

 age = t.age;

 std::cout << "move" << std::endl;

 }

};

int method(Test&& a, const Test& b) {return a.age + b.age;}

std::string strappend(std::string str1, std::string str2) {std::string result(str1);

 result.append(str2);

 return result;

}

template<typename F, typename T, size_t... l>

decltype(auto) apply_impl(F& f, T&& t, std::index_sequence<l...>) {return f(std::forward<typename std::tuple_element<l, std::decay_t<T>>::type>(std::get<l>(std::forward<T>(t)))...);

}

template<typename F, typename T>

decltype(auto) apply(F& f, T&& tuple) {return apply_impl(f, std::forward<T>(tuple), std::make_index_sequence<std::tuple_size<std::decay_t<T>>::value>());

}

template<typename Rt, typename... Args>

struct func_traits {

 using ReturnType = Rt;

 using ArgsType = std::tuple<typename std::decay_t<Args>::type...>;

};

class elti {

public:

 // just for test.

 template<typename T>

 T getElementAs(size_t i) {return T();

 }

 template<typename TupleType>

 void Convert(TupleType& tuple) {ConvertInner<0>(tuple);

 }

private:

 template<size_t index, typename TupleType>

 void ConvertInner(TupleType& tuple) {

 using nthType = typename std::tuple_element<index, TupleType>::type;

 nthType obj = getElementAs<nthType>(index);

 std::get<index>(tuple) = std::move(obj);

 ConvertInner<index + 1>(tuple);

 }

};

template<typename TupleType>

class MyTupleElement {

 template<typename Tp>

 class TupleElementInner;

 template<typename Arg, typename... ArgsTypes>

 class TupleElementInner<std::tuple<Arg, ArgsTypes...>> : public TupleElementInner<std::tuple<ArgsTypes...>> {

 public:

 using BaseType = TupleElementInner<std::tuple<ArgsTypes...>>;

 constexpr static size_t index = BaseType::index + 1;

 template<typename Tp>

 static void PrintElement(Tp& tuple) {std::cout << std::get<index>(tuple) << std::endl;

 BaseType::PrintElement(tuple);

 }

 };

 template<typename LastArg>

 class TupleElementInner<std::tuple<LastArg>> {

 public:

 constexpr static size_t index = 0;

 template<typename Tp>

 static void PrintElement(Tp& tuple) {std::cout << std::get<index>(tuple) << std::endl;

 }

 };

public:

 static void PrintTupleElement(TupleType& tuple) {TupleElementInner<TupleType>::PrintElement(tuple);

 }

};

int main() {

 using namespace std::literals::string_literals;

 auto tu1 = std::make_tuple(Test(2), Test(3));

 auto result1 = apply(method, tu1);

 auto tu2 = std::make_tuple(std::string("chloro"), std::string("pn"));

 //auto re2 = apply(strappend, tu2);

 //std::cout << result1 << std::endl;

 //std::cout << re2 << std::endl;

 MyTupleElement<std::tuple<std::string, std::string>>::PrintTupleElement(tu2);

 return 0;

}

通过开展 tuple 包到函数调用,参考 url 的那个比较简单,这里实现了具备完满转发能力的形式。

正文完
 0