项目介绍
这是一个用C++实现的轻量级、分布式JSON-RPC框架项目。RPC(远程过程调用)的目标是让你能够像调用本地函数一样调用远程机器上的服务。这个项目不仅实现了基础的RPC调用,还在此基础上扩展了服务注册与发现、发布订阅等高级特性,形成了一个可用的分布式通信框架原型。
RPC(Remote Procedure Call)远程过程调⽤,是⼀种通过⽹络从远程计算机上请求服务,⽽不需要 了解底层⽹络通信细节。RPC可以使⽤多种⽹络协议进⾏通信, 如HTTP、TCP、UDP等, 并且在 TCP/IP⽹络四层模型中跨越了传输层和应⽤层。简⾔之RPC就是像调⽤本地⽅法⼀样调⽤远程⽅法。过程可以理解为业务处理、计算任务,更直⽩的说,就是程序/⽅法/函数等,就是像调⽤本地⽅法⼀样调⽤远程⽅法。

一个完整的RPC通信大概包含以下内容:
(1) 序列化协议
(2)通信协议
(3)连接复⽤
(4)服务注册
(5)服务发现
(6)服务订阅和通知
(7)负载均衡
(8)服务监控
(9)同步调⽤
(10)异步调⽤
技术选型
实现方案
核心架构方案
选择实现一个通用的远程调用接口 call,通过传入函数名和参数来调用RPC接口。
参数与返回值的映射
使用通用的JSON类型来定义参数和返回值协议。
网络通信
选用 Muduo网络库。
序列化与反序列化
采用 JSON。因为项目需要使用JSON来定义函数参数和返回值,所以顺势采用JSON进行序列化和反序列化。
开发环境
操作系统:Linux(Ubuntu版本)
编译器:g++/gcc
sudo apt-get install gcc g++
调试器:gdb
sudo apt-get install gdb
构建工具:Makefile
sudo apt-get install make
编译器:VSCode
环境依赖
Jsoncpp库:用于JSON数据的序列化与反序列化。
sudo apt-get install libjsoncpp-dev
Muduo库:用于高并发TCP网络通信。
git clone https://github.com/chenshuo/muduo.git
安装muduo库依赖环境
sudo apt-get install libz-dev libboost-all-dev
运行脚本编译安装
unzip muduo-master.zip
./build.sh
./build.sh install
cmake:用于构建Muduo等库。
sudo apt-get install cmake
wget:用于下载资源。
sudo apt-get install wget
lrzsz:文件传输工具(用于在Windows与Linux间互传文件)。
sudo apt-get install lrzsz
第三方库使用介绍
JsonCpp库
用途
JsonCpp库是项目中用于实现JSON格式数据序列化和反序列化的核心第三方库。它的功能是将多个数据对象组织成JSON格式的字符串,以及将接收到的JSON格式字符串解析还原为数据对象。
Json数据格式
{
"姓名" : "xx",
"年龄" : 18,
"成绩" : [88.5, 99, 58],
"爱好" :{
"书籍" : "西游记",
"运动" : "打篮球"
}
}
数据类型:
对象:用花括号 {}括起来的键值对集合。
数组:用中括号 []括起来的值列表。
字符串:用双引号 ""括起来的文本。
数字:包括整型和浮点型,直接表示。
Json::Value类
class Json::Value {
Value &operator=(const Value &other); // Value重载了[]和=,因此所有的赋值和获取数据都可以通过
Value& operator[](const std::string& key); // 简单的方式完成 val["name"] = "xx";
Value& operator[](const char* key);//通过 C 风格字符串键访问或设置值
Value removeMember(const char* key); // 移除元素
const Value& operator[](ArrayIndex index) const; // val["score"][0]
Value& append(const Value& value); // 添加数组元素 val["score"].append(88);
ArrayIndex size() const; // 获取数组元素个数 val["score"].size();
std::string asString() const; // 转string string name = val["name"].asString();
const char* asCString() const; // 转char* char *name = val["name"].asCString();
Int asInt() const; // 转int int age = val["age"].asInt();
float asFloat() const; // 转float float weight = val["weight"].asFloat();
bool asBool() const; // 转 bool bool ok = val["ok"].asBool();
};
JsonCpp的序列化与反序列化
Jsoncpp 库主要借助三个类以及其对应的少量成员函数完成序列化及反序列化
序列化接口
class JSON_API StreamWriter {
virtual int write(Value const& root, std::ostream* sout) = 0;
};
class JSON_API StreamWriterBuilder : public StreamWriter::Factory {
virtual StreamWriter* newStreamWriter() const;
};//工厂类
StreamWriter类:
virtual:表明这是一个虚函数,可以被派生类重写。
= 0:表示这是一个纯虚函数,意味着这个类不能被实例化,必须由子类实现该函数。
Value const& root:表示一个 JSON 值对象(来自之前的 Json::Value类),它是要写入的数据根节点。
std::ostream* sout:指向一个输出流的指针(比如文件流、控制台流等),用于写入数据。
作用:
(1)定义了一个流式写入器接口,用于将 JSON 数据写入到输出流中。
(2)所有具体的 JSON 写入器(如写入文件、写入内存、压缩写入等)都必须继承这个类并实现 write()方法。、
StreamWriterBuilder类
返回值:StreamWriter*,即返回一个 StreamWriter类型的指针(通常是某个具体子类的实例)。
作用
(1)这是一个工厂方法模式的实现。
(2)它不直接写数据,而是负责创建 StreamWriter的具体实例。
(3)客户端代码可以通过 StreamWriterBuilder获取一个 StreamWriter实例,然后调用其 write()方法来写入 JSON。
架构思想
接口分离:StreamWriter只定义"如何写",不关心"怎么创建"。StreamWriterBuilder只定义"如何创建",不关心"怎么写"。
依赖倒置原则:高层模块(调用者)依赖于抽象(StreamWriter接口),而不是具体实现。可以通过不同的 StreamWriterBuilder实现来创建不同的写入器(比如写文件、写网络、写内存等)。
可扩展性:你可以轻松添加新的 StreamWriter子类(FileStreamWriter,MemoryStreamWriter),并配合对应的 StreamWriterBuilder来创建它们。客户端代码无需修改,只需更换 Builder 即可切换写入方式。
反序列化接口
class JSON_API CharReader {
virtual bool parse(char const* beginDoc, char const* endDoc,
Value* root, std::string* errs) = 0;
};
class JSON_API CharReaderBuilder : public CharReader::Factory {
virtual CharReader* newCharReader() const;
};
JsonCpp的使用
#include <iostream>
#include <sstream>
#include <string>
#include <memory>
#include <jsoncpp/json/json.h>
int main()
{
// 序列化
Json::Value stu;
stu["name"] = "zhangsan";
stu["age"] = 19;
stu["socre"].append(77.5);
stu["socre"].append(88);
stu["socre"].append(99.5);
Json::StreamWriterBuilder swb;
std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
std::stringstream ss;
int ret = sw->write(stu, &ss);
if (ret != 0) {
std::cout << "Serialize failed!\n";
return -1;
}
std::cout << "序列化结果:\n" << ss.str() << std::endl;
// 反序列化
std::string str = ss.str();
Json::Value root;
Json::CharReaderBuilder crb;
std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
bool ret1 = cr->parse(str.c_str(), str.c_str() + str.size(), &root, nullptr);
if (!ret1) {
std::cout << "UnSerialize failed!" << std::endl;
return -1;
}
std::cout << "反序列化结果:\n"
<< "name:" << root["name"].asString() << "\n"
<< "age:" << root["age"].asInt() << "\n"
<< "socre:" << root["socre"][0].asFloat() << " " << root["socre"][1].asInt()
<< " " << root["socre"][2].asFloat() << "\n";
return 0;
}
封装Json工具类
class json_util
{
public:
// 序列化:Json对象 -> 字符串
// 输入输出型参数
// root输入参数:表示要序列化的json对象
// str输出参数:表示序列化之后的字符串
static bool serialize(const Json::Value &root, std::string &str)
{
Json::StreamWriterBuilder swb;
std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
std::stringstream ss;
int ret = sw->write(root, &ss);
if (ret != 0) {
std::cout << "Serialize failed!" << std::endl;
return false;
}
str = ss.str();
return true;
}
// 反序列化:字符串 ->Json对象
// 输入输出型参数
// str输入参数:表示需要反序列化的字符串
// root输出参数:表示反序列化后的json对象
static bool unserialize(const std::string &str, Json::Value &root)
{
Json::CharReaderBuilder crb;
std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
bool ret = cr->parse(str.c_str(), str.c_str() + str.size(), &root, nullptr);
if (!ret) {
std::cout << "UnSerialize failed!" << std::endl;
return false;
}
return true;
}
};
Muduo库
简介
Muduo是由陈硕大佬开发的一个基于非阻塞IO和事件驱动的C++高并发TCP网络编程库。它采用主从Reactor模型,其线程模型是 "one loop per thread"。这个模型意味着:一个线程只能有一个事件循环(EventLoop),用于响应计时器和IO事件。一个文件描述符(如一个TCP连接)只能由一个线程进行读写。

