RPC之rpclib

RPC

远程过程调用(Remote Procedure Call,RPC)是一种计算机通信协议,它允许一台计算机(客户端)通过网络向另一台计算机(服务器)请求服务或调用程序,就好像这个程序是在本地执行一样。RPC 隐藏了网络通信的细节,使得开发分布式应用程序更为简单。RPC 强调的是程序间的交互,而不是底层的传输机制。

RPC 系统一般包括以下几个关键组成部分:

  1. 客户端:发起远程调用请求的一方。
  2. 服务器:提供服务或执行程序代码响应远程调用的一方。
  3. 存根(Stub):在客户端和服务器上分别存在的存根提供了一个代理,使得远程调用看起来如同本地调用一样。客户端存根打包(序列化)调用信息并发送到服务器,服务器存根解包(反序列化)并执行程序,然后返回结果。
  4. 传输层:RPC 使用的网络传输协议,常见的有 TCP/IP 和 UDP。

RPC 的工作流程大致如下:

  1. 客户端程序调用本地的客户端存根。
  2. 客户端存根打包(序列化)调用的方法和参数,并通过网络发送到服务器。
  3. 服务器上的服务器存根接收到请求,解包(反序列化)出方法和参数。
  4. 服务器存根按照解包出来的信息调用本地的程序。
  5. 服务器程序执行并将结果返回给服务器存根。
  6. 服务器存根再次打包这些结果,并发送回客户端存根。
  7. 客户端存根解包响应,并将结果回传给客户端程序。

RPC 的优点包括:

  • 抽象化的复杂性:RPC 抽象了网络间通信的复杂性,开发者可以像调用本地程序一样调用远程程序,无需关心底层通信协议的实现。
  • 语言无关性:很多 RPC 系统支持不同编程语言之间的调用,这使得构建多语言的分布式系统变得可能。
  • 高效性:RPC 可以高效地处理客户端和服务器之间的通信,特别是一些支持异步调用的 RPC 系统。

同时,RPC 也有一些缺点:

  • 网络依赖:RPC 依赖网络连接,网络延迟和中断可能会影响系统的稳定性和相应时间。
  • 复杂性和调试:虽然 RPC 抽象了分布式通信的复杂性,但在调试和排查分布式系统错误时可能会比本地程序更加困难。
  • 安全性考虑:通过网络公开方法可能增加安全风险,务必考虑适当的认证、授权和加密措施。

RPC 技术的应用非常广泛,从传统的客户端-服务端应用到现代的微服务架构,RPC 在很多分布式系统中都发挥着关键作用。

概叙

        rpclib 是一个用于 C++ 的 RPC(远程过程调用)库,提供了客户端和服务器的实现。它使用现代 C++14 构建,因此需要较新的编译器。主要亮点如下:

  • 暴露你的程序函数,使其可以通过 RPC(远程过程调用)被调用(来自实现了 msgpack-rpc 的任何语言)
  • 通过 RPC 调用函数(属于用任何语言编写的程序)
  • 无需学习 IDL(接口定义语言)
  • 在你的构建中无需集成代码生成步骤,只需 C++

Demo

        主要的核心设计,是通过绑定、回调返回不同的参数类型实现rpc的CS架构。

基本使用

rpc/server端

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

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

void bad(int x) {
    if (x == 5) {
        throw std::runtime_error("x == 5. I really don't like 5.");
    }
}

int main() {
    // Create a server that listens on port 8080, or whatever the user selected
    rpc::server srv("0.0.0.0", rpc::constants::DEFAULT_PORT);

    // 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; });

    // Throwing an exception will cause the server to write
    // an error response. This call will make it also
    // suppress the exception (note that this is not default
    // because this behavior might hide errors in the
    // code).
    srv.suppress_exceptions(true);
    srv.bind("bad", &bad);

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

    return 0;
}

rpc/client端

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

int main() {
    rpc::client client("127.0.0.1", rpc::constants::DEFAULT_PORT);
    auto result = client.call("add", 2, 3).as<int>();
    std::cout << "The result is: " << result << std::endl;
    return 0;
}

多元化回调类型

mandelbrot/server

#include <iostream>
#include <math.h>
#include <time.h>

#include "rpc/server.h"
#include "mandelbrot.h"

int mandelbrot(double cr, double ci, int max_iterations) {
    int i = 0;
    double zr = 0.0, zi = 0.0;
    while (i < max_iterations && zr * zr + zi * zi < 4.0) {
        double temp = zr * zr - zi * zi + cr;
        zi = 2.0 * zr * zi + ci;
        zr = temp;
        i++;
    }
    return i;
}

double to_real(int x, int width, double minR, double maxR) {
    double range = maxR - minR;
    return x * (range / width) + minR;
}

double to_im(int y, int height, double minI, double maxI) {
    double range = maxI - minI;
    return y * (range / height) + minI;
}

