找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 320|回复: 0

【Python】Python3调用C++代码之封装rest_rpc

[复制链接]
发表于 2024-6-8 10:36:08 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×

背景

  由于HTTP比较容易被抓包,笔者最近在找基于TCP/UDP的RPC第三方库。经过层层筛选笔者最终找到rest_rpc这个项目https://github.com/qicosmos/rest_rpc
rest_rpc是基于tcp的RPC项目,RPC即远程过程调用,大部分RPC库是用通过网络实现“客户端调用服务器的函数执行且获取结果”,目前最火的RPC库是grpc(但是他是基于http2的,不符合笔者要求)。其实是否使用http(s)/ws(s),有利有弊。如果使用http(s)/ws(s)则可以直接用CDN防止DDos,但被抓包也是很容易的,网络开销也比较大。而如果直接用TCP/UDP就得有其他方案缓解DDos,但不易被抓包(或者说不太容易被关注,因为正常的都用http(s)),性能也更好一些。     

编码

  因为rest_rpc本身无Python支持,所以我们得为其开发wrapper,参照https://www.0xaa55.com/thread-26563-1-1.html准备好pybind:

#include<iostream>
#include<string>
#include<unordered_map>
#define MSGPACK_DISABLE_LEGACY_NIL
#define MSGPACK_NO_BOOST
#include<rest_rpc.hpp>
#include<pybind11/pybind11.h>
#include<pybind11/functional.h>
namespace py = pybind11;

using rest_rpc::rpc_service::rpc_conn;
using rest_rpc::rpc_service::rpc_server;
using rest_rpc::rpc_client;

typedef std::function<std::string(const std::string&)> F_SS;
typedef std::function<int(const std::string&)> F_SI;

class Server {
public:
    void set_handler_ss(const std::string& name, const F_SS& f) {
        func_ss_map[name] = f;
    }
    void set_handler_si(const std::string& name, const F_SI& f) {
        func_si_map[name] = f;
    }
    int serve(int port) {
        py::gil_scoped_release release;
        static rpc_server server(port, std::thread::hardware_concurrency());
        for (auto& it : func_ss_map) {
            server.register_handler(it.first, [&it](rpc_conn conn, const std::string& data) -> std::string {
                return it.second(data);
            });
        }
        for (auto& it : func_si_map) {
            server.register_handler(it.first, [&it](rpc_conn conn, const std::string& data) -> int {
                return it.second(data);
            });
        }
        server.run();
        return 0;
    }
public:
    std::unordered_map<std::string,F_SS> func_ss_map;
    std::unordered_map<std::string,F_SI> func_si_map;
};

static std::string test_call_ss(int port, const std::string& name, const std::string& indata) {
    rpc_client client("127.0.0.1", port);
    if (!client.connect()) {
        std::cout << "connect failed" << std::endl; 
        return "";
    }
    std::string result = client.call<std::string>(name, indata);
    client.close();
    return result;
}

static int test_call_si(int port, const std::string& name, const std::string& indata) {
    rpc_client client("127.0.0.1", port);
    if (!client.connect()) {
        std::cout << "connect failed" << std::endl; 
        return -1;
    }
    int ret = client.call<int>(name, indata);
    client.close();
    return ret;
}

PYBIND11_MODULE(rest_rpc, m) {
    py::class_<Server>(m, "Server")
        .def(py::init())
        .def("set_handler_ss", &Server::set_handler_ss)
        .def("set_handler_si", &Server::set_handler_si)
        .def("serve", &Server::serve);
    m.def("test_call_ss", &test_call_ss);
    m.def("test_call_si", &test_call_si);
}

注意: 此代码中默认只定义2个通用函数类型

  • std::string f(const std::string& data)
  • int f(const std::string& data)

  为什么定义这两个函数就够了?实际参数可以通过msgpack序列化成字符串作为参数传递,这样就解决了函数任意参数的问题。而返回值无非是一个字符串,字典(又可序列化成字符串)或者整形。

编译

g++ -Iinclude -std=c++11 -lc++ -shared -undefined dynamic_lookup $(python3 -m pybind11 --includes) -DPYBIND11 restrpc_glue.cpp -o rest_rpc$(python3-config --extension-suffix)

测试

#!python3
import sys
import rest_rpc
def echo(data):
    print("echo ", data)
    return data

if __name__ == "__main__":
    t = sys.argv[1]
    if t == "server":
        serv = rest_rpc.Server()
        serv.set_handler_ss("echo", echo)
        serv.serve(8080)
    elif t == "client":
        data = rest_rpc.test_call_ss(8080, "echo", "somedata")
        print("recv", data)


回复

使用道具 举报

本版积分规则

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-12-21 23:06 , Processed in 0.028580 second(s), 21 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表