常见接口
TcpServer类
用于构建TCP服务器。其构造函数接收 EventLoop*、InetAddress(地址端口)、服务器名和选项(如kReusePort)等参数。
typedef std::shared_ptr<TcpConnection> TcpConnectionPtr;
//用于管理 TcpConnection(TCP 连接)对象的生命周期
typedef std::function<void (const TcpConnectionPtr&)> ConnectionCallback;
//当TCP 连接建立/断开时,触发此回调(例如打印连接信息、记录日志等)
typedef std::function<void (const TcpConnectionPtr&, Buffer*, Timestamp)> MessageCallback;
//当TCP 连接收到消息时,触发此回调(例如处理业务逻辑、解析协议等)。
class InetAddress : public muduo::copyable
{
public:
InetAddress(StringArg ip, uint16_t port, bool ipv6 = false);
};
class TcpServer : noncopyable
{
public:
enum Option
{
kNoReusePort,
kReusePort,
};
TcpServer(EventLoop* loop,
const InetAddress& listenAddr,
const string& nameArg,
Option option = kNoReusePort);
void setThreadNum(int numThreads);
//设置服务器的工作线程数(默认是单线程,通过此函数可开启多线程模式,提升并发处理能力)。
void start();
/// 当一个新连接建立成功的时候被调用
void setConnectionCallback(const ConnectionCallback& cb)
{ connectionCallback_ = cb; }
/// 消息的业务处理回调函数---这是收到新连接消息的时候被调用的函数
//例如:连接建立时打印"新连接 from xxx",断开时打印"连接关闭"。
void setMessageCallback(const MessageCallback& cb)
{ messageCallback_ = cb; }
//设置"消息回调":当 TCP 连接收到数据时,调用传入的 cb,并传递连接、缓冲区、时间戳。
//例如:从 Buffer中读取数据,解析协议,执行业务逻辑(如 Echo 服务、HTTP 请求处理等)。
};
InetAddress类
继承自 muduo::copyable,表示该类支持拷贝(或设计上允许拷贝)。
构造函数:
功能:初始化网络地址(IP + 端口),ipv6默认为 false表示 IPv4,true表示 IPv6。
参数:ip(IP 地址字符串)、port(端口号,16 位无符号)、ipv6(是否启用 IPv6)。
TcpServer类
枚举option:kNoReusePort:不使用端口复用(默认选项)。
kReusePort:使用端口复用(允许多个进程/线程绑定同一个端口,提升并发能力)。
构造函数:初始化TCP服务器
参数 :
loop:事件循环(Muduo 核心,负责 IO 事件的监听和分发)。
listenAddr:服务器监听的地址(由 InetAddress构造,包含 IP + 端口)。
nameArg:服务器的名字(用于日志、调试等)。
option:端口复用选项(默认 kNoReusePort)。
EventLoop类
事件循环,是网络库的核心驱动。主要接口包括:
loop():启动事件循环。
quit():退出事件循环。
runAfter()/ runEvery():用于运行定时任务。
class EventLoop : noncopyable//表示该类对象不可拷贝
{
public:
// 持续循环。
// 必须在创建该对象的同一线程中调用。
void loop();
//退出循环
void quit();
//在指定时间点运行回调函数
TimerId runAt(Timestamp time, TimerCallback cb);
// 在延迟 @c delay 秒后运行回调函数。
TimerId runAfter(double delay, TimerCallback cb);
// 每隔 @c interval 秒运行回调函数。
TimerId runEvery(double interval, TimerCallback cb);
// 取消定时器。
void cancel(TimerId timerId);
private:
std::atomic<bool> quit_;// 表示是否退出循环
std::unique_ptr<Poller> poller_;// Poller 智能指针,管理 I/O 多路复用
mutable MutexLock mutex_;// 可变的互斥锁,保护待执行函数队列
std::vector<Functor> pendingFunctors_ GUARDED_BY(mutex_);
// 待执行的函数队列,由 mutex_ 保护
};
核心机制
(1) 事件循环
loop(): 核心循环函数,不断执行:
通过 poller_->poll()获取活跃的 I/O 事件
处理 I/O 事件回调
处理定时器回调
执行 pendingFunctors_中的任务
(2) 定时器管理
三级定时器接口:
runAt(): 绝对时间执行
runAfter(): 相对延迟执行
runEvery(): 周期性执行
通过 TimerId标识定时器,支持取消操作
(3) 跨线程调用
通过 pendingFunctors_队列实现跨线程任务提交
其他线程可以安全地调用 runAfter()、runEvery()、cancel()等接口
通过 std::atomic<bool>实现无锁退出标志
(4) 线程安全注意事项
loop()必须在创建线程调用(线程绑定)
quit()可通过 shared_ptr<EventLoop>实现完全线程安全
定时器相关操作已做线程安全处理
TcpConnection类
表示一个TCP连接。主要接口包括:
send():发送数据。
shutdown():关闭连接。
setMessageCallback():设置收到消息时的回调函数。
connected():判断连接是否建立。
class TcpConnection : noncopyable,
public std::enable_shared_from_this<TcpConnection>
{
public:
//创建一个已连接的 TCP 连接对象。
TcpConnection(EventLoop* loop,
const string& name,
int sockfd,
const InetAddress& localAddr,
const InetAddress& peerAddr);
//判断当前连接是否已连接状态。
bool connected() const { return state_ == kConnected; }
//判断当前连接是否已断开状态。
bool disconnected() const { return state_ == kDisconnected; }
//发送数据接口
void send(string&& message); // 右值引用,移动语义
void send(const void* message, int len);//原始指针 + 长度。
void send(const StringPiece& message);//Muduo 自定义的字符串视图类。
void send(Buffer* message);//指向 Buffer 对象的指针,会交换数据(swap data),提高效率。
void shutdown(); //关闭连接
void setContext(const boost::any& context)//设置上下文。
{ context_ = context; }
const boost::any& getContext() const//获取只读上下文。
{ return context_; }
boost::any* getMutableContext()/获取可修改的上下文指针。
{ return &context_; }
void setConnectionCallback(const ConnectionCallback& cb)//连接建立或断开时的回调函数。
{ connectionCallback_ = cb; }
void setMessageCallback(const MessageCallback& cb)//收到数据时的回调函数。
{ messageCallback_ = cb; }
private:
enum StateE { kDisconnected, kConnecting, kConnected, kDisconnecting };
EventLoop* loop_;
ConnectionCallback connectionCallback_;
MessageCallback messageCallback_;
WriteCompleteCallback writeCompleteCallback_;
boost::any context_;
};
枚举值
StateE:连接状态枚举:
kDisconnected:已断开。
kConnecting:正在连接。
kConnected:已连接。
kDisconnecting:正在断开。
TcpClient类
于构建TCP客户端。主要接口包括:
connect():连接服务器。
disconnect():断开连接。
setMessageCallback():设置收到消息时的回调函数。
class TcpClient : noncopyabl
{
public:
TcpClient(EventLoop* loop,
const InetAddress& serverAddr,
const string& nameArg);
~TcpClient(); // force out-line dtor, for std::unique_ptr members.
void connect();//连接服务器
void disconnect();//关闭连接
void stop();//// 停止客户端(用于取消正在进行的连接)
//获取客户端对应的通信连接Connection对象的接口,发起connect后,有可能还没有连接建立成功
TcpConnectionPtr connection() const
{
MutexLockGuard lock(mutex_);
return connection_;
}
/// 连接服务器成功时的回调函数
void setConnectionCallback(ConnectionCallback cb)
{ connectionCallback_ = std::move(cb); }
/// 收到服务器发送的消息时的回调函数
void setMessageCallback(MessageCallback cb)
{ messageCallback_ = std::move(cb); }
private:
EventLoop* loop_;
ConnectionCallback connectionCallback_;
MessageCallback messageCallback_;
WriteCompleteCallback writeCompleteCallback_;
TcpConnectionPtr connection_ GUARDED_BY(mutex_);// 当前连接对象,受互斥锁保护
};
/*
需要注意的是,因为muduo库不管是服务端还是客户端都是异步操作,
对于客户端来说如果我们在连接还没有完全建立成功的时候发送数据,这是不被允许的。
因此我们可以使用内置的CountDownLatch类进行同步控制
*/
class CountDownLatch : noncopyable//同步工具
{
public:
explicit CountDownLatch(int count);
void wait(){//阻塞等待,直到计数减为 0。
MutexLockGuard lock(mutex_);
while (count_ > 0)
{
condition_.wait();
}
}
void countDown(){
//将计数减 1,如果计数变为 0,则唤醒所有等待的线程。
MutexLockGuard lock(mutex_);
--count_;
if (count_ == 0)
{
condition_.notifyAll();//唤醒等待线程
}
}
int getCount() const;//获取当前剩余计数。
private:
mutable MutexLock mutex_;
Condition condition_ GUARDED_BY(mutex_);// 条件变量,用于线程间通知
int count_ GUARDED_BY(mutex_);
};
Buffer类
应用层缓冲区,用于处理输入输出数据。提供了readableBytes()、retrieveAsString()等接口。
class Buffer : public muduo::copyable
{
public:
static const size_t kCheapPrepend = 8;
//在缓冲区开头预留 8 字节,用于后续 prepend操作(比如写入消息长度)。
static const size_t kInitialSize = 1024;
//默认初始容量 1024 字节。
explicit Buffer(size_t initialSize = kInitialSize)
//创建一个容量为 kCheapPrepend + initialSize的 vector<char>。
: buffer_(kCheapPrepend + initialSize),
readerIndex_(kCheapPrepend),
writerIndex_(kCheapPrepend)
{ }
void swap(Buffer& rhs);
size_t readableBytes() const;
size_t writableBytes() const;
const char* peek() const;// 返回当前可读数据的起始地址(不移动指针)
const char* findEOL() const;// 查找换行符 '\n'(从 readerIndex_ 开始)
const char* findEOL(const char* start) const;// 从指定位置查找换行符
void retrieve(size_t len);// 从 readerIndex_ 开始移除 len 字节
void retrieveInt64();// 读取并移除一个 int64_t
void retrieveInt32();
void retrieveInt16();
void retrieveInt8();
string retrieveAllAsString();// 读取全部可读数据并返回 string(清空可读区)
string retrieveAsString(size_t len);// 读取 len 字节并返回 string(移动指针)
void append(const StringPiece& str);
void append(const char* /*restrict*/ data, size_t len);
void append(const void* /*restrict*/ data, size_t len);
char* beginWrite();
const char* beginWrite() const;
void hasWritten(size_t len);
void appendInt64(int64_t x);
void appendInt32(int32_t x);
void appendInt16(int16_t x);
void appendInt8(int8_t x);
int64_t readInt64();
int32_t readInt32();
int16_t readInt16();
int8_t readInt8();
int64_t peekInt64() const;
int32_t peekInt32() const;
int16_t peekInt16() const;
int8_t peekInt8() const;
void prependInt64(int64_t x);
void prependInt32(int32_t x);
void prependInt16(int16_t x);
void prependInt8(int8_t x);
void prepend(const void* /*restrict*/ data, size_t len);
private:
std::vector<char> buffer_;
size_t readerIndex_;
size_t writerIndex_;
static const char kCRLF[];
};
CountDownLatch类
同步辅助类,用于在异步操作中实现线程间等待。客户端可利用它在连接建立成功后再发送数据。
快速上手
基础echo服务器示例
#include "muduo/net/EventLoop.h"
#include "muduo/net/TcpServer.h"
#include <iostream>
using namespace muduo;
using namespace muduo::net;
class EchoServer {
public:
EchoServer(EventLoop* loop, const InetAddress& listenAddr)
: server_(loop, listenAddr, "EchoServer") {
// 设置连接回调
server_.setConnectionCallback(
std::bind(&EchoServer::onConnection, this, std::placeholders::_1));
// 设置消息回调
server_.setMessageCallback(
std::bind(&EchoServer::onMessage, this,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
// 设置线程数量(可选)
server_.setThreadNum(4);
}
void start() {
server_.start();
std::cout << "Echo server started on " << server_.ipPort() << std::endl;
}
private:
// 连接建立/断开时的回调
void onConnection(const TcpConnectionPtr& conn) {
if (conn->connected()) {
std::cout << "New connection: " << conn->peerAddress().toIpPort()
<< " -> " << conn->localAddress().toIpPort() << std::endl;
} else {
std::cout << "Connection closed: " << conn->peerAddress().toIpPort() << std::endl;
}
}
// 收到消息时的回调
void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp time) {
std::string msg = buf->retrieveAllAsString();
std::cout << "Received " << msg.size() << " bytes at " << time.toString()
<< " from " << conn->peerAddress().toIpPort() << std::endl;
// 回显消息
conn->send(msg);
}
TcpServer server_;
};
int main() {
EventLoop loop;
InetAddress listenAddr(8888);
EchoServer server(&loop, listenAddr);
server.start();
loop.loop(); // 启动事件循环
return 0;
}
echo客户端示例
#include "muduo/net/EventLoop.h"
#include "muduo/net/TcpClient.h"
#include "muduo/net/InetAddress.h"
#include <iostream>
#include <string>
using namespace muduo;
using namespace muduo::net;
class EchoClient {
public:
EchoClient(EventLoop* loop, const InetAddress& serverAddr)
: client_(loop, serverAddr, "EchoClient"),
loop_(loop) {
// 设置连接回调
client_.setConnectionCallback(
std::bind(&EchoClient::onConnection, this, std::placeholders::_1));
// 设置消息回调
client_.setMessageCallback(
std::bind(&EchoClient::onMessage, this,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
}
void connect() {
client_.connect();
}
void send(const std::string& msg) {
if (conn_) {
conn_->send(msg);
}
}
private:
// 连接建立/断开时的回调
void onConnection(const TcpConnectionPtr& conn) {
if (conn->connected()) {
std::cout << "Connected to server!" << std::endl;
conn_ = conn;
} else {
std::cout << "Disconnected from server." << std::endl;
conn_.reset();
loop_->quit(); // 断开连接后退出事件循环
}
}
// 收到消息时的回调
void onMessage(const TcpConnectionPtr conn, Buffer* buf, Times&tamp time) {
std::string msg = buf->retrieveAllAsString();
std::cout << "Received: " << msg << std::endl;
}
TcpClient client_;
EventLoop* loop_;
TcpConnectionPtr conn_;
};
int main() {
EventLoop loop;
InetAddress serverAddr("127.0.0.1", 8888);
EchoClient client(&loop, serverAddr);
client.connect();
// 发送一些测试消息
std::string line;
while (std::getline(std::cin, line)) {
if (line == "quit") break;
client.send(line);
}
loop. loop(); // 启动事件循环
return 0;
}
makefile
CXX = g++
CXXFLAGS = -std=c++11 -O2 -g
LDFLAGS = -lmuduo_net -lmuduo_base -lpthread
all: server client
server: echo_server.o
$(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS)
client: echo_client.o
$(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS)
%.o: %.cpp
$(C) $(CXXFLAGS) -I../thirdXX/include -c -o $@ $<
clean:
rm -f *.o server client
C++11异步操作
future
介绍
std::future是C++11标准库中的⼀个模板类,它表⽰⼀个异步操作的结果。当我们在多线程编程中使⽤ 异步任务时,std::future可以帮助我们在需要的时候获取任务的执⾏结果。std::future的⼀个重要特性 是能够阻塞当前线程,直到异步操作完成,从⽽确保我们在获取结果时不会遇到未完成的操作。
基本概念
一个占位符,表示未来的值
用于异步获取其他线程的计算结果
支持阻塞等待或轮询查询结果是否就绪
|---------|----------------------|
| 特性 | 说明 |
| 异步结果获取 | 在需要时才获取结果,避免阻塞主线程 |
| 自动同步 | 内部有同步机制,保证线程安全 |
| 异常传播 | 异步操作中的异常会传递到 future |
| 一次性使用 | 一个 future 只能获取一次结果 |
| 可移动不可拷贝 | 支持移动语义,禁止拷贝(保证唯一所有权) |
创建方式
通过anync创建
std::async是⼀种将任务与std::future关联的简单⽅法。它创建并运⾏⼀个异步任务,并返回⼀个与该任务结果关联的std::future对象。默认情况下,std::async是否启动⼀个新线程,或者在等待future时,任务是否同步运⾏都取决于你给的 参数。这个参数为std::launch类型:
(1)std::launch::deferred 表明该函数会被延迟调⽤,直到在future上调⽤get()或者wait()才会开始
执⾏任务
(2)std::launch::async 表明函数会在⾃⼰创建的线程上运⾏
(3)std::launch::deferred | std::launch::async 内部通过系统等条件⾃动选择策略
示例:
#include <iostream>
#include <future>
#include <thread>
#include <chrono>
int compute(int x, int y) {
std::this_thread::sleep_for(std::chrono::seconds(1));
return x + y;
}
int main() {
// 启动异步任务
std::future<int> fut = std::async(std::launch::async, compute, 10, 20);
// 在主线程做其他事情...
std::cout << "正在等待计算结果..." << std::endl;
// 获取结果(会阻塞直到计算完成)
int result = fut.get();
std::cout << "计算结果: " << result << std::endl; // 输出: 30
return 0;
}
通过packaged_task和future配合
std::packaged_task就是将任务和 std::feature 绑定在⼀起的模板,是⼀种对任务的封装。我们可以通 过std::packaged_task对象获取任务相关联的std::feature对象,通过调⽤get_future()⽅法获得。 std::packaged_task的模板参数是函数签名。 可以把std::future和std::async看成是分开的, ⽽ std::packaged_task则是⼀个整体。
示例
#include <iostream>
#include <future>
#include <thread>
int add(int x, int y) {
return x + y;
}
int main() {
// 将函数封装为 packaged_task
std::packaged_task<int(int, int)> task(add);
// 获取与任务关联的 future
std::future<int> result = task.get_future();
// 在新线程中执行任务
std::thread t(std::move(task), 5, 3);
// 获取结果
std::cout << "5 + 3 = " << result.get() << std::endl; // 输出: 8
t.join();
return 0;
}
使用promise手动控制
std::promise提供了⼀种设置值的⽅式,它可以在设置之后通过相关联的std::future对象进⾏读取。换种说法就是之前说过std::future可以读取⼀个异步函数的返回值了, 但是要等待就绪, ⽽std::promise就提供⼀种⽅式⼿动让 std::future就绪。
#include <iostream>
#include <future>
#include <thread>
#include <chrono>
void worker(std::promise<int> result_promise) {
std::this_thread::sleep_for(std::chrono::seconds(1));
result_promise.set_value(42); // 设置结果
}
int main() {
std::promise<int> result_promise;
std::future<int> result_future = result_promise.get_future();
std::thread t(worker, std::move(result_promise));
// 等待结果
std::cout << "等待结果..." << std::endl;
int result = result_future.get();
std::cout << "结果: " << result << std::endl; // 输出: 42
t.join();
return 0;
}
应用场景
(1)异步任务: 当我们需要在后台执⾏⼀些耗时操作时,如⽹络请求或计算密集型任务等,std::future可以⽤来表⽰这些异步任务的结果。通过将任务与主线程分离,我们可以实现任务的并⾏处理,从⽽提⾼程序的执⾏效率
(2)并发控制: 在多线程编程中,我们可能需要等待某些任务完成后才能继续执⾏其他操作。通过使⽤std::future,我们可以实现线程之间的同步,确保任务完成后再获取结果并继续执⾏后续操作
(3)结果获取:std::future提供了⼀种安全的⽅式来获取异步任务的结果。我们可以使⽤
std::future::get()函数来获取任务的结果,此函数会阻塞当前线程,直到异步操作完成。这样,在
调⽤get()函数时,我们可以确保已经获取到了所需的结果
项目设计
理解项目功能
该项目的功能是客⼾端想 要完成某个任务的处理,但是这个处理的过程并不⾃⼰来完成,⽽是,将请求发送到服务器上,让服 务器来帮其完成处理过程,并返回结果,客⼾端拿到结果后返回。

然⽽上图的模型中,是⼀种多对⼀或⼀对⼀的关系,⼀旦服务端掉线,则客⼾端⽆法进⾏远端调⽤, 且其服务端的负载也会较⾼,因此在rpc实现中,我们不仅要实现其基本功能,还要再进⼀步,实现分 布式架构的rpc。
分布式架构:简单理解就是由多个节点组成的⼀个系统,这些节点通常指的是服务器,将不同的业务或者同⼀个业务拆分分布在不同的节点上,通过协同⼯作解决⾼并发的问题,提⾼系统扩展和可⽤性。
|------|------------------|--------------------|
| 维度 | 单机架构(Monolithic) | 分布式架构(Distributed) |
| 部署方式 | 所有模块部署在同一台服务器 | 模块拆分,部署在不同服务器 |
| 通信方式 | 进程内函数调用(极快) | 网络通信(RPC/HTTP,有延迟) |
| 数据状态 | 共享内存,强一致性 | 数据分片,存在一致性问题 |
| 容错能力 | 单点故障,系统整体宕机 | 部分节点故障,服务降级仍可用 |
其实现思想也并不复杂,也就是在原来的模型基础上,增加⼀个注册中⼼,基于注册中⼼不同的服务 提供服务器向注册中⼼进⾏服务注册,相当于告诉注册中⼼⾃⼰能够提供什么服务,⽽客⼾端在进⾏ 远端调⽤前,先通过注册中⼼进⾏服务发现,找到能够提供服务的服务器,然后发起调⽤。
(1)服务注册
服务端启动时,向注册中心登记自己提供的服务
示例:服务端1注册 Add()和 Translate()方法
(2)(3)服务发现
客户端需要调用服务时,先查询注册中心
注册中心返回可用的服务地址列表
(4)(5)远程调用
客户端选择其中一个服务地址,发起RPC调用
服务端执行对应方法,返回结果
客户端得到结果,就像调用本地函数一样
⽽其次的发布订阅功能,则是依托于多个客⼾端围绕服务端进⾏消息的转发。
不过单纯的消息转发功能,并不能满⾜于⼤部分场景的需要,因此会在其基础上实现基于主题订阅的转发。


基于以上功能的合并,我们可以得到一个结构图:
在上图的结构中,我们甚⾄可以让每⼀个Server作为备⽤注册中⼼形成分布式架构,⼀旦⼀个注册中 ⼼下线,可以向备⽤中⼼进⾏注册以及请求,且在此基础上客⼾端在请求Rpc服务的时候,因为可以有 多个rpc-provider可选,因此可以实现简单的负载均衡策略,且基于注册中⼼可以更简便实现发布订阅 的功能。
框架设计
服务端功能需求
(1)基于⽹络通信接收客⼾端的请求,提供rpc服务
(2)基于⽹络通信接收客⼾端的请求,提供服务注册与发现,上线&下线通知
(3)基于⽹络通信接收客⼾端的请求,提供主题操作(创建/删除/订阅/取消),消息发布
服务端模块
Network:⽹络通信模块
功能:实现底层的网络通信功能
基于Muduo网络库实现
处理TCP连接、数据传输
采用非阻塞I/O和事件驱动模型
Protocol:应⽤层通信协议模块
功能:解析数据,解决粘包问题
使用LV格式定义应用层协议
协议格式:[长度(4字节)][类型(4字节)][ID长度(4字节)][ID][Body]
Length:该字段固定4字节⻓度,⽤于表⽰后续的本条消息数据⻓度。
MType:该字段为Value中的固定字段,固定4字节⻓度,⽤于表⽰该条消息的类型。
Rpc调⽤请求/响应类型消息
发布/订阅/取消订阅/消息推送类型消息
主题创建/删除类型消息
服务注册/发现/上线/下线类型消息
IDLength:为消息中的固定字段,该字段固定4字节⻓度,⽤于描述后续ID字段的实际⻓度。
MID:在每条消息中都会有⼀个固定字段为ID字段,⽤于唯⼀标识消息,ID字段⻓度不固定。
Body:消息主题正⽂数据字段,为请求或响应的实际内容字段。
消息类型:
enum class MType {
REQ_RPC, // RPC请求/响应
RSP_RPC,
REQ_TOPIC, // 发布订阅
RSP_TOPIC,
REQ_SERVICE, // 服务操作
RSP_SERVICE
};
Dispatcher:消息分发处理模块
功能:区分消息类型,调用对应的业务处理函数
内部维护hash_map<消息类型, 回调函数>
根据消息类型找到对应的处理回调函数
支持多种消息类型:
RPC请求&响应
服务注册/发现/上线/下线
主题创建/删除/订阅/取消订阅/发布

RpcRouter:远端调⽤路由功能模块
功能:提供RPC请求的处理回调函数
解析RPC请求中的方法名称和参数信息
提供服务描述(参数校验功能)
维护方法名称↔业务回调的映射
服务描述包含:1.服务名称(如"Add")2.参数描述(名称、类型)3.返回值类型4.回调函数
我们用 **"客户端调用 Add(11,22)"** 的完整流程,把这些模块串联起来:
客户端发起 RPC 请求 :客户端把请求序列化为 JSON(比如 {"method":"Add","parameters{"num1":11,"num2":22}}),通过网络发送给服务端。
Dispatcher接收请求 :服务端的 Dispatcher收到请求后,触发 onRpcRequest回调函数。
Dispatcher查找处理函数 :Dispatcher根据消息类型(比如 Add),在自己的 map<消息类型, 回调函数>中找到对应的业务处理回调(这个回调最终会指向 RpcRouter的逻辑)。
RpcRouter介入:路由+校验 :RpcRouter从 hash_map<method, describe>中找到 Add对应的 ServiceDescribe(服务说明书)。然后调用 ServiceDescribe里的参数校验接口 ,检查 num1=11、num2=22是否符合要求(是整数、没有少填)。
RpcRouter调用业务函数 :校验通过后,RpcRouter调用 ServiceDescribe里的业务回调函数 (比如 Add函数),执行 return 11 + 22;得到结果 33。
结果返回客户端 :业务函数的返回值被封装成 RPC 响应(比如 {"rcode":"OK","result":33}),通过 Dispatcher反向传输给客户端。
Publish-Subscribe:发布订阅功能模块
功能:针对发布订阅请求进行处理
主题创建/删除
主题订阅/取消订阅
主题消息发布
内部维护主题与客户端的订阅关系
设计要点:1.主题管理(保存订阅了该主题的客户端连接)2.订阅者管理(保存订阅的主题信息)3.消息推送(将消息推送给所有订阅者)
(1)该模块必须具备⼀个主题管理,且主题中需要保存订阅了该主题的客⼾端连接 ,主题收到⼀条消息,需要将这条消息推送给订阅了该主题的所有客⼾端
(2)该模块必须具备⼀个订阅者管理,且每个订阅者描述中都必须保存⾃⼰所订阅的主题名称 ,⽬的是为了当⼀个订阅客⼾端断开连接时,能够找到订阅信息的关联关系,进⾏删除
(3)该模块必须向外提供 主题创建/销毁,主题订阅/取消订阅,消息发布处理的业务处理函数
1. 订阅者向主题(Topic)订阅感兴趣的消息
2. 发布者向主题发布消息
3. 主题(Broker)将消息转发给所有订阅者
4. 订阅者收到消息并处理
Registry-Discovery:服务注册/发现/上线/下线功能模块
功能:实现分布式架构的服务治理
服务注册(Provider向注册中心登记服务)
服务发现(Caller查询可用服务)
服务上线/下线通知
数据管理:(1)服务发现者管理(记录谁发现过哪些服务)(2)服务提供者管理(记录谁提供哪些服务)
1. 服务提供者启动 → 向注册中心注册服务
2. 服务调用者需要服务 → 查询注册中心
3. 注册中心返回可用服务地址列表
4. 调用者选择地址进行RPC调用
5. 服务上下线时,注册中心通知相关调用者
Server:基于以上模块整合⽽出的服务端模块
功能:将所有模块整合为完整的服务端
RpcServer:RPC功能 + 网络通信
RegistryServer:服务注册发现 + 网络通信
TopicServer:发布订阅 + 网络通信



客户端模块
Network (网络通信模块)
功能:负责与服务器建立连接、断开连接、发送和接收原始网络数据。
实现:基于 Muduo 网络库的 TcpClient进行封装。这是客户端与网络交互的底层基础。
Protocol (应用层通信协议模块)
功能:与服务端保持一致,解析网络字节流,解决TCP粘包问题,从原始数据中提取出完整的、有业务含义的一条消息。
实现:同样采用 LV (Length-Value) 格式协议。它位于 Network 模块之上,确保交付给上层的是完整的消息包。
Dispatcher (消息分发处理模块)
功能:区分收到的消息类型,并根据不同的消息类型(如RPC响应、服务上线通知、主题消息推送),调用预先注册的对应的业务处理函数。
逻辑:与服务端的 Dispatcher 功能对称。客户端在启动时会注册各种消息类型的处理器。当从网络收到一条消息后,Dispatcher 根据其 MType字段找到正确的处理器进行调用。
Requestor (请求管理模块)
功能:这是客户端实现可靠异步通信的核心。它管理客户端发出的每一条请求,确保请求和响应能正确匹配。
解决的核心问题:在异步网络编程中,发送请求和接收响应是独立的时序,无法保证"发一条收一条"的顺序。同时,muduo等异步IO库不提供"发送后阻塞等待"的接口。
实现机制:为每个请求生成一个全局唯一的请求ID。服务端在响应中必须带回对应的请求ID。客户端内部维护一个以请求ID为键的哈希表,存储请求的上下文(如关联的 std::promise或回调函数)。当收到响应时,根据其携带的请求ID,从哈希表中找到对应的请求上下文,将结果存入(如设置 std::promise的值或执行回调),从而解除等待或触发后续处理。
对外接口:提供发送请求并获取其响应的多种方式(阻塞获取、异步Future、回调)。

describe结构体
describe是一个用于封装一次RPC调用所有必要信息的结构体,是客户端发起请求前的准备数据:
request(请求体):这是实际要发送给服务器的数据,包含三个子字段:
(1)RID(Request ID):全局唯一的请求标识符,用于在异步环境中匹配请求和响应。
(2)MType(Message Type):消息类型,用于告知服务器这是什么类型的消息(如调用、心跳等)。
(3)Body(消息体):具体的业务参数,通常是JSON序列化后的字符串。
std::promise<response>:这是C++标准库提供的用于实现异步同步化的工具。如果客户端使用同步调用模式,会传递一个 promise对象,服务器响应到达时通过 set_value来解除阻塞。
callback:回调函数。如果客户端使用异步调用模式,会传递一个回调函数,服务器响应到达时直接调用该函数。
RType(Return Type):返回类型,指定了响应的处理方式,包含两个枚举值:
(1)ASYNC:异步模式,响应到达后触发回调。
(2)CALLBACK:回调模式(图中可能标注重复,通常指代异步回调,或者强调有回调参与)。
Requestor模块
对外接口:提供了两个重载的 send方法:
(1)send(connection, request, callback):异步回调模式,直接传入 callback。
(2)send(connection, request, std::future<response>):同步模式,通过 std::future获取结果。
内部状态:Requestor内部维护了一个 map<rid, describe> 的哈希表。
作用:当请求发出去时,将 describe对象存入表中(以 rid为键),以便后续收到响应时能快速找到是哪个请求的响应。
生命周期:通常响应到达并处理完后,会从这个表中移除对应的记录,防止内存泄漏。
响应入口:onResponse(connection, response)是 Requestor接收来自网络层原始响应的入口函数。
onResponse与 Dispatcher
onResponse函数:
当网络层收到一条响应消息时,会调用这个函数。
函数内部通常会根据响应消息中的 RID去 Requestor内部的 map中查找对应的 describe对象。
Dispatcher分发器:
Dispatcher内部维护了一个 map<mtype, handler> 的映射表。
作用:根据响应消息的 MType,调用预先注册好的 handler(处理器) 来处理该消息。
结合逻辑:虽然 Requestor负责找到具体的请求上下文(通过RID),但具体的业务逻辑(如反序列化、调用用户回调)由 Dispatcher统一管理。
RpcCaller (RPC调用功能模块)
功能:向用户提供进行RPC调用的友好接口。它是对 Requestor 和网络通信的进一步封装,隐藏了底层细节。
提供的调用方式:
同步调用:发起调用后,阻塞当前线程直到收到响应结果后返回。
异步调用:发起调用后立即返回一个 std::future对象,用户可以在未来的某个时间点通过这个对象获取结果(如果结果未就绪则会阻塞)。
回调调用:发起调用的同时,设置一个处理结果的回调函数。当响应返回后,自动在IO线程中执行该回调。

Publish-Subscribe (发布订阅功能模块)
功能:向用户提供发布订阅所需的接口,并处理从服务器推送过来的主题消息。
对内:需要向 Dispatcher 模块注册,以处理"消息推送"类型的请求。
对外:提供主题的创建、删除、订阅、取消订阅以及消息发布等接口。同时,需要管理用户为不同主题设置的消息处理回调函数。

Registry-Discovery (服务注册/发现功能模块)
功能:实现客户端作为"服务提供者"或"服务调用者"时,与注册中心交互的能力。此模块功能较为复杂,包含两个角色:
注册者角色 (Registry):作为RPC服务提供者,需要实现向注册中心注册自己提供的服务。
发现者角色 (Discovery):作为RPC服务调用者,需要实现:
服务发现:向注册中心查询可提供某项服务的主机地址列表,并管理这个列表。
处理通知:关注并处理注册中心发来的服务上线通知和服务下线通知,动态更新本地维护的服务主机列表。
Client (客户端整合模块)
功能:将以上所有功能模块进行整合,形成具体的、可用的客户端程序。
具体实现:
RegistryClient:整合服务注册功能模块与网络通信客户端。
DiscoveryClient:整合服务发现功能模块与网络通信客户端。
RpcClient:整合 DiscoveryClient 和 RPC 功能模块与网络通信客户端。这是最常用的客户端,它先通过服务发现找到可用服务器,再进行RPC调用。
TopicClient:整合发布订阅功能模块与网络通信客户端。




框架设计
在当前项⽬的实现中,我们将整个项⽬的实现划分为三层来进⾏实现
第一层:抽象层
这是整个框架设计的基石和灵魂,体现了"面向接口编程"和"依赖倒置"的思想。
目标 :将核心逻辑与具体的技术实现(如网络库、序列化协议)完全解耦。这使得上层业务代码不关心底层到底用的是Muduo还是Boost.Asio,用的是JSON还是Protobuf。
设计思想 :定义一个完整的、可工作的框架的接口。只要实现了这些接口,框架就能运行。至于这些接口如何实现,是下一层"具象层"的事。
抽象层包含的核心抽象基类:
BaseServer和 BaseClient :定义了服务器和客户端的行为,如启动、设置连接/消息回调、发送数据等。但不涉及如何监听端口、如何建立连接。
BaseConnection :表示一个网络连接的抽象。定义了发送、关闭、查询状态等操作,但不绑定到具体的TCP连接对象。
BaseProtocol :定义了应用层协议的接口。核心是canProcessed(判断数据是否完整)和onMessage(解析数据成消息对象)。这让你可以自由替换"定长"、"LV格式"或"分隔符"等协议。
BaseMessage :定义了所有网络消息的通用接口。每条消息必须有ID、类型,并能进行序列化/反序列化。它不关心消息内部是JSON还是二进制。
BaseBuffer:定义了网络缓冲区的操作,如读取数据、查看数据长度等。用于将底层网络库(如Muduo的Buffer)与上层的协议解析隔离开。
第二层:具象层
这是针对"抽象层"中所有接口的具体实现。可以把它看作是抽象层的"驱动程序"或"插件"。
目标 :用特定的、选定的技术去实现抽象层定义的接口,搭建起框架运行所需的具体基础设施。
设计思想 :继承 + 实现。针对每一个抽象基类,创建一个派生类,在里面填充基于特定第三方库(如Muduo, JsonCpp)的实现代码。
具象层包含的核心实现(基于文档中的技术选型):
MuduoServer和 MuduoClient:基于Muduo网络库的TcpServer和TcpClient,实现了BaseServer和BaseClient的接口。它们是网络通信的实体。
MuduoConnection:内部包装了一个muduo::TcpConnectionPtr,实现了BaseConnection的接口。
LVProtocol:实现了BaseProtocol接口,采用文档中设计的"Length-Value"格式来解决TCP粘包问题。它知道如何从字节流中切分出完整消息。
JsonMessage及其子类:这是具象层的关键扩展。JsonMessage实现了BaseMessage,使用JsonCpp库进行序列化。然后派生出各种具体的业务消息类型:
RpcRequest/ RpcResponse:用于RPC调用的请求和响应。
TopicRequest/ TopicResponse:用于发布订阅的请求和响应。
ServiceRequest/ ServiceResponse:用于服务注册与发现的请求和响应。
这些子类不仅实现了序列化,还提供了便捷的接口来获取和设置各自消息的特有字段(如RPC的方法名、参数等)。
MuduoBuffer:包装了muduo::net::Buffer,实现了BaseBuffer的接口。
工厂类 (Factory Classes):为了方便地创建这些具体对象,文档中还实现了MessageFactory、ConnectionFactory、ServerFactory等,这是工厂模式的应用,进一步隐藏了具体类的创建细节。
总结具象层的作用:它用Muduo+JsonCpp+LV协议这把"螺丝刀",把抽象层的"设计图纸"变成了一个可以实际运行的工具箱。
第三层:业务层
这是在抽象层和具象层构建的通信框架之上,实现项目具体功能的层次。这一层是用户(框架的使用者)主要接触和编写的部分。
目标 :实现RPC、服务注册与发现、发布订阅这三大核心业务功能。
设计思想 :使用底层框架提供的能力,组织业务逻辑和数据流。它调用抽象接口,处理具体的业务消息。
业务层包含的核心模块:
Dispatcher(消息分发器):
位置: 位于网络层之上,是业务逻辑的总入口。
功能: 内部维护一个map<MType, Handler>。当底层网络(通过BaseProtocol)解析出一条消息后,会根据消息的类型(MType),找到预先注册的对应的业务处理器(Handler)并调用。
作用:将"接收网络数据"和"处理业务逻辑"解耦。网络层只负责收发和解析,不关心业务;业务模块只关心自己类型的消息,不关心网络细节。
RPC业务模块:
RpcRouter(服务端): 管理所有注册的RPC服务(方法名 -> 处理函数)。当Dispatcher分派来一个RpcRequest时,RpcRouter负责找到对应的服务,校验参数,然后调用用户注册的实际业务函数,最后封装RpcResponse返回。
Requestor和 RpcCaller(客户端):
Requestor: 请求管理器。为每个请求生成唯一ID,并管理std::promise或回调函数,是客户端实现同步/异步调用的核心。
RpcCaller: 用户友好接口。对Requestor进行封装,向用户提供同步调用、异步Future调用、回调调用三种模式。
Publish-Subscribe (发布订阅) 业务模块:
服务端: 管理主题(Topic)和订阅关系。当收到创建、订阅、发布等TopicRequest时,更新内部数据结构,并在发布消息时,将消息推送给所有订阅了该主题的客户端连接。
客户端: 提供主题操作的接口,并为接收到的推送消息设置处理回调。
Registry-Discovery (服务注册与发现) 业务模块:
服务端 (注册中心): 管理服务提供者列表和发现者列表。处理服务注册、发现请求,并主动向发现者推送服务上线/下线通知。
客户端: 分为"注册者"(Provider)和"发现者"(Caller)两种角色,分别实现向注册中心注册服务,以及从注册中心发现服务、监听服务变化的功能。
**整合的 Server 和 Client:**最后,将上述业务模块与具体的网络模块(MuduoServer/MuduoClient)整合,形成可执行的程序,如RpcServer、RegistryServer、RpcClient等。
项目实现
常用零碎功能接口实现
简单日志宏实现
意义:快速定位程序运⾏逻辑出错的位置。
项⽬在运⾏中可能会出现各种问题,出问题不可怕,关键的是要能找到问题,并解决问题。
解决问题的⽅式:
gdb调试:逐步调试过于繁琐,缓慢。主要⽤于程序崩溃后的定位。
系统运⾏⽇志分析:在任何程序运⾏有可能逻辑错误的位置进⾏输出提⽰,快速定位逻辑问题的位
置。
#pragma once
#include<stdio.h>
#include<time.h>
#define LDBG 0
#define LINF 1
#define LERR 2
namespace myrpc{
#define LDEFAULT LINF
#define LOG(level,format,...){\
if(level >= LDEFAULT)\
{\
time_t t = time(NULL);\
struct tm * lt = localtime(&t);\
char time_tmp[32] = {0};\
strftime(time_tmp,31,"%m-%d %T",lt);\
fprintf(stdout,"[%s][%s:%d]" format "\n",time_tmp,__FILE__,__LINE__,##__VA_ARGS__);\
}\
}
#define DLOG(format,...) LOG(LDBG,format,##__VA_ARGS__);
#define ILOG(format,...) LOG(LINF,format,##__VA_ARGS__);
#define ELOG(format,...) LOG(LERR,format,##__VA_ARGS__);
}
JSON序列化/反序列化
#include<iostream>
#include<string>
#include<memory>
#include<sstream>
#include<jsoncpp/json/json.h>
class JSON{
public:
//实现数据的序列化
static bool serialize(const Json::Value &val,std::string &body)
{
std::stringstream ss;
//先实例化一个工厂类对象
Json::StreamWriterBuilder swb;
// 设置不进行Unicode转义
swb.settings_["emitUTF8"] = true;
//通过工厂类对象来生产派生类对象
std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
int ret = sw->write(val,&ss);
if(ret != 0)
{
ELOG("json serialize failed!\n");
}
body = ss.str();
return true;
}
//实现json字符串的反序列化
static bool unserialize(const std::string &body,Json::Value &val)
{
//实例化工厂类对象
Json::CharReaderBuilder crb;
//生产charreader对象
std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
std::string errs;
bool ret = cr->parse(body.c_str(),body.c_str()+body.size(),&val,&errs);
if(ret == false)
{
ELOG("json unserialialize failed : %s",errs.c_str());
return false;
}
return true;
}
};
生成UUID
一个标准的 UUID 是一个 128 位(16 字节)的数字,通常表示为 32 个十六进制字符,分为 5 组,用连字符分隔:
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
例子:
550e8400-e29b-41d4-a716-446655440000
在这⾥,uuid⽣成,我们采⽤⽣成8个随机数字,加上8字节序号,共16字节数组⽣成32位16进制字符 的组合形式来确保全局唯⼀的同时能够根据序号来分辨数据。
class UUid
{
public:
static std::string uuid()
{
std::stringstream ss;
//1.构造一个机器随机数对象
std::random_device rd;
//2.以机器随机数为种子构造伪随机数对象
std::mt19937 generator (rd());
//3.构造限定数据范围的对象
std::uniform_int_distribution<int> distribution(0,255);
//4.生成8个随机数,按照特定格式组织成为16禁止数字字符的字符串
for(int i = 0;i < 8;i++)
{
if(i == 4 || i == 6)
{
ss <<"-";
}
ss <<std::setw(2)<<std::setfill('0')<<std::hex<<distribution(generator);
}
ss <<"-";
//5.定义一个8字节序号,逐字节组织成为16进制数字字符的字符串
static std::atomic<size_t> seq(1);
size_t cur = seq.fetch_add(1);
for(int i = 7;i >= 0;i--)
{
if(i == 5)
{
ss<<"-";
}
ss << std::setw(2)<<std::setfill('0')<<std::hex<<((cur >> (i*8)) & 0xFF);
}
return ss.str();
}
};
项⽬消息类型字段信息定义
请求字段宏定义
#define KEY_METHOD "method"
#define KEY_PARAMS "parameters"
#define KEY_TOPIC_KEY "topic_key"
#define KEY_TOPIC_MSG "topic_msg"
#define KEY_OPTYPE "optype"
#define KEY_HOST "host"
#define KEY_HOST_IP "ip"
#define KEY_HOST_PORT "port"
#define KEY_RCODE "rcode"
#define KEY_RESULT "result"
| 宏定义 | 对应键名 | 用途说明 | 主要应用的消息类型 |
|---|---|---|---|
#define KEY_METHOD "method" |
"method" |
标识RPC 方法名称或服务名称 | RPC 请求、服务操作请求 / 响应 |
#define KEY_PARAMS "parameters" |
"parameters" |
标识RPC 调用参数(JSON 对象) | RPC 请求 |
#define KEY_TOPIC_KEY "topic_key" |
"topic_key" |
标识主题名称 / 键 | 主题操作请求 / 响应 |
#define KEY_TOPIC_MSG "topic_msg" |
"topic_msg" |
标识主题消息内容 | 主题发布请求 |
#define KEY_OPTYPE "optype" |
"optype" |
标识操作类型(枚举值) | 主题操作、服务操作 |
#define KEY_HOST "host" |
"host" |
标识主机信息(JSON 对象 / 数组) | 服务操作请求 / 响应 |
#define KEY_HOST_IP "ip" |
"ip" |
标识主机 IP 地址 | 服务操作(KEY_HOST的子字段) |
#define KEY_HOST_PORT "port" |
"port" |
标识主机端口号 | 服务操作(KEY_HOST的子字段) |
#define KEY_RCODE "rcode" |
"rcode" |
标识响应状态码 | 所有响应消息(RPC、主题、服务) |
#define KEY_RESULT "result" |
"result" |
标识RPC 调用结果 | RPC 响应 |
消息类型定义
enum class MType
{
REQ_RPC = 0,
RSP_RPC,
REQ_TOPIC,
RSP_TOPIC,
REQ_SERVICE,
RSP_SERVICE
};
| 枚举值 | 数值 | 含义 | 说明 |
|---|---|---|---|
REQ_RPC |
0 | RPC 请求 | 客户端向服务器发送的 RPC 调用请求 |
RSP_RPC |
1 | RPC 响应 | 服务器对 RPC 请求的响应 |
REQ_TOPIC |
2 | 主题操作请求 | 客户端对主题的创建、删除、订阅、取消订阅、发布等操作请求 |
RSP_TOPIC |
3 | 主题操作响应 | 服务器对主题操作请求的响应 |
REQ_SERVICE |
4 | 服务操作请求 | 客户端对服务的注册、发现、上线、下线等操作请求 |
RSP_SERVICE |
5 | 服务操作响应 | 服务器对服务操作请求的响应 |
响应码类型定义
enum class RCode{
RCODE_OK = 0,
RCODE_PARSE_FAILED,
RCODE_ERROR_MSGTYPE,
RCODE_INVALID_MSG,
RCODE_DISCONNECTED,
RCODE_INVALID_PARAMS,
RCODE_NOT_FOUND_SERVICE,
RCODE_INVALID_OPTYPE,
RCODE_NOT_FOUND_TOPIC,
RCODE_INTERNAL_ERROR
};
static std::string errReason(RCode code) {
static std::unordered_map<RCode, std::string> err_map = {
{RCode::RCODE_OK, "成功处理!"},
{RCode::RCODE_PARSE_FAILED, "消息解析失败!"},
{RCode::RCODE_ERROR_MSGTYPE, "消息类型错误!"},
{RCode::RCODE_INVALID_MSG, "⽆效消息"},
{RCode::RCODE_DISCONNECTED, "连接已断开!"},
{RCode::RCODE_INVALID_PARAMS, "⽆效的Rpc参数!"},
{RCode::RCODE_NOT_FOUND_SERVICE, "没有找到对应的服务!"},
{RCode::RCODE_INVALID_OPTYPE, "⽆效的操作类型"},
{RCode::RCODE_NOT_FOUND_TOPIC, "没有找到对应的主题!"},
{RCode::RCODE_INTERNAL_ERROR, "内部错误!"}};
auto it = err_map.find(code);
if (it == err_map.end()) {
return "未知错误!";}
return it->second;
}
| 枚举值 | 数值 | 含义 | 常见触发场景 |
|---|---|---|---|
RCODE_OK |
0 | 成功处理 | 请求被正确处理并返回结果 |
RCODE_PARSE_FAILED |
1 | 消息解析失败 | JSON 反序列化失败、协议解析错误 |
RCODE_ERROR_MSGTYPE |
2 | 消息类型错误 | 收到了不支持的 MType |
RCODE_INVALID_MSG |
3 | 无效消息 | 消息字段缺失、格式不符合规范 |
RCODE_DISCONNECTED |
4 | 连接已断开 | 对方连接已关闭时仍尝试发送 |
RCODE_INVALID_PARAMS |
5 | 无效的 Rpc 参数 | RPC 参数类型不匹配、缺少必需参数 |
RCODE_NOT_FOUND_SERVICE |
6 | 没有找到对应的服务 | 请求的服务名未在服务端注册 |
RCODE_INVALID_OPTYPE |
7 | 无效的操作类型 | TopicOptype 或 ServiceOptype 值非法 |
RCODE_NOT_FOUND_TOPIC |
8 | 没有找到对应的主题 | 操作的主题不存在 |
RCODE_INTERNAL_ERROR |
9 | 内部错误 | 服务端内部异常(如内存分配失败) |
RPC请求类型定义
同步请求:等待收到响应后返回
异步请求:返回异步对象,在需要的时候通过异步对象获取响应结果(还未收到结果会阻塞)
回调请求:设置回调函数,通过回调函数对响应进⾏处理
enum class RType {
REQ_SYNC = 0,
REQ_ASYNC,
REQ_CALLBACK
};
主题操作类型定义
(1)主题创建
(2)主题删除
(3)主题订阅
(4)主题取消订阅
(5)主题消息发布
enum class TopicOptype {
TOPIC_CREATE = 0,
TOPIC_REMOVE,
TOPIC_SUBSCRIBE,
TOPIC_CANCEL,
TOPIC_PUBLISH
};
服务操作类型定义
(1)服务注册
(2)服务发现
(3)服务上线
(4)服务下线
enum class ServiceOptype {
SERVICE_REGISTRY = 0,
SERVICE_DISCOVERY,
SERVICE_ONLINE,
SERVICE_OFFLINE,
SERVICE_UNKNOW
};
通信抽象实现
BaseMessage
BaseBuffer
BaseProtocol
BaseConnection
BaseServer
BaseClient
#pragma once
#include<memory>
#include<functional>
#include"filed.hpp"
namespace myrpc{
class BaseMessage{
public:
using ptr = std::shared_ptr<BaseMessage>;
virtual ~BaseMessage(){}
virtual void setId(const std::string &id){
_rid = id;
}
virtual std::string rid()
{
return _rid;
}
virtual void setMType(MType mtype)
{
_mtype = mtype;
}
virtual MType mtype()
{
return _mtype;
}
virtual std::string serialize() = 0;
virtual bool unserialize(const std::string &msg) = 0;
virtual bool check() = 0;
protected:
MType _mtype;
std::string _rid;
};
class BaseBuffer{
public:
using ptr = std::shared_ptr<BaseBuffer>;
virtual size_t readablesize() = 0;
virtual int32_t peekInt32() = 0;
virtual void retrieveInt32() = 0;
virtual int32_t readInt32() = 0;
virtual std::string retrieveAsString(size_t len) = 0;
private:
};
class Baseprotocol{
public:
using ptr = std::shared_ptr<Baseprotocol>;
virtual bool canProcessed(const BaseBuffer::ptr &buf) = 0;
virtual bool onMessage(const BaseBuffer::ptr &buf,BaseMessage::ptr &msg) = 0;
virtual std::string serialize(const BaseMessage::ptr &msg) = 0;
};
class BaseConnection{
public:
using ptr = std::shared_ptr<BaseConnection>;
virtual void send(const BaseMessage::ptr &msg) = 0;
virtual void shutdown() = 0;
virtual bool connected() = 0;
};
using ConnectionCallback = std::function<void(const BaseConnection::ptr&)>;
using CloseCallback = std::function<void(const BaseConnection::ptr&)>;
using MessageCallback = std::function<void(const BaseConnection::ptr&,BaseMessage::ptr&)>;
class BaseServer{
public:
using ptr = std::shared_ptr<BaseServer>;
virtual void setConnectionCallback(const ConnectionCallback& cb){
_cb_connection = cb;
}
virtual void setCloseCallback(const CloseCallback& cb){
_cb_close = cb;
}
virtual void setMessageCallback(const MessageCallback& cb){
_cb_message = cb;
}
virtual void start() = 0;
protected:
ConnectionCallback _cb_connection;
CloseCallback _cb_close;
MessageCallback _cb_message;
};
class BaseClient{
public:
using ptr = std::shared_ptr<BaseClient>;
virtual void setConnectionCallback(const ConnectionCallback& cb){
_cb_connection = cb;
}
virtual void setCloseCallback(const CloseCallback& cb){
_cb_close = cb;
}
virtual void setMessageCallback(const MessageCallback& cb){
_cb_message = cb;
}
virtual void connect() = 0;
virtual void shutdown() = 0;
virtual bool send(const BaseMessage::ptr&) = 0;\
virtual BaseConnection::ptr connection() = 0;
virtual bool connected() = 0;
protected:
ConnectionCallback _cb_connection;
CloseCallback _cb_close;
MessageCallback _cb_message;
};
}
消息抽象实现
JsonMessage
JsonRequest & JsonResponse
RpcRequest & RpcResponse
TopicRequest & TopicResponse
ServiceRequest & ServiceResponse
#pragma once
#include "detail.hpp"
#include "filed.hpp"
#include "abstrac.hpp"
namespace myrpc{
class JsonMessage : public BaseMessage {
public:
using ptr = std::shared_ptr<JsonMessage>;
virtual std::string serialize() override{
std::string body;
bool ret = JSON::serialize(_body,body);
if(ret == false)
{
return std::string();
}
return body;
}
virtual bool unserialize(const std::string &msg) override{
return JSON::unserialize(msg, _body);
}
protected:
Json::Value _body;
};
class JsonRequest : public JsonMessage{
public:
using ptr = std::shared_ptr<JsonRequest>;
};
class JsonResponse : public JsonMessage{
public:
using ptr = std::shared_ptr<JsonResponse>;
virtual bool check() override{
//在响应中,大部分响应只有响应状态码
//因此只需要判断相应状态码字段是否存在,类型是否正确
if(_body[KEY_RCODE].isNull() == true)
{
ELOG("响应中没有响应状态码!");
return false;
}
if(_body[KEY_RCODE].isIntegral() == false)
{
ELOG("响应状态码类型错误!");
return false;
}
return true;
}
virtual RCode rcode(){
return (RCode)_body[KEY_RCODE].asInt();
}
virtual void setRcode(RCode rcode){
_body[KEY_RCODE] = (int)rcode;
}
};
}
通信Muduo封装实现
MuduoBuffer
MuduoProtocol
MuduoConnection
MuduoServer
MuduoClient
#pragma once
#include<muduo/net/TcpServer.h>
#include<muduo/net/EventLoop.h>
#include<muduo/net/TcpConnection.h>
#include<muduo/net/Buffer.h>
#include <muduo/base/CountDownLatch.h>
#include <muduo/net/EventLoopThread.h>
#include <muduo/net/TcpClient.h>
#include "detail.hpp"
#include "filed.hpp"
#include "abstrac.hpp"
#include "message.hpp"
#include <mutex>
#include <unordered_map>
namespace myrpc{
class MuduoBuffer : public BaseBuffer{
public:
using ptr = std::shared_ptr<MuduoBuffer>;
MuduoBuffer(muduo::net::Buffer *buf):_buf(buf){}
virtual size_t readablesize() override{
return _buf->readableBytes();
}
virtual int32_t peekInt32() override{
//muduo库是一个网络库,从缓冲区取出一个4字节的整形,会进行网络字节序的转换
return _buf->peekInt32();
}
virtual void retrieveInt32() override{
return _buf->retrieveInt32();
}
virtual int32_t readInt32() override{
return _buf->readInt32();
}
virtual std::string retrieveAsString(size_t len)override{
return _buf->retrieveAsString(len);
}
private:
muduo::net::Buffer *_buf;
};
class BufferFactor{
public:
template<typename ...Args>//可变模板参数
static BaseBuffer::ptr create(Args&& ...args){//工厂方法,用于创建MuduoBuffer的智能指针
return std::make_shared<MuduoBuffer>(std::forward<Args>(args)...);
}
private:
};
class LVProtocol : public Baseprotocol{
public:
// |--Len--|--VALUE--|
// |--Len--|--mtype--|--idlen--|--id--|--body--|
using ptr = std::shared_ptr<LVProtocol>;
//判断缓冲区中的数据量是否足够一条消息的处理
virtual bool canProcessed(const BaseBuffer::ptr &buf)override
{
if(buf->readablesize() < lenFieldsLength)
{
return false;
}
int32_t total_len = buf->peekInt32();
if(buf->readablesize() < (total_len + lenFieldsLength))
{
return false;
}
return true;
}
virtual bool onMessage(const BaseBuffer::ptr &buf,BaseMessage::ptr &msg) override{
//当调用onMessage的时候,默认认为缓冲区中的数据足够一条完整的数据
int32_t total_len = buf->readInt32();//读取总长度
MType mtype = (MType)buf->readInt32();//读取数据类型
int32_t idlen = buf->readInt32();//读取id长度
int32_t body_len = total_len - idlen -idlenFieldsLength - mtypeFieldsLength;
std::string id = buf->retrieveAsString(idlen);
std::string body = buf->retrieveAsString(body_len);
msg = MessageFactory::create(mtype);
if (msg.get() == nullptr) {
ELOG("消息类型错误,构造消息对象失败!");
return false;
}
bool ret = msg->unserialize(body);
if (ret == false) {
ELOG("消息正⽂反序列化失败!");
return false;
}
msg->setId(id);
msg->setMType(mtype);
return true;
}
virtual std::string serialize(const BaseMessage::ptr &msg)override
{
// |--Len--|--mtype--|--idlen--|--id--|--body--|
std::string body = msg->serialize();
std::string id = msg->rid();
auto mtype = htonl((int32_t)msg->mtype());
int32_t idlen = htonl(id.size());
int32_t h_total_len = mtypeFieldsLength + idlenFieldsLength + id.size() + body.size();
int32_t n_total_len = htonl(h_total_len);
std::string result;
result.reserve(h_total_len);
result.append((char*)&n_total_len,lenFieldsLength);
result.append((char*)&mtype,mtypeFieldsLength);
result.append((char*)&idlen,idlenFieldsLength);
result.append(id);
result.append(body);
return result;
}
private:
const size_t lenFieldsLength = 4;
const size_t mtypeFieldsLength = 4;
const size_t idlenFieldsLength = 4;
};
class ProtocolFactory{
public:
template<typename ...Args>
static Baseprotocol::ptr create(Args&& ...args) {
return std::make_shared<LVProtocol>(std::forward<Args>(args)...);
}
};
class MuduoConnection: public BaseConnection{
public:
using ptr = std::shared_ptr<MuduoConnection>;
MuduoConnection(const muduo::net::TcpConnectionPtr &conn,const Baseprotocol::ptr &protocol)
:_protocol(protocol),_conn(conn){}
virtual void send(const BaseMessage::ptr &msg)override{
std::string body = _protocol->serialize(msg);
_conn->send(body);
}
virtual void shutdown() override{
_conn->shutdown();
}
virtual bool connected() override{
return _conn->connected();
}
private:
Baseprotocol::ptr _protocol;
muduo::net::TcpConnectionPtr _conn;
};
class ConnectionFactory{
public:
template<typename ...Args>
static BaseConnection::ptr create(Args&& ...args) {
return std::make_shared<MuduoConnection>(std::forward<Args>(args)...);
}
private:
};
class MuduoServer : public BaseServer{
public:
using ptr = std::shared_ptr<MuduoServer>;
MuduoServer(int port) :
_server(&_baseloop,muduo::net::InetAddress("0.0.0.0",port),"MuduoServer",muduo::net::TcpServer::kReusePort),
_protocol(ProtocolFactory::create())
{}
virtual void start(){
_server.setConnectionCallback(std::bind(&MuduoServer::onConnection, this, std::placeholders::_1));
_server.setMessageCallback(std::bind(&MuduoServer::onMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
_server.start();//先开始监听
_baseloop.loop();//开始死循环事件监控)
}
private:
void onConnection(const muduo::net::TcpConnectionPtr &conn)
{
if(conn->connected()){
std::cout << "建立连接!\n";
auto muduo_conn = ConnectionFactory::create(conn,_protocol);
{
std::unique_lock<std::mutex> lock(_mutex);
_conns.insert(std::make_pair(conn,muduo_conn));
}
if(_cb_connection)
{
_cb_connection(muduo_conn);
}
}
else{
std::cout << "连接断开!\n";
BaseConnection::ptr muduo_conn;
{
std::unique_lock<std::mutex> lock(_mutex);
auto it = _conns.find(conn);
if(it == _conns.end())
{
return ;
}
muduo_conn = it->second;
_conns.erase(conn);
}
if(_cb_close)
{
_cb_close(muduo_conn);
}
}
}
void onMessage(const muduo::net::TcpConnectionPtr &conn, muduo::net::Buffer *buf, muduo::Timestamp){
DLOG("连接有数据到来,开始处理!");
auto base_buf = BufferFactor::create(buf);
while(1) {
if (_protocol->canProcessed(base_buf) == false) {
//数据不⾜
if (base_buf->readablesize() > maxDataSize) {
conn->shutdown();
ELOG("缓冲区中数据过⼤!");
return ;
}
//DLOG("数据量不⾜!");
break;
}
//DLOG("缓冲区中数据可处理!");
BaseMessage::ptr msg;
bool ret = _protocol->onMessage(base_buf, msg);
if (ret == false) {
conn->shutdown();
ELOG("缓冲区中数据错误!");
return ;
}
//DLOG("消息反序列化成功!")
BaseConnection::ptr base_conn;
{
std::unique_lock<std::mutex> lock(_mutex);
auto it = _conns.find(conn);
if (it == _conns.end()) {
conn->shutdown();
return;
}
base_conn = it->second;
}
//DLOG("调⽤回调函数进⾏消息处理!");
if (_cb_message) _cb_message(base_conn, msg);
}
}
private:
const size_t maxDataSize = (1 << 16);
Baseprotocol::ptr _protocol;
muduo::net::EventLoop _baseloop;
muduo::net::TcpServer _server;
std::mutex _mutex;
std::unordered_map<muduo::net::TcpConnectionPtr, BaseConnection::ptr> _conns;
};
class MuduoClient : public BaseClient{
public:
using ptr = std::shared_ptr<MuduoClient>;
MuduoClient(const std::string &sip,int sport):
_protocol(ProtocolFactory::create()),
_baseloop(_loopthread.startLoop()),
_downlatch(1),
_client(_baseloop,muduo::net::InetAddress(sip,sport),"MuduoClient")
{}
virtual void connect()override{
DLOG("设置回调函数,连接服务器");
_client.setConnectionCallback(std::bind(&MuduoClient::onConnection, this, std::placeholders::_1));
//设置连接消息的回调
_client.setMessageCallback(std::bind(&MuduoClient::onMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
//连接服务器
_client.connect();
_downlatch.wait();
DLOG("连接服务器成功!");
}
virtual void shutdown() override{
return _client.disconnect();
}
virtual bool send(const BaseMessage::ptr &msg)override{
if(connected() == false)
{
ELOG("链接已断开");
return false;
}
_conn->send(msg);
return true;
}
virtual BaseConnection::ptr connection()override{
return _conn;
}
virtual bool connected()
{
return(_conn && _conn->connected());
}
private:
void onConnection(const muduo::net::TcpConnectionPtr &conn)
{
if(conn->connected())
{
std::cout << "建立连接!\n";
_conn = ConnectionFactory::create(conn, _protocol);
_downlatch.countDown();
}
else
{
std::cout << "连接断开!\n";
_conn.reset();
}
}
void onMessage(const muduo::net::TcpConnectionPtr &conn,muduo::net::Buffer *buf,muduo::Timestamp)
{
DLOG("连接数据到来,开始处理");
auto base_buf = BufferFactor::create(buf);
while(1)
{
if(_protocol->canProcessed(base_buf) == false)
{
//数据不足
if(base_buf->readablesize() > maxDataSize)
{
conn->shutdown();
ELOG("缓冲区数据量过大!");
return ;
}
break;
}
BaseMessage::ptr msg;
bool ret = _protocol->onMessage(base_buf,msg);
if(ret == false)
{
conn->shutdown();
ELOG("缓冲区数据错误");
return ;
}
if(_cb_message)
{
_cb_message(_conn,msg);
}
}
}
private:
const size_t maxDataSize = (1 << 16);
Baseprotocol::ptr _protocol;
BaseConnection::ptr _conn;
muduo::CountDownLatch _downlatch;
muduo::net::EventLoopThread _loopthread;
muduo::net::EventLoop *_baseloop;
muduo::net::TcpClient _client;
};
class ClientFactory{
public:
template<typename ... Args>
static BaseClient::ptr create(Args&& ...args){
return std::make_shared<MuduoClient>(std::forward<Args>(args)...);
}
};
}
不同消息封装实现
JsonRequest:
(1)RpcRequest
(2)TopicRequest
(3)ServiceRequest
JsonResponse:
(1)RpcResponse
(2)TopicResponse
(3)ServiceResponse
#pragma once
#include "detail.hpp"
#include "filed.hpp"
#include "abstrac.hpp"
namespace myrpc
{
class RpcRequest : public JsonRequest{
public:
using ptr = std::shared_ptr<RpcRequest>;
virtual bool check() override{
//rpc请求中,包含请求方法名称-字符串,参数字段-对象
if(_body[KEY_METHOD].isNull() == true ||
_body[KEY_METHOD].isString() == false){
ELOG("RPC请求方法中没有方法名称或方法名称错误!");
return false;
}
if(_body[KEY_PARAMS].isNull() == true ||
_body[KEY_PARAMS].isString() == false){
ELOG("RPC请求方法中没有参数信息或参数信息类型错误!");
return false;
}
return true;
}
std::string method(){
return _body[KEY_METHOD].asString();
}
void SetMethod(const std::string &method_name)
{
_body[KEY_METHOD] = method_name;
}
Json::Value params(){
return _body[KEY_PARAMS];
}
void Setparams(const Json::Value ¶ms)
{
_body[KEY_METHOD] = params;
}
};
typedef std::pair<std::string,int> Address;
class TopicRequest : public JsonRequest{
public:
using ptr = std::shared_ptr<TopicRequest>;
virtual bool check() override{
//rpc请求中,包含请求方法名称-字符串,参数字段-对象
if(_body[KEY_TOPIC_KEY].isNull() == true ||
_body[KEY_TOPIC_KEY].isString() == false){
ELOG("主题请求方法中没有方法名称或方法名称错误!");
return false;
}
if(_body[KEY_OPTYPE].isNull() == true ||
_body[KEY_OPTYPE].isString() == false){
ELOG("主题请求方法中没有参数信息或参数信息类型错误!");
return false;
}
if(_body[KEY_OPTYPE].isNull() == true
|| _body[KEY_OPTYPE].isString() == false){
ELOG("主题消息发布请求中没有消息内容字段或消息内容类型错误");
return false;
}
return true;
}
std::string topicKey(){//获取主题的唯一标识符
return _body[KEY_TOPIC_KEY].asString();
}
void setTopicKey(const std::string &key)//设置主题的唯一标识符
{
_body[KEY_TOPIC_KEY] = key;
}
TopicOptype optype(){//返回主题操作的类型枚举
return (TopicOptype)_body[KEY_OPTYPE].asInt();
}
void setoptye(TopicOptype optype){//设置主题操作的枚举类型
_body[KEY_OPTYPE] = (int)optype;
}
std::string topicMsg(){//返回主题相关的消息内容
return _body[KEY_TOPIC_KEY].asString();
}
void setTopicMsg(const std::string &msg){//设置主题的消息内容
_body[KEY_TOPIC_KEY] = msg;
}
};
class ServiceRequest : public JsonRequest{
public:
using ptr = std::shared_ptr<ServiceRequest>;
virtual bool check() override{
//rpc请求中,包含请求方法名称-字符串,参数字段-对象
if(_body[KEY_METHOD].isNull() == true ||
_body[KEY_METHOD].isString() == false){
ELOG("服务请求方法中没有方法名称或方法名称错误!");
return false;
}
if(_body[KEY_OPTYPE].isNull() == true ||
_body[KEY_OPTYPE].isString() == false){
ELOG("服务请求方法中没有参数信息或参数信息类型错误!");
return false;
}
if(_body[KEY_OPTYPE].asInt() != (int)(ServiceOptype::SERVICE_DISCOVERY)
&& (_body[KEY_HOST].isNull() == true || _body[KEY_HOST].isObject() == false
|| _body[KEY_HOST][KEY_HOST_IP].isNull() == true ||
_body[KEY_HOST][KEY_HOST_IP].isString() == false ||
_body[KEY_HOST][KEY_HOST_PORT].isNull() == true ||
_body[KEY_HOST][KEY_HOST_PORT].isIntegral() == false)){
ELOG("服务请求中主机地址信息错误");
return false;
}
return true;
}
std::string method(){
return _body[KEY_METHOD].asString();
}
void SetMethod(const std::string &name){
_body[KEY_METHOD] = name;
}
ServiceOptype optype()
{
return (ServiceOptype)_body[KEY_OPTYPE].asInt();
}
void setoptye(ServiceOptype optype){//设置主题操作的枚举类型
_body[KEY_OPTYPE] = (int)optype;
}
Address host()//获取服务器的IP地址和端口号
{
Address addr;
// 从 JSON 的 _body[KEY_HOST] 对象中提取 IP 地址
addr.first = _body[KEY_HOST][KEY_HOST_IP].asString();
// 从同一个 JSON 对象中提取端口号
addr.second = _body[KEY_HOST][KEY_HOST_PORT].asInt();
return addr;// 返回包含 IP 和端口的 Address 对象
}
void setHost(const Address &host)
{
Json::Value val;
val[KEY_HOST_IP] = host.first;
val[KEY_HOST_PORT] = host.second;
_body[KEY_HOST] = val;
}
};
class RpcResponse : public JsonResponse{
public:
using ptr = std::shared_ptr<RpcResponse>;
virtual bool check() override{
//rpc请求中,包含请求方法名称-字符串,参数字段-对象
if(_body[KEY_RCODE].isNull() == true ||
_body[KEY_RCODE].isString() == false){
ELOG("响应中没有响应状态码,或状态码类型错误");
return false;
}
if(_body[KEY_RESULT].isNull() == true){
ELOG("响应中没有rpc调用该结果,或结果类型错误!");
return false;
}
return true;
}
Json::Value result()//从 JSON 响应数据中提取 RPC 调用的返回结果
{
return _body[KEY_RESULT];
}
void serResult(const Json::Value & result){
_body[KEY_RESULT] = result;
}
};
class TopicResponse : public JsonResponse{
public:
using ptr = std::shared_ptr<TopicResponse>;
};
class ServiceResponse : public JsonResponse
{
public:
using ptr = std::shared_ptr<ServiceResponse>;
virtual bool check() override{
//rpc请求中,包含请求方法名称-字符串,参数字段-对象
if(_body[KEY_RCODE].isNull() == true ||
_body[KEY_RCODE].isIntegral() == false){
ELOG("响应中没有响应状态码,或状态码类型错误");
return false;
}
if(_body[KEY_OPTYPE].isNull() == true ||
_body[KEY_OPTYPE].isIntegral() == false){
ELOG("响应中没有操作类型,或操作类型的类型错误!");
return false;
}
if(_body[KEY_OPTYPE].asInt() != (int)(ServiceOptype::SERVICE_DISCOVERY)
&& (_body[KEY_METHOD].isNull() == true ||
_body[KEY_METHOD].isString() == false ||
_body[KEY_HOST].isNull() == true ||
_body[KEY_HOST].isArray() == false)){
ELOG("服务发现响应中响应信息字段错误");
return false;
}
return true;
}
std::string method(){
return _body[KEY_METHOD].asString();
}
void SetMethod(const std::string &name){
_body[KEY_METHOD] = name;
}
ServiceOptype optype()
{
return (ServiceOptype)_body[KEY_OPTYPE].asInt();
}
void setoptye(ServiceOptype optype){//设置主题操作的枚举类型
_body[KEY_OPTYPE] = (int)optype;
}
//一对用于处理主机地址列表的 setter 和 getter 函数,专门用于管理多个服务器地址。
void setHost(std::vector<Address> addrs){//将多个服务器地址设置到 JSON 数据中,把地址列表转换为 JSON 数组格式。
for(auto &addr : addrs){
Json::Value val;
val[KEY_HOST_IP] = addr.first;
val[KEY_HOST_PORT] = addr.second;
_body[KEY_HOST].append(val);
}
}
std::vector<Address> hosts(){//从 JSON 数据中提取所有服务器地址,把 JSON 数组转换回地址列表。
std::vector<Address> addrs;
int sz = _body[KEY_HOST].size();
for(int i = 0;i < sz;i++)
{
Address addr;
addr.first = _body[KEY_HOST][i][KEY_HOST_IP].asString();
addr.second = _body[KEY_HOST][i][KEY_HOST_PORT].asInt();
addrs.push_back(addr);
}
return addrs;
}
};
class MessageFactory{//创建一个消息工厂类,根据消息类型创建对应的消息对象
public:
static BaseMessage::ptr create(MType mtype)
{
switch(mtype){
case MType::REQ_RPC : return std::make_shared<RpcRequest>();
case MType::RSP_RPC : return std::make_shared<RpcResponse>();
case MType::REQ_TOPIC : return std::make_shared<TopicRequest>();
case MType::RSP_TOPIC : return std::make_shared<TopicResponse>();
case MType::REQ_SERVICE : return std::make_shared<ServiceRequest>();
case MType::RSP_SERVICE : return std::make_shared<ServiceResponse>();
}
return BaseMessage::ptr();
}
template<typename T,typename ...Args>//模板工厂方法
static std::shared_ptr<T> create(Args&& ...args){
return std::make_shared<T>(std::forward(args)...);
}
};
}
Dispacther实现
#pragma once
#include "net.hpp"
#include"message.hpp"
namespace myrpc{
class Callback{
public:
using ptr = std::shared_ptr<Callback>;
virtual void onMessage(const BaseConnection::ptr &conn,BaseMessage::ptr &msg) = 0;
};
template<typename T>
class CallbackT : public Callback{
public:
using ptr = std::shared_ptr<CallbackT>;
using MessageCallback = std::function<void(const BaseConnection::ptr &conn,std::shared_ptr<T> &msg)>;
CallbackT(const MessageCallback &handler):_handler(handler) { }
void onMessage(const BaseConnection::ptr &conn,BaseMessage::ptr &msg) override{
auto type_msg = std::dynamic_pointer_cast<T>(msg);
_handler(conn,type_msg);
}
private:
MessageCallback _handler;
};
class Dispatcher{
public:
using ptr = std::shared_ptr<Dispatcher>;
template<typename T>
void registerHandler(MType mtype,const typename CallbackT<T>::MessageCallback &handler){
std::unique_lock<std::mutex> lock(_mutex);
auto cb = std::make_shared<CallbackT<T>>(handler);
_handlers.insert(std::make_pair(mtype,cb));
}
void onMessage(const BaseConnection::ptr &conn,BaseMessage::ptr &msg)
{
//找到消息类型对应的业务处理函数,进行调用即可
std::unique_lock<std::mutex> lock(_mutex);
auto it = _handlers.find(msg->mtype());
if(it != _handlers.end())
{
return it->second->onMessage(conn,msg);
}
//没有找到指定类型的处理回调--因为客户端和服务端都是我们自己编写的所以不可能出现这种情况
ELOG("收到未知类型的消息: %d!",msg ->mtype());
conn->shutdown();
}
private:
std::mutex _mutex;
std::unordered_map<MType,Callback::ptr> _handlers;
};
}
服务端RPCRouter实现
提供Rpc请求处理回调函数
内部的服务管理:
(1)⽅法名称
(2)参数信息
(3)对外提供参数校验接⼝
#pragma once
#include "net.hpp"
#include "message.hpp"
#include"filed.hpp"
namespace myrpc{
namespace server{
enum class VType{
BOOL = 0,
INTEGRAL,
NUMERIC,
STRING,
ARRAY,
OBJECT,
};
class serviceDescribe{
public:
using ptr = std::shared_ptr<serviceDescribe>;
using ServiceCallback = std::function<void(const Json::Value&,Json::Value &)>;
using ParamsDescribe = std::pair<std::string,VType>;
serviceDescribe(std::string &&mname,std::vector<ParamsDescribe> &&desc,VType vtype,ServiceCallback &&handler):
_method_name(std::move(mname)),_callback(std::move(handler)),
_params_desc(std::move(desc)), _return_type(vtype)
{}
const std::string &method()
{
return _method_name;
}
//针对收到的请求中的参数进行校验
bool paramCheck(const Json::Value ¶ms)
{
//对params进行参数校验--判断所描述的参数字段是否存在,类型是否一致
for(auto &desc : _params_desc)
{
if(params.isMember(desc.first) == false)
{
ELOG("参数字段完整性校验失败! %s字段缺失!",desc.first.c_str());
return false;
}
if (check(desc.second, params[desc.first]) == false)
{
ELOG("%s 参数类型校验失败!", desc.first.c_str());
return false;
}
}
return true;
}
bool call(const Json::Value ¶ms,Json::Value &result)
{
_callback(params,result);
if(rtypeCheck(result) == false)
{
ELOG("回调处理函数中的相应信息校验失败!");
return false;
}
return true;
}
private:
bool rtypeCheck(const Json::Value &val)
{
return check(_return_type,val);
}
bool check(VType vtype,const Json::Value &val)
{
switch(vtype)
{
case VType::BOOL : return val.isBool();
case VType::INTEGRAL : return val.isIntegral();
case VType::NUMERIC :return val.isNumeric();
case VType::STRING :return val.isString();
case VType::ARRAY :return val.isArray();
case VType::OBJECT :return val.isObject();
}
return false;
}
private:
std::string _method_name;//方法名称
ServiceCallback _callback;//实际的业务回调函数
std::vector<ParamsDescribe> _params_desc;//参数字段格式描述
VType _return_type;//结果作为返回值类型的描述
};
class SDescribeFactory{
public:
void setMethodName(const std::string &name)
{
_method_name = name;
}
void setReturnType(VType vtype)
{
_return_type = vtype;
}
void setParamsDesc(const std::string &pname,VType vtype)
{
_params_desc.push_back(serviceDescribe::ParamsDescribe(pname,vtype));
}
void setCallback(const serviceDescribe::ServiceCallback &cb)
{
_callback = cb;
}
serviceDescribe::ptr build()
{
return std::make_shared<serviceDescribe>(std::move(_method_name), std::move(_params_desc), _return_type, std::move(_callback));
}
private:
std::string _method_name;
serviceDescribe::ServiceCallback _callback;//实际的业务回调函数
std::vector<serviceDescribe::ParamsDescribe> _params_desc;//参数字段格式描述
VType _return_type;//结果作为返回值类型的描述
};
class ServiceManager{
public:
using ptr = std::shared_ptr<ServiceManager>;
void insert(const serviceDescribe::ptr &desc)
{
std::unique_lock<std::mutex> lock(_mutex);
_services.insert(std::make_pair(desc->method(),desc));
}
serviceDescribe::ptr select(const std::string &method_name)
{
std::unique_lock<std::mutex> lock(_mutex);
auto it = _services.find(method_name);
if(it == _services.end())
{
return serviceDescribe::ptr();
}
return it->second;
}
void remove(const std::string &method_name)
{
std::unique_lock<std::mutex> lock(_mutex);
_services.erase(method_name);
}
private:
std::mutex _mutex;
std::unordered_map<std::string,serviceDescribe::ptr> _services;
};
class RpcRouter{
public:
using ptr = std::shared_ptr<RpcRouter>;
RpcRouter(): _service_manager(std::make_shared<ServiceManager>()){}
//这是注册到Dispatcher模块针对rpc请求进行回调处理的业务函数
void onRpcRequest(const BaseConnection::ptr &conn,RpcRequest::ptr &request)
{
//1.查询客户端请求的方法描述--判断当前服务端能否提供对应的服务
auto service = _service_manager->select(request->method());
if(service.get() == nullptr)
{
ELOG("%s 服务未找到!",request->method().c_str());
return response(conn,request,Json::Value(),RCode::RCODE_NOT_FOUND_SERVICE);
}
//2.进行参数校验,确定能否提供服务
if(service->paramCheck(request->params()) == false)
{
ELOG("%s 服务参数校验失败!",request->method().c_str());
return response(conn, request, Json::Value(), RCode::RCODE_INVALID_PARAMS);
}
//3.运用业务回调接口进行业务处理
Json::Value result;
bool ret = service->call(request->params(),result);
if(ret == false)
{
ELOG(" %s 服务校验失败!",request->method().c_str());
return response(conn,request,Json::Value(),RCode::RCODE_INTERNAL_ERROR);
}
//4.处理完毕得到结果,组织响应,向客户端发送
return response(conn,request,result,RCode::RCODE_OK);
}
void registerMethod(const serviceDescribe::ptr &service)
{
return _service_manager->insert(service);
}
private:
void response(const BaseConnection::ptr &conn,const RpcRequest::ptr &req,const Json::Value &res,RCode rcode)
{
auto msg = MessageFactory::create<RpcResponse>();
msg->setId(req->rid());
msg->setMType(myrpc::MType::RSP_RPC);
msg->setRcode(rcode);
msg->serResult(res);
conn->send(msg);
}
private:
ServiceManager::ptr _service_manager;
};
};
}
服务端Publish&Subscribe实现
(1)对外提供主题操作处理回调函数
(2)对外提供消息发布处理回调函数
(3)内部进⾏主题及订阅者的管理
#pragma once
#include "net.hpp"
#include "message.hpp"
#include<unordered_set>
namespace myrpc{
namespace server{
class TopicManager{
public:
using ptr = std::shared_ptr<TopicManager>;
TopicManager(){};
void onTopicRequest(const BaseConnection::ptr &conn,const TopicRequest::ptr &msg)
{
TopicOptype topic_optype = msg->optype();
bool ret = true;
switch(topic_optype)
{
case TopicOptype::TOPIC_CREATE: topicCreate(conn, msg); break;
//主题的删除
case TopicOptype::TOPIC_REMOVE: topicRemove(conn, msg); break;
//主题的订阅
case TopicOptype::TOPIC_SUBSCRIBE: ret = topicSubscribe(conn, msg); break;
//主题的取消订阅
case TopicOptype::TOPIC_CANCEL: topicCancel(conn, msg); break;
//主题消息的发布
case TopicOptype::TOPIC_PUBLISH: ret = topicPublish(conn, msg); break;
default: return errorResponse(conn, msg, RCode::RCODE_INVALID_OPTYPE);
}
if (!ret) return errorResponse(conn, msg, RCode::RCODE_NOT_FOUND_TOPIC);
return topicResponse(conn,msg);
}
//一个订阅者在连接断开时的处理--删除其关联的数据
void onShutdown(const BaseConnection::ptr &conn)
{
//消息发布者断开连接不需要任何操作,消息订阅者断开连接需要删除管理数据
//1.判断断开连接的是否是订阅者,不是的话则直接返回
std::vector<Topic::ptr> topics;
Subscriber::ptr subscriber;
{
std::unique_lock<std::mutex> lock(_mutex);
auto it = _subscribers.find(conn);
if (it == _subscribers.end()) {
return;//断开的连接,不是⼀个订阅者的连接
}
subscriber = it->second;
//2.获取到订阅者退出受影响的主题对象
for(auto &topic_name : subscriber->topics)
{
auto topic_it = _topics.find(topic_name);
if(topic_it == _topics.end())
{
continue;
}
topics.push_back(topic_it->second);
}
//4.从订阅者映射信息中删除订阅者
_subscribers.erase(it);
}
//3.从受影响的主题对象中,移除订阅者
for (auto &topic : topics) {
topic->removeSubscriber(subscriber);
}
}
private:
void errorResponse(const BaseConnection::ptr &conn,const TopicRequest::ptr &msg,RCode rocde)
{
auto msg_rsp = MessageFactory::create<TopicResponse>();
msg_rsp->setId(msg->rid());
msg_rsp->setMType(MType::RSP_TOPIC);
msg_rsp->setRcode(rocde);
conn->send(msg_rsp);
}
void topicResponse(const BaseConnection::ptr &conn,const TopicRequest::ptr &msg)
{
auto msg_rsp = MessageFactory::create<TopicResponse>();
msg_rsp->setId(msg->rid());
msg_rsp->setMType(MType::RSP_TOPIC);
msg_rsp->setRcode(RCode::RCODE_OK);
conn->send(msg_rsp);
}
void topicCreate(const BaseConnection::ptr &conn, const TopicRequest::ptr &msg) {std::unique_lock<std::mutex> lock(_mutex);
//构造⼀个主题对象,添加映射关系的管理
std::string topic_name = msg->topicKey();
auto topic = std::make_shared<Topic>(topic_name);
_topics.insert(std::make_pair(topic_name, topic));
}
void topicRemove(const BaseConnection::ptr &conn,const TopicRequest::ptr &msg)
{
// 1. 查看当前主题,有哪些订阅者,然后从订阅者中将主题信息删除掉
// 2. 删除主题的数据 -- 主题名称与主题对象的映射关系
std::string topic_name = msg->topicKey();
std::unordered_set<Subscriber::ptr> subscribers;
{
std::unique_lock<std::mutex> lock(_mutex);
//在删除主题之前,先找出会受到影响的订阅者
auto it = _topics.find(topic_name);
if (it == _topics.end()) {
return;
}
subscribers = it->second->subscribers;
_topics.erase(it);//删除当前的主题映射关系
}
for(auto &Subscriber : subscribers)
{
Subscriber->removerTopic(topic_name);
}
}
bool topicSubscribe(const BaseConnection::ptr &conn,const TopicRequest::ptr &msg)
{
//1. 先找出主题对象,以及订阅者对象
// 如果没有找到主题-就要报错; 但是如果没有找到订阅者对象,那就要构造⼀个订阅者
Topic::ptr topic;
Subscriber::ptr subscriber;
{
std::unique_lock<std::mutex> lock(_mutex);
auto topic_it = _topics.find(msg->topicKey());
if(topic_it == _topics.end())
{
return false;
}
topic = topic_it->second;
auto sub_it = _subscribers.find(conn);
if (sub_it != _subscribers.end()) {
subscriber = sub_it->second;
}else {
subscriber = std::make_shared<Subscriber>(conn);
_subscribers.insert(std::make_pair(conn, subscriber));
}
}
//2.在主题对象中,新增一个订阅者对象关联的连接,在订阅者对象中新增一个订阅的主题
topic->appendSubscriber(subscriber);
subscriber->appendTopic(msg->topicKey());
return true;
}
void topicCancel(const BaseConnection::ptr &conn,const TopicRequest::ptr &msg)
{
//1.先找出主题对象和订阅者对象
Topic::ptr topic;
Subscriber::ptr subscriber;
{
std::unique_lock<std::mutex> lock(_mutex);
auto topic_it = _topics.find(msg->topicKey());
if (topic_it != _topics.end()) {
topic = topic_it->second;
}
auto sub_it = _subscribers.find(conn);
if (sub_it != _subscribers.end()) {
subscriber = sub_it->second;
}
}
//2.从主题对象中删除当前订阅者连接,从订阅者信息中删除所订阅的主题名称
if(subscriber)
{
subscriber->removerTopic(msg->topicKey());
}
if(topic &&subscriber)
{
topic->removeSubscriber(subscriber);
}
}
bool topicPublish(const BaseConnection::ptr &conn,const TopicRequest::ptr &msg)
{
Topic::ptr topic;
{
std::unique_lock<std::mutex> lock(_mutex);
auto topic_it = _topics.find(msg->topicKey());
if (topic_it == _topics.end()) {
return false;
}
topic = topic_it->second;
}
topic->pushMessage(msg);
return true;
}
private:
struct Subscriber{
using ptr = std::shared_ptr<Subscriber>;
std::mutex _mutex;
BaseConnection::ptr conn;
std::unordered_set<std::string> topics;//订阅者所订阅的主题名
Subscriber(const BaseConnection::ptr &c): conn(c)
{}
//订阅主题的时候调用
void appendTopic(const std::string &topic_name)
{
std::unique_lock<std::mutex> lock(_mutex);
topics.insert(topic_name);
}
//主题被删除 或者取消订阅的时候调用
void removerTopic(const std::string &topic_name)
{
std::unique_lock<std::mutex> lock(_mutex);
topics.erase(topic_name);
}
};
struct Topic{
using ptr = std::shared_ptr<Topic>;
std::mutex _mutex;
std::string topic_name;
std::unordered_set<Subscriber::ptr> subscribers; //当前主题的订阅者
Topic(const std::string &name) : topic_name(name){}
//新增订阅的时候调用
void appendSubscriber(const Subscriber::ptr &subscriber)
{
std::unique_lock<std::mutex> lock(_mutex);
subscribers.insert(subscriber);
}
//取消订阅 或者 订阅者断开连接的时候调用
void removeSubscriber(const Subscriber::ptr &subscriber)
{
std::unique_lock<std::mutex> lock(_mutex);
subscribers.erase(subscriber);
}
//收到消息发布请求的时候调用
void pushMessage(const BaseMessage::ptr &msg)
{
std::unique_lock<std::mutex> lock(_mutex);
for(auto &subscriber : subscribers)
{
subscriber->conn->send(msg);
}
}
};
private:
std::mutex _mutex;
std::unordered_map<std::string,Topic::ptr> _topics;
std::unordered_map<BaseConnection::ptr,Subscriber::ptr> _subscribers;
};
}
}
服务端Registry&Discovery实现
对外提供服务操作(注册/发现)消息处理回调函数
内部进⾏服务发现者的管理
内部进⾏服务提供者的管理
#pragma once
#include"net.hpp"
#include"message.hpp"
#include<set>
namespace myrpc{
namespace server{
class ProviderManager{
public:
using ptr = std::shared_ptr<ProviderManager>;
struct Provider{
using ptr = std::shared_ptr<Provider>;
std::mutex _mutex;
BaseConnection::ptr conn;
Address host;
std::vector<std::string> methods;
Provider(const BaseConnection::ptr &c,const Address &h):
conn(c),host(h){}
void appendMethod(const std::string &method)
{
std::unique_lock<std::mutex> lock(_mutex);
methods.emplace_back(method);
}
};
//当一个新的服务提供者进行服务注册的时候调用
void addProvider(const BaseConnection::ptr &c,const Address &h,const std::string &method)
{
Provider::ptr provider;
//查找连接所关联的服务器提供着对象,找到则获取,找不到则创建,并建立关联
{
std::unique_lock<std::mutex> lock(_mutex);
auto it = _conns.find(c);
if (it != _conns.end()) {
provider = it->second;
}else {
provider = std::make_shared<Provider>(c, h);
_conns.insert(std::make_pair(c, provider));
}
//method方法的提供主机要多出一个,_providers新增数据
auto &providers = _providers[method];
providers.insert(provider);
}
//向服务对象中新增一个所能提供的服务名称
provider->appendMethod(method);
}
//当一个服务提供者断开连接的时候,获取他的信息--用于进行服务下线通知
Provider::ptr getProvider(const BaseConnection::ptr &c)
{
std::unique_lock<std::mutex> lock(_mutex);
auto it = _conns.find(c);
if (it != _conns.end()) {
return it->second;
}
return Provider::ptr();
}
//当一个服务提供者断开连接的时候,删除它的关联信息
void delProvider(const BaseConnection::ptr &c)
{
std::unique_lock<std::mutex> lock(_mutex);
auto it = _conns.find(c);
if(it == _conns.end())
{
//当前断开连接的不是一个服务提供者
return;
}
//如果是提供者,看看提供了什么服务,从服务作者提供信息中删除当前服务提供者
for(auto & method : it->second->methods)
{
auto &providers = _providers[method];
providers.erase(it->second);
}
//删除连接与服务提供者的关联关系
_conns.erase(it);
}
std::vector<Address> methodHosts(const std::string &method)
{
std::unique_lock<std::mutex> lock(_mutex);
auto it = _providers.find(method);
if (it == _providers.end()) {
return std::vector<Address>();
}
std::vector<Address> result;
for (auto &provider : it->second) {
result.push_back(provider->host);
}
return result;
}
private:
std::mutex _mutex;
std::unordered_map<std::string,std::set<Provider::ptr>> _providers;
std::unordered_map<BaseConnection::ptr,Provider::ptr> _conns;
};
class DiscovererManager{
public:
using ptr = std::shared_ptr<DiscovererManager>;
struct Discoverer{
using ptr = std::shared_ptr<Discoverer>;
std::mutex _mutex;
BaseConnection::ptr conn;//发现者关联的客户端连接
std::vector<std::string> methods;//发现过的服务名称
Discoverer(const BaseConnection::ptr &c) : conn(c)
{}
void appendMethod(const std::string &method)
{
std::unique_lock<std::mutex> lock(_mutex);
methods.push_back(method);
}
};
//当客户端进行服务发现的说实话新增发现者新增服务名称
Discoverer::ptr addDiscoverer(const BaseConnection::ptr &c,const std::string &method)
{
Discoverer::ptr discoverer;
{
std::unique_lock<std::mutex> lock(_mutex);
auto it = _conns.find(c);
if (it != _conns.end()) {
discoverer = it->second;
}else {
discoverer = std::make_shared<Discoverer>(c);
_conns.insert(std::make_pair(c, discoverer));
}
auto &discoverers = _discoverers[method];
discoverers.insert(discoverer);
}
discoverer->appendMethod(method);
return discoverer;
}
//发现者客户端断开连接时,找到发现者信息,删除关联数据
void delDiscoverer(const BaseConnection::ptr &c)
{
std::unique_lock<std::mutex> lock(_mutex);
auto it = _conns.find(c);
if (it == _conns.end()) {
//没有找到连接对应的发现者信息,代表客⼾端不是⼀个服务发现者
return;
}
for (auto &method : it->second->methods) {
auto discoverers = _discoverers[method];
discoverers.erase(it->second);
}
_conns.erase(it);
}
//当有一个新的服务提供者上线,则进行上线通知
void onlineNotify(const std::string &method,const Address &host)
{
return notify(method,host,ServiceOptype::SERVICE_ONLINE);
}
//当有一个服务提供者断开连接,则进行下线通知
void offlineNotify(const std::string &method,const Address &host)
{
return notify(method,host,ServiceOptype::SERVICE_OFFLINE);
}
private:
void notify(const std::string &method,const Address &host,ServiceOptype optype)
{
std::unique_lock<std::mutex> lock(_mutex);
auto it = _discoverers.find(method);
if (it == _discoverers.end()) {
//这代表这个服务当前没有发现者
return;
}
auto msg_req = MessageFactory::create<ServiceRequest>();
msg_req->setId(UUid::uuid());
msg_req->setMType(MType::REQ_SERVICE);
msg_req->SetMethod(method);
msg_req->setHost(host);
msg_req->setoptye(optype);
for (auto &discoverer : it->second) {
discoverer->conn->send(msg_req);
}
}
private:
std::mutex _mutex;
std::unordered_map<std::string,std::set<Discoverer::ptr>> _discoverers;
std::unordered_map<BaseConnection::ptr,Discoverer::ptr> _conns;
};
class PDManager{
public:
using ptr = std::shared_ptr<PDManager>;
PDManager():
_providers(std::make_shared<ProviderManager>()),
_discoverers(std::make_shared<DiscovererManager>())
{}
void onServiceRequest(const BaseConnection::ptr &conn,const ServiceRequest::ptr &msg)
{
//服务操作请求:服务注册/服务发现
ServiceOptype optype = msg->optype();
if(optype == ServiceOptype::SERVICE_REGISTRY)
{
//服务注册:
//1.新增服务提供者;2.进行服务上线的通知
ILOG("%s %d 注册服务 %s",msg->host().first.c_str(),msg->host().second,msg->method().c_str());
_providers->addProvider(conn,msg->host(),msg->method());
_discoverers->onlineNotify(msg->method(),msg->host());
return registryResponse(conn,msg);
}
else{
ELOG("收到服务操作请求,但是操作类型错误!");
return errorResponse(conn,msg);
}
}
void onConnShutdown(const BaseConnection::ptr &conn)
{
auto provider = _providers->getProvider(conn);
if (provider.get() != nullptr) {
ILOG("%s:%d 服务下线", provider->host.first.c_str(), provider->host.second);
for(auto &method : provider->methods)
{
_discoverers->offlineNotify(method,provider->host);
}
_providers->delProvider(conn);
}
_discoverers->delDiscoverer(conn);
}
private:
void errorResponse(const BaseConnection::ptr &conn,const ServiceRequest::ptr &msg)
{
auto msg_rsp = MessageFactory::create<ServiceResponse>();
msg_rsp->setId(msg->rid());
msg_rsp->setMType(MType::RSP_SERVICE);
msg_rsp->setRcode(RCode::RCODE_INVALID_OPTYPE);
msg_rsp->setoptye(ServiceOptype::SERVICE_UNKNOW);
conn->send(msg_rsp);
}
void registryResponse(const BaseConnection::ptr &conn, const ServiceRequest::ptr &msg) {
auto msg_rsp = MessageFactory::create<ServiceResponse>();
msg_rsp->setId(msg->rid());
msg_rsp->setMType(MType::RSP_SERVICE);
msg_rsp->setRcode(RCode::RCODE_OK);
msg_rsp->setoptye(ServiceOptype::SERVICE_REGISTRY);
conn->send(msg_rsp);
}
void discoveryResponse(const BaseConnection::ptr &conn,const ServiceRequest::ptr &msg)
{
auto msg_rsp = MessageFactory::create<ServiceResponse>();
msg_rsp->setId(msg->rid());
msg_rsp->setMType(MType::RSP_SERVICE);
msg_rsp->setoptye(ServiceOptype::SERVICE_DISCOVERY);
std::vector<Address> hosts = _providers->methodHosts(msg->method());
if(hosts.empty())
{
msg_rsp->setRcode(RCode::RCODE_NOT_FOUND_SERVICE);
return conn->send(msg_rsp);
}
msg_rsp->setRcode(RCode::RCODE_OK);
msg_rsp->SetMethod(msg->method());
msg_rsp->setHost(hosts);
return conn->send(msg_rsp);
}
private:
ProviderManager::ptr _providers;
DiscovererManager::ptr _discoverers;
};
};
}
客户端Requestor实现
提供发送请求的接⼝
内部进⾏请求&响应的管理
#pragma once
#include"../common/net.hpp"
#include"../common/message.hpp"
#include<future>
#include<functional>
namespace myrpc{
namespace client{
class Requestor{
public:
using ptr = std::shared_ptr<Requestor>;
using RequestCallback = std::function<void(const BaseMessage::ptr&)>;
using AsyncResponse = std::future<BaseMessage::ptr>;
struct RequestDescribe{
using ptr = std::shared_ptr<RequestDescribe>;
BaseMessage::ptr request;
RType rtype;
std::promise<BaseMessage::ptr> response;
RequestCallback callback;
};
void onResponse(const BaseConnection::ptr &conn,BaseMessage::ptr &msg)
{
std::string rid = msg->rid();
RequestDescribe::ptr rdp = getDescribe(rid);
if(rdp.get() == nullptr)
{
ELOG("受到相应 - %s,但是未找到对应的请求描述!",rid.c_str());
return;
}
if (rdp->rtype == RType::REQ_ASYNC) {
rdp->response.set_value(msg);
}else if (rdp->rtype == RType::REQ_CALLBACK){
if (rdp->callback) rdp->callback(msg);
}else {
ELOG("请求类型未知!");
}
delDescribe(rid);
}
bool send(const BaseConnection::ptr &conn,
const BaseMessage::ptr &req, AsyncResponse &async_rsp) {
RequestDescribe::ptr rdp = newDescribe(req, RType::REQ_ASYNC);
if (rdp.get() == nullptr) {
ELOG("构造请求描述对象失败!");
return false;
}
conn->send(req);
async_rsp = rdp->response.get_future();
return true;
}
bool send(const BaseConnection::ptr &conn,
const BaseMessage::ptr &req, BaseMessage::ptr &rsp) {
AsyncResponse rsp_future;
bool ret = send(conn, req, rsp_future);
if (ret == false) {
return false;
}
rsp = rsp_future.get();
return true;
}
bool send(const BaseConnection::ptr &conn,
const BaseMessage::ptr &req, const RequestCallback &cb) {
RequestDescribe::ptr rdp = newDescribe(req, RType::REQ_CALLBACK, cb);
if (rdp.get() == nullptr) {
ELOG("构造请求描述对象失败!");
return false;
}
conn->send(req);
return true;
}
private:
RequestDescribe::ptr newDescribe(const BaseMessage::ptr &req,RType rtype,const RequestCallback &cb = RequestCallback())
{
std::unique_lock<std::mutex> lock(_mutex);
RequestDescribe::ptr rd = std::make_shared<RequestDescribe>();
rd->request = req;
rd->rtype = rtype;
if(rtype == RType::REQ_CALLBACK && cb)
{
rd->callback = cb;
}
_request_desc.insert(std::make_pair(req->rid(),rd));
return rd;
}
RequestDescribe::ptr getDescribe(const std::string &rid)
{
std::unique_lock<std::mutex> lock(_mutex);
auto it = _request_desc.find(rid);
if(it == _request_desc.end())
{
return RequestDescribe::ptr();
}
return it->second;
}
void delDescribe(const std::string &rid)
{
std::unique_lock<std::mutex> lock(_mutex);
_request_desc.erase(rid);
}
private:
std::mutex _mutex;
std::unordered_map<std::string,RequestDescribe::ptr> _request_desc;
};
}
}
客户端RpcCaller实现
#pragma once
#include"requestor.hpp"
namespace myrpc{
namespace client{
class RpcCaller{
public:
using ptr = std::shared_ptr<RpcCaller>;
using JsonAsyncResponse = std::future<Json::Value>;
using JsonResponseCallback = std::function<void(const Json::Value&)>;
RpcCaller(const Requestor::ptr &Requestor)
:_requestor(Requestor)
{}
//requestor中的处理是针对BaseMessage进行处理的
//用于在rpcCaller中针对结果的处理是针对RpcResponse里边的result进行的
bool call(const BaseConnection::ptr &conn,const std::string &method,const Json::Value ¶ms,Json::Value &result)
{
DLOG("开始同步rpc调用...");
//1.组织请求
auto req_msg = MessageFactory::create<RpcRequest>();
req_msg->setId(UUid::uuid());
req_msg->setMType(MType::REQ_RPC);
req_msg->SetMethod(method);
req_msg->Setparams(params);
BaseMessage::ptr rsp_msg;
//2.发送请求
bool ret = _requestor->send(conn,std::dynamic_pointer_cast<BaseMessage>(req_msg),rsp_msg);
if (ret == false) {
ELOG("同步Rpc请求失败!");
return false;
}
DLOG("收到响应,进⾏解析,获取结果!");
//3.等待响应
auto rpc_rsp_msg = std::dynamic_pointer_cast<RpcResponse>(rsp_msg);
if (!rpc_rsp_msg) {
ELOG("rpc响应,向下类型转换失败!");
return false;
}
if (rpc_rsp_msg->rcode() != RCode::RCODE_OK) {
ELOG("rpc请求出错:%s", errReason(rpc_rsp_msg->rcode()));
return false;
}
result = rpc_rsp_msg->result();
DLOG("结果设置完毕");
return true;
}
bool call(const BaseConnection::ptr &conn,const std::string &method,const Json::Value ¶ms,JsonAsyncResponse &result)
{
//向服务器发送异步回调请求,设置回调函数,回调函数中会传入一个promise对象
//在回调函数中去掉promise设置数据
auto req_msg = MessageFactory::create<RpcRequest>();
req_msg->setId(UUid::uuid());
req_msg->setMType(MType::REQ_RPC);
req_msg->SetMethod(method);
req_msg->Setparams(params);
auto json_promise = std::make_shared<std::promise<Json::Value>>();
result = json_promise->get_future();
Requestor::RequestCallback cb = std::bind(&RpcCaller::Callback,this,json_promise,std::placeholders::_1);
bool ret = _requestor->send(conn,
std::dynamic_pointer_cast<BaseMessage>(req_msg), cb);
if (ret == false) {
ELOG("异步Rpc请求失败!");
return false;
}
return true;
}
bool call(const BaseConnection::ptr &conn, const std::string &method, const Json::Value ¶ms, const JsonResponseCallback &cb) {
auto req_msg = MessageFactory::create<RpcRequest>();
req_msg->setId(UUid::uuid());
req_msg->setMType(MType::REQ_RPC);
req_msg->SetMethod(method);
req_msg->Setparams(params);
Requestor::RequestCallback req_cb = std::bind(&RpcCaller::Callback1,
this, cb, std::placeholders::_1);
bool ret = _requestor->send(conn,
std::dynamic_pointer_cast<BaseMessage>(req_msg), req_cb);
if (ret == false) {
ELOG("回调Rpc请求失败!");
return false;
}
return true;
}
private:
void Callback1(const JsonResponseCallback &cb,const BaseMessage::ptr &msg)
{
auto rpc_rsp_msg = std::dynamic_pointer_cast<RpcResponse>(msg);
if(!rpc_rsp_msg)
{
ELOG("rpc响应,向下类型转换失败!");
return;
}
if(rpc_rsp_msg->rcode() != RCode::RCODE_OK)
{
ELOG("rpc回调请求出错: %s",errReason(rpc_rsp_msg->rcode()));
return;
}
cb(rpc_rsp_msg->result());
}
void Callback(std::shared_ptr<std::promise<Json::Value>> result,const BaseMessage::ptr &msg)
{
auto rpc_rsp_msg = std::dynamic_pointer_cast<RpcResponse>(msg);
if (!rpc_rsp_msg) {
ELOG("rpc响应,向下类型转换失败!");
return ;
}
if (rpc_rsp_msg->rcode() != RCode::RCODE_OK) {
ELOG("rpc异步请求出错:%s", errReason(rpc_rsp_msg->rcode()));
return ;
}
result->set_value(rpc_rsp_msg->result());
}
private:
Requestor::ptr _requestor;
};
}
}
客户端Publish&Subscribe实现
提供消息发布接⼝
提供主题操作接⼝
内部进⾏主题及订阅者的管理
#pragma once
#include"requestor.hpp"
#include<unordered_set>
namespace myrpc{
namespace client{
class TopicManager{
public:
using SubCallback = std::function<void(const std::string&, const std::string&)>;
using ptr = std::shared_ptr<TopicManager>;
TopicManager(const Requestor::ptr &requestor):
_requestor(requestor){}
bool create(const BaseConnection::ptr &conn,const std::string &key)
{
return commonRequest(conn,key,TopicOptype::TOPIC_CREATE);
}
bool remove(const BaseConnection::ptr &conn,const std::string &key)
{
return commonRequest(conn,key,TopicOptype::TOPIC_REMOVE);
}
bool subscribe(const BaseConnection::ptr &conn,const std::string &key,const SubCallback &cb)
{
addSubscribe(key,cb);
bool ret = commonRequest(conn, key, TopicOptype::TOPIC_SUBSCRIBE);
if (ret == false) {
delSubscribe(key);
return false;
}
return true;
}
bool cancel(const BaseConnection::ptr &conn,const std::string &key)
{
delSubscribe(key);
return commonRequest(conn,key,TopicOptype::TOPIC_CANCEL);
}
bool publish(const BaseConnection::ptr &conn,const std::string &key,const std::string &msg)
{
return commonRequest(conn,key,TopicOptype::TOPIC_PUBLISH,msg);
}
void onPublish(const BaseConnection::ptr &conn,const TopicRequest::ptr &msg)
{
//1.从消息中取出操作类型进行判断,是否是消息请求
auto type = msg->optype();
if (type != TopicOptype::TOPIC_PUBLISH) {
ELOG("收到了错误类型的主题操作!");
return ;
}
//2.取出消息主题名称以及消息内容
std::string topic_key = msg->topicKey();
std::string topic_msg = msg->topicMsg();
//3.通过主题名称,查找对应主题的回调处理函数,有在处理,无在报错
auto callback = getSubscribe(topic_key);
if (!callback) {
ELOG("收到了 %s 主题消息,但是该消息⽆主题处理回调!",
topic_key.c_str());
return ;
}
return callback(topic_key,topic_msg);
}
private:
void addSubscribe(const std::string &key,const SubCallback &cb)
{
std::unique_lock<std::mutex> lock(_mutex);
_topic_callbacks.insert(std::make_pair(key,cb));
}
void delSubscribe(const std::string &key)
{
std::unique_lock<std::mutex> lock(_mutex);
_topic_callbacks.erase(key);
}
const SubCallback getSubscribe(const std::string &key)
{
std::unique_lock<std::mutex> lock(_mutex);
auto it = _topic_callbacks.find(key);
if (it == _topic_callbacks.end()) {
return SubCallback();
}
return it->second;
}
bool commonRequest(const BaseConnection::ptr &conn,const std::string &key,TopicOptype type,const std::string &msg = "")
{
//1.构造请求对象,并填充数据
auto msg_req = MessageFactory::create<TopicRequest>();
msg_req->setId(UUid::uuid());
msg_req->setMType(MType::REQ_TOPIC);
msg_req->setoptye(type);
msg_req->setTopicKey(key);
if(type == TopicOptype::TOPIC_PUBLISH)
{
msg_req->setTopicMsg(msg);
}
//2.向服务端发送请求等待响应
BaseMessage::ptr msg_rsp;
bool ret = _requestor->send(conn,msg_req,msg_rsp);
if (ret == false) {
ELOG("主题操作请求失败!");
return false;
}
//3.判断请求处理是否成功
auto topic_rsp_msg = std::dynamic_pointer_cast<TopicResponse>(msg_rsp);
if (!topic_rsp_msg) {
ELOG("主题操作响应,向下类型转换失败!");
return false;
}
if(topic_rsp_msg->rcode() != RCode::RCODE_OK)
{
ELOG("主题操作请求出错:%s", errReason(topic_rsp_msg->rcode()));
return false;
}
return true;
}
private:
std::mutex _mutex;
std::unordered_map<std::string,SubCallback> _topic_callbacks;
Requestor::ptr _requestor;
};
}
}
客⼾端Registry&Discovery实现
提供服务发现接⼝
提供服务注册接⼝
提供服务操作(上线/下线)通知处理回调函数
内部进⾏发现的服务与主机信息管理
#pragma once
#include"requestor.hpp"
#include<unordered_set>
namespace myrpc{
namespace client{
class Provider{
public:
using ptr = std::shared_ptr<Provider>;
Provider(const Requestor::ptr &requestor) :
_requestor(requestor){}
bool registryMethod(const BaseConnection::ptr &conn,const std::string &method,const Address &host)
{
auto msg_req = MessageFactory::create<ServiceRequest>();
msg_req->setId(UUid::uuid());
msg_req->setMType(MType::REQ_SERVICE);
msg_req->SetMethod(method);
msg_req->setHost(host);
msg_req->setoptye(ServiceOptype::SERVICE_REGISTRY);
BaseMessage::ptr msg_rsp;
bool ret = _requestor->send(conn, msg_req, msg_rsp);
if (ret == false) {
ELOG("%s 服务注册失败!", method.c_str());
return false;
}
auto service_rsp = std::dynamic_pointer_cast<ServiceResponse>(msg_rsp);
if (service_rsp.get() == nullptr) {
ELOG("响应类型向下转换失败!");
return false;
}
if (service_rsp->rcode() != RCode::RCODE_OK) {
ELOG("服务注册失败,原因:%s",
errReason(service_rsp->rcode()).c_str());
return false;
}
return true;
}
private:
Requestor::ptr _requestor;
};
class MethodHost{
public:
using ptr = std::shared_ptr<MethodHost>;
MethodHost() : _idx(0){}
MethodHost(const std::vector<Address> &hosts):
_hosts(hosts.begin(), hosts.end()), _idx(0){}
void appendHost(const Address &host)
{
//中途收到了服务上线请求后被调用
std::unique_lock<std::mutex> lock(_mutex);
_hosts.push_back(host);
}
void removeHost(const Address &host)
{
//中途收到了服务下线请求后被调用
std::unique_lock<std::mutex> lock(_mutex);
for (auto it = _hosts.begin(); it != _hosts.end(); ++it) {
if (*it == host) {
_hosts.erase(it);
break;
}
}
}
Address chooseHost()
{
std::unique_lock<std::mutex> lock(_mutex);
size_t pos = _idx++ %_hosts.size();
return _hosts[pos];
}
bool empty()
{
std::unique_lock<std::mutex> lock(_mutex);
return _hosts.empty();
}
private:
std::mutex _mutex;
size_t _idx;
std::vector<Address> _hosts;
};
class Discoverer{
public:
using OfflineCallback = std::function<void(const Address&)>;
using ptr = std::shared_ptr<Discoverer>;
Discoverer(const Requestor::ptr &requestor, const OfflineCallback &cb) :
_requestor(requestor), _offline_callback(cb){}
bool serviceDiscovery(const BaseConnection::ptr &conn,const std::string &method,Address &host)
{
{//当前所保管的提供者信息存在,则直接返回地址
std::unique_lock<std::mutex> lock(_mutex);
auto it = _method_hosts.find(method);
if (it != _method_hosts.end()) {
if (it->second->empty() == false) {
host = it->second->chooseHost();
return true;
}
}
}
//当前服务的提供者为空
auto msg_req = MessageFactory::create<ServiceRequest>();
msg_req->setId(UUid::uuid());
msg_req->setMType(MType::REQ_SERVICE);
msg_req->SetMethod(method);
msg_req->setoptye(ServiceOptype::SERVICE_DISCOVERY);
BaseMessage::ptr msg_rsp;
bool ret = _requestor->send(conn, msg_req, msg_rsp);
if (ret == false) {
ELOG("服务发现失败!");
return false;
}
auto service_rsp =
std::dynamic_pointer_cast<ServiceResponse>(msg_rsp);
if (!service_rsp) {
ELOG("服务发现失败!响应类型转换失败!");
return false;
}
if (service_rsp->rcode() != RCode::RCODE_OK) {
ELOG("服务发现失败!%s", errReason(service_rsp->rcode()).c_str());
return false;
}
//能走到这里,代表当前是没有对应的服务提供主机的
std::unique_lock<std::mutex> lock(_mutex);
auto method_host = std::make_shared<MethodHost>(service_rsp->hosts());
if (method_host->empty()) {
ELOG("%s 服务发现失败!没有能够提供服务的主机!", method.c_str());
return false;
}
host = method_host->chooseHost();
_method_hosts[method] = method_host;
return true;
}
//这个接口是提供给Dispatcher模块进行服务上线下线请求处理的回调函数
void onServiceRequest(const BaseConnection::ptr &conn,const ServiceRequest::ptr &msg)
{
//1.判断是上线还是下线请求,如果都不是那就不用处理了
auto optype = msg->optype();
std::string method = msg->method();
std::unique_lock<std::mutex> lock(_mutex);
if(optype == ServiceOptype::SERVICE_ONLINE)
{
//2.上线请求:找到MethodHost,向其中新增一个主机地址
auto it = _method_hosts.find(method);
if (it == _method_hosts.end()) {
auto method_host = std::make_shared<MethodHost>();
method_host->appendHost(msg->host());
_method_hosts[method] = method_host;
}else {
it->second->appendHost(msg->host());
}
}
else if(optype == ServiceOptype::SERVICE_OFFLINE)
{
//3.下线请求:找到MethodHost,从其中删除一个主机地址
auto it = _method_hosts.find(method);
if (it == _method_hosts.end()) {
return;
}
it->second->removeHost(msg->host());
_offline_callback(msg->host());
}
}
private:
OfflineCallback _offline_callback;
std::mutex _mutex;
std::unordered_map<std::string,MethodHost::ptr> _method_hosts;
Requestor::ptr _requestor;
};
}
}
简单RPC调用
#include "../../client/rpc_client.hpp"
void callback(const Json::Value &result) {
ILOG("callback result: %d", result.asInt());
}
int main()
{
bitrpc::client::RpcClient client(false, "127.0.0.1", 9090);
Json::Value params, result;
params["num1"] = 11;
params["num2"] = 22;
bool ret = client.call("Add", params, result);
if (ret != false) {
ILOG("result: %d", result.asInt());
}
bitrpc::client::RpcCaller::JsonAsyncResponse res_future;
params["num1"] = 33;
params["num2"] = 44;
ret = client.call("Add", params, res_future);
if (ret != false) {
result = res_future.get();
ILOG("result: %d", result.asInt());
}
params["num1"] = 55;
params["num2"] = 66;
ret = client.call("Add", params, callback);
DLOG("--------\n");
std::this_thread::sleep_for(std::chrono::seconds(1));
return 0;
}