C++开发:rest_rpc示例

 【链接】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。可以快速上手,使用者只需要关注自己的业务逻辑即可。

核心特性

  1. 跨平台支持:可在 Linux/Windows 环境下运行

  2. 高性能:基于 asio 实现异步网络通信

  3. 易用接口

    • 简单易用的服务注册/调用机制

    • 支持多种参数类型自动序列化

示例

一个加法的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();
}

关键优势

  1. 低延迟:平均延迟可控制在微秒级别

  2. 高并发:单机可支持数万并发连接

  3. 简洁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 &params);

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...");
	}
}

测试

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值