从零实现Json-Rpc框架】- 项目实现 - 服务端registrydiscovery实现
📢博客主页:https://blog.csdn.net/2301_779549673
📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson
📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
📢本文由 JohnKi 原创,首发于 CSDN🙉
📢未来很长,值得我们全力奔赴更美好的生活✨
文章目录
- 📢前言
- 🏳️🌈一、重新理一遍思路
- 1.1 首先,是最基本的三个前置块
- 1.2 抽象层子类的细分
- 1.3 dispatcher 分发转承中心
- 1.4 服务端
- 1.5 客户端
- 🏳️🌈二、服务端注册类实现
- 2.1 核心设计目标
- 2.2 成员变量
- 2.3 服务提供信息结构
- 2.4 关键方法
- 2.5 整体代码
- 🏳️🌈三、服务端发现实现
- 3.1 核心设计目标
- 3.2 关键成员变量
- 3.3 核心接口分析
- 3.4 整体代码
- 🏳️🌈四、服务端整合
- 4.1 核心设计目标
- 4.2 关键接口分析
- 整体代码
- 🏳️🌈四、服务端registry&discovery整体代码
- 👥总结
📢前言
前几篇文章中,笔者介绍了rpc
的原理和目的,也介绍了需要使用的部分第三方库
和我们所需实现的功能
现在我们着手项目实现篇章
,目前零碎接口 、 项目消息字段类型 和 抽象层的封装 都已经完成了
截至上一篇文章,我们已经完成了 rpc框架 的实现
接下来我们将对 服务端 的 registry & discovery 进行实现,让其能够更加便捷
🏳️🌈一、重新理一遍思路
说实话,到这里笔者的脑子已经不清醒了,每个模块在实现的时候都很明白,放在一起就特别的困惑,所以这里再回顾一遍这个 rpc框架 的整个逻辑
1.1 首先,是最基本的三个前置块
1.2 抽象层子类的细分
这部分至关重要,它将整个框架分开来,使得 客户端 与 服务端 能够凭借着 缓冲区、连接、协议 串联起来,真正实现通信,但此时,通信模式仅仅是 一对一。
所以就需要一个 dispatcher 作为中间的转承,使得多对多的模式实现
1.3 dispatcher 分发转承中心
dispatcher 的两个最大的功能
- 找到消息类型对应的业务处理函数,并调用
- 关联消息类型和消息处理方法,并存储
这里提示一下
- rcp 中的服务 service 是指这个框架所能提供的服务,也就是那些回调函数所整合的功能
- 而服务端,是指 各个具有处理各种消息功能的函数(主机) 的集合
1.4 服务端
首先是 rpc_route.hpp
这部分,通过模块化设计,实现了 RPC 服务端核心功能,包括服务注册、参数校验、请求路由和线程安全管理。
然后就是这篇要实现的 rcp_registry.hpp
,这段代码通过分层设计实现了服务注册与发现的核心功能
最后是一整个 服务端 逻辑,也就是 rcp_server.hpp
通过消息协议和 Dispatcher 实现松耦合交互,构建灵活可扩展的 RPC 框架
1.5 客户端
🏳️🌈二、服务端注册类实现
2.1 核心设计目标
该类用于管理分布式系统中的服务提供者(Provider),核心功能包括:
- 服务注册:新服务提供者上线时注册方法与连接信息。
- 服务发现:根据方法名查询可用的服务提供者地址。
- 生命周期管理:处理服务提供者的上下线,维护提供者-连接映射关系。
- 线程安全:通过互斥锁保护共享数据结构的并发访问。
2.2 成员变量
先来看看他的变量都有哪些
2.3 服务提供信息结构
我们所需要创建的是一个 服务提供者对象 ,他需要提供一些能够解决 来自客户端的请求 的方法
因为一个 服务提供者对象 是不仅仅只有一种方法能够提供的,所以我们可以通过一个 vector
来管理
所以就需要有
- 方法
- 连接
- 地址
// 服务提供者信息
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);}
};
2.4 关键方法
- addProvider() - 服务注册
void addProvider(const BaseConnection::ptr& c, const Address& h, const std::string& method)
功能:当服务提供者注册新方法时调用,将方法与连接关联。
流程:
- 查找或创建 Provider:通过连接 c 在 _conns 中查找现有 Provider,若不存在则创建并绑定连接。
- 更新方法列表:将方法 method 添加到 Provider 的方法列表,并将该 Provider 注入 _providers[method] 的集合。
- getProvider() - 查询连接对应 Provider
Provider::ptr getProvider(const BaseConnection::ptr& c)
功能:通过连接对象获取对应的 Provider。
特点:短时加锁,快速返回结果,降低锁竞争。
3. deleteProvider() - 服务下线
void deleteProvider(const BaseConnection::ptr& c)
功能:移除断开连接的服务提供者,清理相关资源。
流程:
- 移除方法关联:遍历 Provider 的方法,从 _providers 的对应集合中删除该 Provider。
- 删除连接记录:从 _conns 中移除连接。
4. methodHosts() - 查询方法提供者地址
std::vector<Address> methodHosts(const std::string& method)
功能:根据方法名获取所有可用提供者的地址。
逻辑:遍历 _providers[method] 集合,收集地址。
异常处理:方法不存在时记录日志并返回空列表。
2.5 整体代码
// 管理服务提供者(Provider),处理注册、注销,维护服务-提供者映射。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新增数据// 1. 获取该 mehod 的提供主机的 set,通过取地址得到// 2. 向 set 中插入 providerauto& 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 deleteProvider(const BaseConnection::ptr& c){std::unique_lock<std::mutex> lock(_mutex);auto it = _conns.find(c);if(it != _conns.end()){ auto& provider = it->second;// 遍历该提供者提供的服务,从 _providers 中删除该提供者for(auto& method : it->second->methods){auto& providers = _providers[method];providers.erase(provider);}} _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()){ELOG("methodHosts 找不到该方法的提供主机!");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; // 连接 → 提供者};
🏳️🌈三、服务端发现实现
3.1 核心设计目标
该类用于管理分布式系统中的服务发现者(Discoverer),核心功能包括:
- 服务订阅管理:记录客户端关注的服务方法。
- 上下线通知:向订阅者推送服务提供者的上下线事件。
- 生命周期维护:处理发现者的连接状态与资源清理。
- 线程安全:通过互斥锁保护共享数据结构的并发访问。
3.2 关键成员变量
3.3 核心接口分析
- addDiscoverer() - 订阅服务
Discoverer::ptr addDiscoverer(const BaseConnection::ptr& c, const std::string& method)
功能:为客户端新增服务订阅,记录方法名并关联连接。
流程:
- 查找或创建 Discoverer:通过连接 c 在 _conns 中查找现有发现者,若不存在则创建并绑定连接。
- 更新订阅列表:将方法 method 添加到发现者的订阅列表,并将该发现者注入 _discoverers[method] 的集合。
- deleteDiscoverer() - 取消订阅
void deleteDiscoverer(const BaseConnection::ptr& c)
功能:移除客户端连接及其订阅信息。
流程:
- 清理方法关联:遍历发现者的订阅方法,从 _discoverers 的对应集合中删除该发现者。
- 删除连接记录:从 _conns 中移除连接。
优点:锁覆盖完整操作,避免中间状态不一致。
- onlineNotify()/offlineNotify() - 上下线通知
void onlineNotify(const std::string& method, const Address& host)
void offlineNotify(const std::string& method, const Address &host)
功能:向所有订阅某方法的客户端推送服务提供者的上下线事件。
逻辑:调用私有 notify() 函数构造消息并广播。
3.4 整体代码
// 管理服务发现者(Discoverer),记录客户端关注的服务,处理上下线通知。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> lcok(_mutex);methods.emplace_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 deleteDiscoverer(const BaseConnection::ptr& c){std::unique_lock<std::mutex> lock(_mutex);auto it = _conns.find(c);if(it != _conns.end()){Discoverer::ptr discoverer = it->second;for(auto& method : it->second->methods){auto& discoverers = _discoverers[method];discoverers.erase(discoverer);}}_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, Address(), 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->setServiceOptype(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; // 连接 → 发现者};
🏳️🌈四、服务端整合
4.1 核心设计目标
该类作为服务协调中枢,核心功能包括:
- 请求分发:根据客户端请求类型(注册/发现)分派到对应管理器。
- 生命周期联动:服务提供者上下线时,同步更新发现者订阅状态。
- 响应封装:统一构建服务端响应消息,保障通信协议一致性。
4.2 关键接口分析
- onServiceRequest() - 请求入口
void onServiceRequest(const BaseConnection::ptr& conn, const ServiceRequest::ptr& msg)
功能:处理客户端服务请求,分为注册与发现两种逻辑分支。
流程:
- 注册分支:调用 _providers->addProvider() 注册服务提供者,并触发 _discoverers->onlineNotify() 通知订阅者。
- 发现分支:调用 _discoverers->addDiscoverer() 记录订阅关系,并返回当前服务地址列表。
- onConnShutdown() - 连接关闭处理
void onConnShutdown(const BaseConnection::ptr& conn)
功能:清理连接相关的服务提供者和发现者。
流程:
- 从 _providers 获取提供者,遍历其方法调用 _discoverers->offlineNotify() 下线服务
- 删除提供者和发现者记录。
优点:保证服务上下线状态的一致性。
整体代码
// 协调服务提供者与发现者,处理服务请求和连接事件
class ProviderDiscovererManager {
public:using ptr = std::shared_ptr<ProviderDiscovererManager>;ProviderDiscovererManager(): _providers(std::make_shared<ProviderManager>()),_discoverers(std::make_shared<DiscovererManager>()) {}// 处理客户端服务发现请求void onServiceRequest(const BaseConnection::ptr& conn,const ServiceRequest::ptr& msg) {// 服务操作请求:服务注册/服务发现ServiceOptype optype = msg->Serviceoptype();// 服务注册if (optype == ServiceOptype::SERVICE_REGISTRY) {ILOG("%s:%d 注册服务 %s", msg->host().first.c_str(),msg->host().second, msg->method().c_str());// 1. 新增服务提供者_providers->addProvider(conn, msg->host(), msg->method());// 2. 进行服务上线的通知_discoverers->onlineNotify(msg->method(), msg->host());// 3. 响应服务注册成功registryResponse(conn, msg);}// 服务发现:`else if (optype == ServiceOptype::SERVICE_DISCOVERY) {// 1. 新增服务发现者_discoverers->addDiscoverer(conn, msg->method());discoveryResponse(conn, msg);} else {ELOG("收到服务操作请求,但是操作类型错误!");errorResponse(conn, msg);}}// 服务提供者 断开连接void onConnShutdown(const BaseConnection::ptr& conn) {auto provider = _providers->getProvider(conn);if (provider.get() != nullptr) {for (auto& method : provider->methods) {_discoverers->offlineNotify(method, provider->host);}_providers->deleteProvider(conn);}_discoverers->deleteDiscoverer(conn);}private:// 响应服务注册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->setOptype(ServiceOptype::SERVICE_REGISTRY);conn->send(msg_rsp);}// 响应服务发现void discoveryResponse(const BaseConnection::ptr& conn,const ServiceRequest::ptr& msg) {auto msg_rsp = MessageFactory::create<ServiceResponse>();std::vector<Address> hosts = _providers->methodHosts(msg->method());msg_rsp->setId(msg->rid());msg_rsp->setMType(MType::RSP_SERVICE);if (hosts.empty()) {msg_rsp->setRcode(RCode::RCODE_NOT_FOUND_SERVICE);conn->send(msg_rsp);} else {msg_rsp->setRcode(RCode::RCODE_OK);msg_rsp->setOptype(ServiceOptype::SERVICE_REGISTRY);msg_rsp->setMethod(msg->method());msg_rsp->setHost(hosts);conn->send(msg_rsp);}}// 响应错误请求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->setOptype(ServiceOptype::SERVICE_UNKNOW);conn->send(msg_rsp);}private:ProviderManager::ptr _providers; // 提供者列表DiscovererManager::ptr _discoverers;
};
🏳️🌈四、服务端registry&discovery整体代码
#pragma once
#include "../common/net.hpp"
#include "../common/message.hpp"
#include <set>namespace rpc{namespace server{// 管理服务提供者(Provider),处理注册、注销,维护服务-提供者映射。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新增数据// 1. 获取该 mehod 的提供主机的 set,通过取地址得到// 2. 向 set 中插入 providerauto& 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 deleteProvider(const BaseConnection::ptr& c){std::unique_lock<std::mutex> lock(_mutex);auto it = _conns.find(c);if(it != _conns.end()){ auto& provider = it->second;// 遍历该提供者提供的服务,从 _providers 中删除该提供者for(auto& method : it->second->methods){auto& providers = _providers[method];providers.erase(provider);}} _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()){ELOG("methodHosts 找不到该方法的提供主机!");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; // 连接 → 提供者}; // 管理服务发现者(Discoverer),记录客户端关注的服务,处理上下线通知。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> lcok(_mutex);methods.emplace_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 deleteDiscoverer(const BaseConnection::ptr& c){std::unique_lock<std::mutex> lock(_mutex);auto it = _conns.find(c);if(it != _conns.end()){Discoverer::ptr discoverer = it->second;for(auto& method : it->second->methods){auto& discoverers = _discoverers[method];discoverers.erase(discoverer);}}_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, Address(), 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->setServiceOptype(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 ProviderDiscovererManager{public:using ptr = std::shared_ptr<ProviderDiscovererManager>;ProviderDiscovererManager(): _providers(std::make_shared<ProviderManager>()),_discoverers(std::make_shared<DiscovererManager>()){}// 处理客户端服务发现请求void onServiceRequest(const BaseConnection::ptr& conn, const ServiceRequest::ptr& msg){// 服务操作请求:服务注册/服务发现ServiceOptype optype = msg->Serviceoptype();// 服务注册if(optype == ServiceOptype::SERVICE_REGISTRY){ILOG("%s:%d 注册服务 %s", msg->host().first.c_str(), msg->host().second, msg->method().c_str());// 1. 新增服务提供者_providers->addProvider(conn, msg->host(), msg->method());// 2. 进行服务上线的通知_discoverers->onlineNotify(msg->method(), msg->host());// 3. 响应服务注册成功registryResponse(conn, msg);}// 服务发现:`else if(optype == ServiceOptype::SERVICE_DISCOVERY){// 1. 新增服务发现者_discoverers->addDiscoverer(conn, msg->method());discoveryResponse(conn, msg);}else{ELOG("收到服务操作请求,但是操作类型错误!");errorResponse(conn, msg);}}// 服务提供者 断开连接void onConnShutdown(const BaseConnection::ptr& conn){auto provider = _providers->getProvider(conn);if(provider.get() != nullptr){for(auto& method : provider->methods){_discoverers->offlineNotify(method, provider->host);}_providers->deleteProvider(conn);}_discoverers->deleteDiscoverer(conn);}private:// 响应服务注册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->setOptype(ServiceOptype::SERVICE_REGISTRY);conn->send(msg_rsp);}// 响应服务发现void discoveryResponse(const BaseConnection::ptr& conn, const ServiceRequest::ptr& msg){auto msg_rsp = MessageFactory::create<ServiceResponse>();std::vector<Address> hosts = _providers->methodHosts(msg->method());msg_rsp->setId(msg->rid());msg_rsp->setMType(MType::RSP_SERVICE);if(hosts.empty()){msg_rsp->setRcode(RCode::RCODE_NOT_FOUND_SERVICE);conn->send(msg_rsp);}else{msg_rsp->setRcode(RCode::RCODE_OK);msg_rsp->setOptype(ServiceOptype::SERVICE_REGISTRY);msg_rsp->setMethod(msg->method());msg_rsp->setHost(hosts);conn->send(msg_rsp);} }// 响应错误请求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->setOptype(ServiceOptype::SERVICE_UNKNOW);conn->send(msg_rsp);}private:ProviderManager::ptr _providers; // 提供者列表DiscovererManager::ptr _discoverers;};}
}
👥总结
本篇博文对 从零实现Json-Rpc框架】- 项目实现 - 服务端registry&discovery实现 做了一个较为详细的介绍,不知道对你有没有帮助呢
觉得博主写得还不错的三连支持下吧!会继续努力的~
相关文章:
从零实现Json-Rpc框架】- 项目实现 - 服务端registrydiscovery实现
📢博客主页:https://blog.csdn.net/2301_779549673 📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! &…...
自适应二值化与形态学变换在图像颜色识别与替换中的应用解析
目录 前言一、自适应二值化1.1 取均值 ADAPTIVE_THRESH_MEAN_C1.2 高斯加权求和 ADAPTIVE_THRESH_GAUSSIAN_C1.2.1 一维高斯分布1.2.2 二维高速分布1.2.3 二维高斯分布权重计算规则 1.2.3.1 用户设置了σ1.2.3.2 用户没有设置σ1.3 代码二、形态学变换2.1 核 2.2 腐蚀2.3 膨胀…...
JsonCpp 处理 JSON(现代 C++ 方案)(三)
第三部分:JsonCpp 处理 JSON(现代 C++ 方案) 📢 快速掌握 JSON!文章 + 视频双管齐下 🚀 如果你觉得阅读文章太慢,或者更喜欢 边看边学 的方式,不妨直接观看我录制的 JsonCpp 课程视频!🎬 视频里会用更直观的方式讲解 JsonCpp 的核心概念、实战技巧,并配有动手演…...
flutter 曲线学习 使用第三方插件实现左右滑动
flutter 曲线的使用 实现左右滑动 TemperatureChartPage() TemperatureChartPage2() – 不太完善 方法 ChartDrawPage import package:doluyo/dly_package/widget/dly_widget.dart; import package:fl_chart/fl_chart.dart; import package:flutter/material.dart; impor…...
【WRF工具】GIS4WRF详细介绍:配置 WPS/WRF
【WRF工具】GIS4WRF详细介绍 QGIS-GIS4WRF安装(Installation)安装 QGIS安装 GIS4WRF GIS4WRF 配置(Configuration)一、如何进入配置界面二、可配置内容1️⃣ 设置工作目录2️⃣ 与 WPS/WRF 集成3️⃣ 与 NCAR 数据档案集成 参考 GIS4WRF 是一个在 QGIS 中…...
【自用记录】本地关联GitHub以及遇到的问题
最近终于又想起GitHub,想上传代码和项目到仓库里。 由于很早之前有在本地连接过GitHub(但没怎么用),现在需要重新搞起(操作忘得差不多)。 在看教程实操的过程中遇到了一些小问题,遂记录一下。 前…...
小程序中跨页面组件共享数据的实现方法与对比
小程序中跨页面/组件共享数据的实现方法与对比 在小程序开发中,实现不同页面或组件之间的数据共享是常见需求。以下是几种主要实现方式的详细总结与对比分析: 一、常用数据共享方法 全局变量(getApp())、本地缓存(w…...
ngx_http_core_merge_srv_conf
定义在 src\http\ngx_http_core_module.c static char * ngx_http_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) {ngx_http_core_srv_conf_t *prev parent;ngx_http_core_srv_conf_t *conf child;ngx_str_t name;ngx_http_server_name_t…...
如何在中科方德llinux系统上离线安装salt-minion
1,我的系统是什么 国产操作系统 中科方德 NFSChina Server release 4.0.240701 (RTM4-G320) 2,首先准备好两个安装包 salt-minion-2015.8.8-2.el7.noarch.rpm和salt-2015.8.8-2.el7.noarch.rpm 后者这个是前者的依赖项。 所以先安装salt-2015.8.8-2.e…...
RAG系统实战:当检索为空时,如何实现生成模块的优雅降级(Fallback)?
目录 RAG系统实战:当检索为空时,如何实现生成模块的优雅降级(Fallback)? 一、为什么需要优雅降级(Fallback)? 二、常用的优雅降级策略 策略一:预设后备提示࿰…...
输电线路航空标志球:低空飞行的安全路标 / 恒峰智慧科技
在现代社会,随着航空业的快速发展,低空飞行活动日益频繁。为了确保飞行安全,避免飞机与高压电线等障碍物发生碰撞,输电线路航空标志球应运而生。这种装置被广泛应用于高压输电线路上,尤其是超高压和跨江输电线…...
【SPP】蓝牙 SDP 协议在SPP中的互操作性解析
在蓝牙通信体系中,服务发现协议(SDP, Service Discovery Protocol)扮演着 "服务目录" 的核心角色。对于串口通信协议(SPP, Serial Port Profile)而言,SDP 服务记录是设备间建立串口连接的基础&am…...
本地部署vanna ai+通过http请求调用vanna
本地部署vanna ai ① 准备python环境,推荐最新的python12、13版本 ② 安装vanna库 我这里安装的python环境是python312 进入目录python312/Scripts,在该目录下的命令行窗口中输入以下命令:pip jinstall vanna pip install vanna③ 配置向…...
seq2seq
理解 transformer 中的 encoder decoder 详细的 transformer 教程见:【极速版 – 大模型入门到进阶】Transformer 文章目录 🌊 Encoder: 给一排向量输出另外一排向量🌊 Encoder vs. Decoder: multi-head attention vs. masked multi-head at…...
C++ ---- 虚继承
一、什么是虚继承 虚继承就是子类中只有一份间接父类的数据。用于解决多继承中的父类为非虚继承时出现的二义性问题,即菱形继承问题。继承方式需要加上virtual关键字。 二、虚继承的特性 以菱形继承为例: 1.不使用虚继承 根据输出的大小和关系图&…...
COMSOL多层圆片随机堆积三维模型
构建多层圆片随机堆积三维模型可用于材料、化工、土木、生物医学等多领域的研究,如复合材料设计、催化剂载体、颗粒物堆积研究等。本案例介绍在COMSOL内建立三维圆片堆积模型。 三维圆片堆积模型可采用CAD纤维密堆积3D插件建立,参数设置如图所示&#…...
PHP 开发API接口签名验证
就安全来说,所有客户端和服务器端的通信内容应该都要通过加密通道(HTTPS)传输,明文的HTTP通道将会是man-in-the- middle及其各种变种攻击的温床。所谓man-in-the-middle攻击简单讲就是指恶意的黑客可以在客户端和服务器端的明文通信通道上做手 脚&#x…...
Web开发-JavaEE应用ORM框架SQL预编译JDBCMyBatisHibernateMaven
知识点: 0、安全开发-JavaEE-构建工具-Maven 1、安全开发-JavaEE-ORM框架-JDBC 2、安全开发-JavaEE-ORM框架-Mybatis 3、安全开发-JavaEE-ORM框架-Hibernate 4、安全开发-JavaEE-ORM框架-SQL注入&预编译 一、演示案例-WEB开发-JavaEE-构建工具-Maven IDEA配置m…...
软考-数据库系统工程师第四版pdf
软考-数据库系统工程师第四版pdf git中的文件相对没有那么清楚,网盘的有高清版 github下载 这里我给出仓库地址 链接: https://github.com/yaodada123/ruankao-pdf https://github.com/yaodada123/ruankao-pdf gitee下载 https://gitee.com/yao-hengchao/ruank…...
扫描仪+文档pdf编辑器+pdf格式转换器
小扫描仪是一款集“扫描仪文档pdf编辑器pdf格式转换器”于一体的多功能扫描软件,软件功能丰富,而且目前是免费,功能包括扫描、编辑、转换三部分。 扫描:扫描的功能包括文档扫描、身份证扫描、护照扫描、书籍扫描、OCR和二维码。 扫…...
【stm32--HAL库DMA+USART+空闲中断不定长收发数据】
串口通信-Hal库实现不定长度收发,DMAUSART DMA串口STM32CUBEMX配置(工程创建)基础配置时钟配置工程配置 代码编写现象 DMA 在正式配置之前,我们先来一起简单了解一下DMA。DMA(Direct Memory Access,直接内…...
5G-A技术
最近的iOS 18.4 推送了 新功能,最引人注目的便是这个5G-A的这个功能,那什么是5G-A呢 ? 目前北京 四环内 还是有能显示出5G-A标志的。 5G-A 🌐 一句话概括: 5G-A 更快的速度 更低的延迟 更强的AI能力 更智能的网…...
Vue 组件 - 动态组件
Vue 渐进式JavaScript 框架 基于Vue2的学习笔记 - Vue 组件 - 动态组件 目录 动态组件 选项卡页面示例 更简单写法 增加输入框 弥补措施 总结 动态组件 选项卡页面示例 功能:选项卡功能,设置导航点击哪个显示相应页面。 设置三个全局组件&#…...
ffmpeg滤镜使用
ffmpeg实现画中画效果 FFmpeg中,可以通过overlay将多个视频流、多个多媒体采集设备、多个视频文件合并到一个界面中,生成画中画的效果 FFmpeg 滤镜 overlay 基本参数 x和y x坐标和Y坐标 eof action 遇到 eof表示时的处理方式,默认为重复。…...
【MVC简介-产生原因、演变历史、核心思想、组成部分、使用场景】
MVC简介 产生原因: MVC(Model-View-Controller)模式诞生于20世纪70年代,由Trygve Reenskaug在施乐帕克研究中心(Xerox PARC)为Smalltalk语言设计,目的是解决图形用户界面(GUI&…...
基于大模型的房间隔缺损手术全流程预测与方案优化研究报告
目录 一、引言 1.1 研究背景与意义 1.2 研究目的与目标 1.3 研究方法与创新点 二、房间隔缺损概述 2.1 房间隔缺损定义与分类 2.2 发病机制与病理生理 2.3 流行病学特征 三、大模型在房间隔缺损预测中的应用原理 3.1 大模型技术简介 3.2 数据收集与预处理 3.3 模型…...
什么是 CSSD?
文章目录 一、什么是 CSSD?CSSD 的职责 二、CSSD 是如何工作的?三、CSSD 为什么会重启节点?情况一:网络和存储都断联(失联)情况二:收到其他节点对自己的踢出通知(外部 fencing&#…...
uniapp APP端在线升级(简版)
设计思路: 1.版本比较:应用程序检查其当前版本与远程服务器上可用的最新版本 2. 更新状态指示:如果应用程序是不是最新的版本,则页面提示下载最新版本。 3.下载启动:通过plus.downloader.createDownload()启动新应用…...
2024年蓝桥杯Java B组省赛真题超详解析-分布式队列
问题:你需要回答在某个时刻,队列中有多少个元素具有可见性 方案:跟踪每个副节点已经同步到主节点队列的元素数量,并找出所有副节点中同步到的最少元素数量,这个数量即为所有副节点都已经同步的元素数量。 解析&#…...
Vue3入门
环境准备: node.js vscode or webstorm 哪个熟悉用哪个 这两个都是傻瓜式安装 浏览器直接搜索 下载即可 安装: 安装完node.js之后 按住快捷键 winR 打开命令提示符输入node 将显示版本信息 接着我们通过 vite 构建vue3工程 优点: 轻量快速的热重载(HMR…...
向量库(Vector Database)概述
向量库(Vector Database)概述 1. 核心概念 向量 高维空间中的数值数组,通常由模型(如BERT、ResNet)将非结构化数据(文本、图像等)转换为嵌入向量。 向量相似性 衡量方法:余弦相…...
Oracle迁移达梦遇中断?试试SQLark的断点续迁功能!
在企业级数据迁移项目中,如果迁移单表数据量超过亿行、占用空间超过100GB时,一旦遇到网络中断或迁移报错,往往需要整表重新迁移,导致效率低下,严重影响项目进度。针对这一痛点,SQLark 支持对 Oracle→DM 的…...
上海某海外视频平台Android高级工程师视频一面
问的问题比较细,有很多小细节在里面,平时真不一定会注意到,做一个备忘: 1.Object类里面有哪些方法? Object 类是 Java 中所有类的根类,它定义了一些基本方法,供所有类继承和重写1. 常用方法 1…...
基于yolov11的汽车损伤检测系统python源码+onnx模型+评估指标曲线+精美GUI界面
【算法介绍】 基于YOLOv11的汽车损伤检测系统是一种先进的计算机视觉技术,旨在快速准确地识别汽车的各种损伤类型。该系统利用YOLOv11模型的强大性能,实现了对车辆损伤的精确检测与分类。 该系统能够识别的损伤类型包括裂纹(crackÿ…...
华为IP(3)
DHCP Relay报文格式 DHCP Relay主要负责转发DHCP客户端与DHCP服务器之间的DHCP报文,所以DHCP Relay的报文格式只是把DHCP的报文部分字段做了相应的修改,报文格式没有发生变化 hops:表示当前DHCP报文经过DHCP中继的数目,该字段由…...
面试问题总结:qt工程师/c++工程师
C 语言相关问题答案 面试问题总结:qt工程师/c工程师 C 语言相关问题答案 目录基础语法与特性内存管理预处理与编译 C 相关问题答案面向对象编程模板与泛型编程STL 标准模板库 Qt 相关问题答案Qt 基础与信号槽机制Qt 界面设计与布局管理Qt 多线程与并发编程 目录 基础…...
【TS学习】(15)分布式条件特性
在 TypeScript 中,分布式条件类型(Distributive Conditional Types) 是一种特殊的行为,发生在条件类型作用于裸类型参数(Naked Type Parameter) 时。这种特性使得条件类型可以“分布”到联合类型的每个成员…...
四款高效数据报表工具 让数据分析更简单
概述 在数字化时代,企业和组织越来越依赖数据驱动决策,报表软件成为提高数据可视化能力、优化业务管理的关键工具。本文将为大家介绍四款功能强大的报表软件,帮助不同需求的企业找到合适的解决方案。 一、山海鲸报表 山海鲸报表是一款零代…...
QT 非空指针 软件奔溃
在用QT的实际项目中,出现如下现象: 运行软件再关闭软件,然后再运行软件会崩溃。等待5~10分钟,再运行软件,又正常,百思不得其解,后面找到原因是在头文件里定义指针变量时没有赋初nullptr&#x…...
图漾相机——C#语言属性设置
文章目录 前言1.示例程序说明2.SDK API功能介绍2.1 ListDevice2.2 Open2.3 OpenDeviceByIP2.4 Close2.5 DeviceStreamEnable2.6 DeviceStreamFormatDump2.7 DeviceStreamFormatConfig2.8 DeviceReadCurrentEnumData2.9 DeviceReadCalibData2.10 DeviceStreamOn2.11 DeviceStrea…...
WPF中viewmodel单例模式
1、单例模式介绍 单例模式是一种创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。它常用于需要全局唯一访问点的场景,如配置管理、日志记录、数据库连接等。 2、WPF 中 ViewModel 的单例实现 在 WPF 中&#…...
AI比人脑更强,因为被植入思维模型【36】时光机理论思维
giszz的理解:据说是软银孙正义提出的一种思维模型,他利用同一时间内的地区差,通过引入技术、思维,在同一地区,形成了时间差。所谓商业模式,有时就是打空间差、时间差,信息差。 一、定义 时光机…...
SQL Server:用户权限
创建 & 删除 1. 创建用户命令整理 创建 admin2 用户 -- 在 master 数据库创建登录名 USE master; BEGINCREATE LOGIN [admin2] WITH PASSWORDNCljslrl0620!, DEFAULT_DATABASE[master], CHECK_EXPIRATIONOFF, CHECK_POLICYON; END;-- 在 db03 数据库创建用户并添加到相应…...
Qt之QTextEdit控制文本滚动, 停止滚动, 开始滚动, 鼠标控制滚动
对工作台文本框进行控制。含以下内容。详细说明在源码中可查看 至最底部停止滚动开始滚动 源码分两部分. .h文件和.cpp文件 MyTextEdit.h #ifndef MYTEXTEDIT_H #define MYTEXTEDIT_H#include <QObject> #include <QTextEdit> #include <QScrollBar> #includ…...
策略模式与元数据映射模式融合 JSR 380 验证规范实现枚举范围校验
类文件 Target({ElementType.METHOD,ElementType.FIELD,ElementType.ANNOTATION_TYPE,ElementType.CONSTRUCTOR,ElementType.PARAMETER,ElementType.TYPE_USE }) Retention(RetentionPolicy.RUNTIME) Documented Constraint(validatedBy {InEnumValidator.class, InEnumColle…...
9对象树(3)
目录 创建自定义的类,最主要的目的,是自定义一个析构函数,在析构函数中,完成打印.方便咱们看到最终的自动销毁对象的效果!!! 写完一个函数的声名之后, 按下 altenter, 在按下enter就可以自动的在对应的 cpp 文件中添加函数的定义了 内置类型,析构不会明…...
深入 OpenPDF:高级 PDF 生成与操作技巧
1 引言 1.1 项目背景 在许多企业级应用中,生成和操作 PDF 文档是一个常见的需求。PDF(Portable Document Format)因其格式统一、易于打印和分发而被广泛使用。本文将介绍如何使用 OpenPDF 库在 Java 项目中生成和操作 PDF 文档。 1.2 技术选型理由 OpenPDF:OpenPDF 是一…...
电脑屏幕亮度随心控,在Windows上自由调整屏幕亮度的方法
调整电脑屏幕的亮度对于保护视力和适应不同环境光线条件非常重要。无论是在白天强光下还是夜晚昏暗环境中,合适的屏幕亮度都能让您的眼睛更加舒适。本文中简鹿办公小编将向您介绍几种在 Windows 系统中调整屏幕亮度的方法。 方法一:使用快捷键 大多数笔…...
Navicat导出mysql数据库表结构说明到excel、word,单表导出方式记录
目前只找到一张一张表导出的方式 使用information_schema传入表名查询 字段名根据需要自行删减,一般保留序号、字段名、类型、说明就行 SELECT COLUMNS.ORDINAL_POSITION AS 序号, COLUMNS.COLUMN_NAME AS 字段名, COLUMNS.COLUMN_TYPE AS 类型(长度), COLUMNS.N…...
【C++笔记】C++常见二叉树OJ和拓扑排序
【C笔记】C常见二叉树OJ和拓扑排序 🔥个人主页:大白的编程日记 🔥专栏:C笔记 文章目录 【C笔记】C常见二叉树OJ和拓扑排序前言一.二叉树OJ1.1 根据二叉树创建字符串1.2 二叉树的层序遍历1.3 二叉树的最近公共祖先1.4 将二叉搜索…...