【链接】GitHub - qicosmos/rest_rpc: modern C++(C++11), simple, easy to use rpc framework
rest_rpc是一个高性能、易用、跨平台、header only的c++11 rpc库,它的目标是让tcp通信变得非常简单易用,即使不懂网络通信的人也可以直接使用它。由 qicosmos 开发,它是一个高性能 C++ RPC 框架,它依赖header-only的standalone asio。可以快速上手,使用者只需要关注自己的业务逻辑即可。
核心特性
-
跨平台支持:可在 Linux/Windows 环境下运行
-
高性能:基于 asio 实现异步网络通信
-
易用接口:
-
简单易用的服务注册/调用机制
-
支持多种参数类型自动序列化
-
示例
一个加法的rpc服务
// 服务端示例
#include <rest_rpc.hpp>
using namespace rest_rpc;
using namespace rpc_service;
//服务端注册加法rpc服务
struct dummy{
int add(rpc_conn conn, int a, int b) { return a + b; }
};
int main(){
rpc_server server(9000, std::thread::hardware_concurrency());
dummy d;
server.register_handler("add", &dummy::add, &d);
server.run();
}
//客户端调用加法的rpc服务
#include <rest_rpc.hpp>
int main(){
rpc_client client("127.0.0.1", 9000);
client.connect();
int result = client.call<int>("add", 1, 2);
client.run();
}
获取一个对象的rpc服务
//服务端注册获取person的rpc服务
//1.先定义person对象
struct person {
int id;
std::string name;
int age;
MSGPACK_DEFINE(id, name, age);
};
//2.提供并服务
person get_person(rpc_conn conn) {
return { 1, "tom", 20 };
}
int main(){
//...
server.register_handler("get_person", get_person);
}
//客户端调用获取person对象的rpc服务
int main(){
rpc_client client("127.0.0.1", 9000);
client.connect();
person result = client.call<person>("get_person");
std::cout << result.name << std::endl;
client.run();
}
关键优势
-
低延迟:平均延迟可控制在微秒级别
-
高并发:单机可支持数万并发连接
-
简洁API:开发者只需关注业务逻辑实现
适用场景
-
微服务架构中的内部服务调用
-
对性能要求较高的分布式系统
该项目采用 MIT 开源协议,适合需要轻量级、高性能 RPC 框架的 C++ 项目使用。最新版本和完整文档可在 GitHub 仓库获取。
VS2017+Qt进行服务端开发
工程组织
服务端类封装
#pragma once
#ifndef RESTRPCSERVER_H
#define RESTRPCSERVER_H
#include <fstream>
#include "ServiceDef.h"
#include <QObject>
#include <rest_rpc.hpp>
#include <QString>
#include <QVariant>
#include <QThread>
using namespace rest_rpc;
using namespace rpc_service;
class RestRpcServer : public /*QObject*/ QThread
{
Q_OBJECT
public:
explicit RestRpcServer(QObject *parent = nullptr);
~RestRpcServer();
dummy1 get_dummy(rpc_conn conn, dummy1 d);
void msgReceived(const std::string& msg);
void run() override {
try {
while (!m_stop)
{
m_server->run();
}
}
catch (const std::exception& e) {
//emit errorOccurred(QString::fromStdString(e.what())));
}
}
bool Init(quint16 port);
void End();
bool RegisterHandler();
signals:
void clientConnected(const QString &clientInfo);
void clientDisconnected(const QString &clientInfo);
void messageReceived(QString msg);
void rpcCallReceived(const QString &method, const QVariant ¶ms);
public slots:
void sendBroadcast(const QString &message);
private:
std::unique_ptr<rpc_service::rpc_server> m_server;
bool m_running = false;
std::atomic<bool> m_stop{ false };
};
#endif // RPCSERVER_H
#include "RestRpcServer.h"
#include <QDateTime>
#include <functional>
#include "qps.h"
#include <QTextCodec>
//dummy1 get_dummy(rpc_conn conn, dummy1 d)
//{
// return d;
//}
std::string get_person_name(rpc_conn conn, const person &p)
{
return p.name;
}
person get_person(rpc_conn conn)
{
return { 1, "tom", 20 };
}
std::string translate(rpc_conn conn, const std::string &orignal) {
std::string temp = orignal;
for (auto &c : temp) {
c = std::toupper(c);
}
return temp;
}
void hello(rpc_conn conn, const std::string &str) {
std::cout << "hello " << str << std::endl;
}
void upload(rpc_conn conn, const std::string &filename,
const std::string &content) {
std::cout << content.size() << std::endl;
std::ofstream file(filename, std::ios::binary);
file.write(content.data(), content.size());
}
std::string download(rpc_conn conn, const std::string &filename) {
std::ifstream file(filename, std::ios::binary);
if (!file) {
return "";
}
file.seekg(0, std::ios::end);
size_t file_len = file.tellg();
file.seekg(0, std::ios::beg);
std::string content;
content.resize(file_len);
file.read(&content[0], file_len);
std::cout << file_len << std::endl;
return content;
}
qps g_qps;
std::string get_name(rpc_conn conn, const person &p) {
g_qps.increase();
return p.name;
}
// if you want to response later, you can use async model, you can control when
// to response
void delay_echo(rpc_conn conn, const std::string &src) {
auto sp = conn.lock();
sp->set_delay(true);
auto req_id = sp->request_id(); // note: you need keep the request id at that
// time, and pass it into the async thread
std::thread thd([conn, req_id, src] {
std::this_thread::sleep_for(std::chrono::seconds(1));
auto conn_sp = conn.lock();
if (conn_sp) {
conn_sp->pack_and_response(req_id, std::move(src));
}
});
thd.detach();
}
std::string echo(rpc_conn conn, const std::string &src)
{
g_qps.increase();
return src;
}
int get_int(rpc_conn conn, int val)
{
return val;
}
dummy1 RestRpcServer::get_dummy(rpc_conn conn, dummy1 d)
{
return d;
}
RestRpcServer::RestRpcServer(QObject *parent) : QThread/*QObject*/(parent)
{
}
RestRpcServer::~RestRpcServer()
{
End();
}
bool RestRpcServer::Init(quint16 port)
{
if (m_running)
return true;
try {
m_server = std::make_unique<rpc_server>(port, std::thread::hardware_concurrency(), 3600);
m_running = true;
m_stop = false;
return true;
}
catch (...) {
return false;
}
}
bool RestRpcServer::RegisterHandler()
{
if (!m_running)
return false;
try {
dummy d;
m_server->register_handler("add", &dummy::add, &d);
m_server->register_handler("get_dummy", &RestRpcServer::get_dummy, this);//类的成员函数
//m_server->register_handler("get_dummy", get_dummy);//普通函数
m_server->register_handler("translate", translate);
m_server->register_handler("hello", hello);
m_server->register_handler("get_person_name", get_person_name);
m_server->register_handler("get_person", get_person);
m_server->register_handler("upload", upload);
m_server->register_handler("download", download);
m_server->register_handler("get_name", get_name);
m_server->register_handler("delay_echo", delay_echo);
m_server->register_handler("echo", echo);
m_server->register_handler("get_int", get_int);
m_server->register_handler("publish_by_token", [this](rpc_conn conn,
std::string key,
std::string token,
std::string val) {
m_server->publish_by_token(std::move(key), std::move(token), std::move(val));
});
m_server->register_handler("publish",
[this](rpc_conn conn, std::string key,
std::string token, std::string val) {
m_server->publish(std::move(key), std::move(val));
});
m_server->set_network_err_callback(
[this](std::shared_ptr<connection> conn, std::string reason) {
std::string msg = "remote client (address:" + conn->remote_address()
+ " id: " + std::to_string(conn->conn_id())
+ "): networking error, reason: " + reason;
msgReceived(msg);
});
//自定义连接成功回调函数
m_server->set_conn_callback(
[this](std::string ipAddress, int nConnID) {
std::string msg = "remote client (address: " + ipAddress
+ " id: " + std::to_string(nConnID) + "): connect successfully.";
msgReceived(msg);
});
return true;
}
catch (...) {
return false;
}
}
void RestRpcServer::msgReceived(const std::string& msg)
{
QString qMsg = QString::fromUtf8(msg.c_str());
if (qMsg.contains(QChar(0xFFFD))) { // 如果出现乱码占位符"�"
// 尝试 GBK(仅限 Qt 5)
QTextCodec *codec = QTextCodec::codecForName("GBK");
qMsg = codec->toUnicode(msg.c_str());
}
emit messageReceived(qMsg);
}
void RestRpcServer::End()
{
m_stop = true;
if (m_server) {
m_server.reset();
//std::move(m_server);
}
m_running = false;
quit(); // 发出退出信号
wait(); // 等待线程结束
}
void RestRpcServer::sendBroadcast(const QString &message)
{
if (!m_running) return;
// 这里简化实现,实际应用中需要维护连接列表
m_server->publish("broadcast", message.toStdString());
}
服务端修改
rpc_server.h中添加自定义回调相关代码
void do_accept() {
conn_.reset(new connection(io_service_pool_.get_io_service(),timeout_seconds_, router_));
conn_->set_callback([this](std::string key, std::string token,
std::weak_ptr<connection> conn) {
std::unique_lock<std::mutex> lock(sub_mtx_);
sub_map_.emplace(std::move(key) + token, conn);
if (!token.empty()) {
token_list_.emplace(std::move(token));
}
});
acceptor_.async_accept(conn_->socket(), [this](asio::error_code ec) {
if (!acceptor_.is_open()) {
return;
}
if (ec) {
// LOG(INFO) << "acceptor error: " <<
// ec.message();
} else {
#ifdef CINATRA_ENABLE_SSL
if (!ssl_conf_.cert_file.empty()) {
conn_->init_ssl_context(ssl_conf_);
}
#endif
if (on_net_err_callback_) {
conn_->on_network_error(on_net_err_callback_);
}
conn_->start();
//自定义回调
if (conn_callback_) {
conn_callback_(conn_->remote_address(), conn_->conn_id());
}
std::unique_lock<std::mutex> lock(mtx_);
conn_->set_conn_id(conn_id_);
connections_.emplace(conn_id_++, conn_);
}
do_accept();
});
}
void set_conn_callback(std::function<void( std::string /*ip address*/, int)> callback) {
conn_callback_ = std::move(callback);
}
std::function<void(int64_t)> conn_timeout_callback_;
std::function<void(std::string /*ip address*/, int)> conn_callback_ = nullptr;
std::function<void(std::shared_ptr<connection>, std::string)>
界面设计
#pragma once
#include <QtWidgets/QMainWindow>
#include "ui_ServerMainwindow.h"
#include <rest_rpc.hpp>
using namespace rest_rpc;
using namespace rpc_service;
#include <fstream>
#include "RestRpcServer.h"
class ServerMainwindow : public QMainWindow
{
Q_OBJECT
public:
ServerMainwindow(QWidget *parent = nullptr);
~ServerMainwindow();
public slots:
void logPrint(QString strLog);
void on_pbServerStart_clicked();
void on_pbServerStop_clicked();
signals:
bool startServer(quint16 port);
private:
Ui::ServerMainwindowClass ui;
//ZeroMQBroker *m_broker;
//ZeroMQConsumer *m_consumer;
RestRpcServer* m_pRpcServer;
};
#include "ServerMainwindow.h"
#include <QDateTime>
#include <thread>
ServerMainwindow::ServerMainwindow(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
ui.splitter->setStretchFactor(0, 2);
ui.splitter->setStretchFactor(1, 1);
ui.splitterSubR->setStretchFactor(0, 2);
ui.splitterSubR->setStretchFactor(1, 5);
m_pRpcServer = new RestRpcServer();
connect(m_pRpcServer, &RestRpcServer::messageReceived, this, &ServerMainwindow::logPrint);
}
ServerMainwindow::~ServerMainwindow()
{
if (m_pRpcServer) {
delete m_pRpcServer;
m_pRpcServer = nullptr;
}
}
void ServerMainwindow::logPrint(QString strLog)
{
QString currentTimeText = QDateTime::currentDateTime().toString("yyyy-MM-dd:hh-mm-ss-zzz ");
ui.plainTextEdit->appendPlainText(currentTimeText+strLog);
}
void ServerMainwindow::on_pbServerStart_clicked()
{
int nPort = ui.spPort->value();
if (m_pRpcServer)
{
m_pRpcServer->Init(nPort);
m_pRpcServer->RegisterHandler();
m_pRpcServer->start();
logPrint("Rpc Server Start...");
}
}
void ServerMainwindow::on_pbServerStop_clicked()
{
if (m_pRpcServer)
{
m_pRpcServer->End();
logPrint("Rpc Server Stop...");
}
}