int main() {
    int maxN = 255;
    double minR = -1.5, maxR = 0.8, minI = -1.0, maxI = 1.0;

    rpc::server srv(rpc::constants::DEFAULT_PORT);

    srv.bind("get_time", []() {
        time_t rawtime;
        struct tm *timeinfo;
        time (&rawtime);
        timeinfo = localtime(&rawtime);
        return asctime(timeinfo);
    });

    srv.bind("get_mandelbrot", [&](int width, int height) {
        pixel_data data;
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                double cr = to_real(x, width, minR, maxR);
                double ci = to_im(y, height, minI, maxI);
                int n = mandelbrot(cr, ci, maxN);

                unsigned char r = ((int)(fabs(n * cosf(n))) % 256);
                unsigned char g = ((n * 3) % 256);
                unsigned char b = (n % 256);

                data.push_back({r, g, b});
            }
        }

        return data;
    });

    srv.async_run(2);
    std::cout << "Press [ENTER] to exit the server." << std::endl;
    std::cin.ignore();
    return 0;
}

mandelbrot/client

#include <iostream>

#include "SFML/Window.hpp"
#include "SFML/Graphics.hpp"
#include "rpc/client.h"
#include "mandelbrot.h"

int main() {
    const int width = 1024, height = 768;

    rpc::client c("127.0.0.1", rpc::constants::DEFAULT_PORT);

    std::cout << "Calling get_mandelbrot asynchronically" << std::endl;
    auto result_obj = c.async_call("get_mandelbrot", width, height);

    std::cout << "Calling get_time synchronically" << std::endl;
    auto current_time = c.call("get_time").as<std::string>();
    std::cout << "Current time: " << current_time << std::endl;

    sf::Image image;
    image.create(width, height, sf::Color::Black);

    std::cout << "Waiting for get_mandelbrot result" << std::endl;
    auto result = result_obj.get().as<pixel_data>();
    std::cout << "Got mandelbrot data, displaying..." << std::endl;

    for (size_t y = 0; y < height; ++y) {
        for (size_t x = 0; x < width; ++x) {
            auto item = result[x * height + y];
            auto color = sf::Color(item.r, item.g, item.b, 255);
            image.setPixel(x, y, color);
        }
    }

    sf::RenderWindow window(sf::VideoMode(width, height), "rpc mandelbrot client");

    sf::Texture texture;
    texture.loadFromImage(image, sf::IntRect(0, 0, width, height));
    sf::Sprite sprite;
    sprite.setTexture(texture, true);
    while (window.isOpen()) {
        sf::Event event;
        while (window.pollEvent(event)) {
            if (event.type == sf::Event::Closed) {
                window.close();
            }
        }
        window.draw(sprite);
        window.display();
    }

    return 0;
}

calculator

server

#include <iostream>

#include "rpc/server.h"
#include "rpc/this_handler.h"

double divide(double a, double b) {
    if (b == 0.0) {
        rpc::this_handler().respond_error(
                std::make_tuple(1, "Division by zero"));
    }
    return a / b;
}

struct subtractor {
    double operator()(double a, double b) {
        return a - b;
    }
};

struct multiplier {
    double multiply(double a, double b) {
        return a * b;
    }
};

int main() {
    rpc::server srv(rpc::constants::DEFAULT_PORT);
    subtractor s;
    multiplier m;

    // It's possible to bind non-capturing lambdas
    srv.bind("add", [](double a, double b) { return a + b; });
    // ... arbitrary callables
    srv.bind("sub", s);
    // ... free functions
    srv.bind("div", &divide);
    // ... member functions with captured instances in lambdas
    srv.bind("mul", [&m](double a, double b) { return m.multiply(a, b); });

    srv.run();

    return 0;
}

client

#include <iostream>

#include "rpc/client.h"
#include "rpc/rpc_error.h"

int main() {
    rpc::client c("localhost", rpc::constants::DEFAULT_PORT);

    try {
        std::cout << "add(2, 3) = ";
        double five = c.call("add", 2, 3).as<double>();
        std::cout << five << std::endl;

        std::cout << "sub(3, 2) = ";
        double one = c.call("sub", 3, 2).as<double>();
        std::cout << one << std::endl;

        std::cout << "mul(5, 0) = ";
        double zero = c.call("mul", five, 0).as<double>();
        std::cout << zero << std::endl;

        std::cout << "div(3, 0) = ";
        double hmm = c.call("div", 3, 0).as<double>();
        std::cout << hmm << std::endl;
    } catch (rpc::rpc_error &e) {
        std::cout << std::endl << e.what() << std::endl;
        std::cout << "in function '" << e.get_function_name() << "': ";

        using err_t = std::tuple<int, std::string>;
        auto err = e.get_error().as<err_t>();
        std::cout << "[error " << std::get<0>(err) << "]: " << std::get<1>(err)
                  << std::endl;
        return 1;
    }

    return 0;
}

性能测试TODO

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

踏马潜行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值