bprc二次封装

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 一、封装的思想
  • 二、封装单个服务的信道管理类
    • 1.成员变量
    • 2.成员函数
  • 三、封装总体的服务信道管理类
    • 1.成员变量
    • 2.成员函数
  • 四.etcd和brpc联合测试
    • 1.服务注册客户端
    • 2.服务发现客户端


一、封装的思想

brpc的二次封装:
brpc本质上来说–进行rpc调用的,但是向谁调用什么服务得管理起来 – 搭配etcd实现的注册中心管理
原因:通过注册中心,能够获知谁能提供什么服务,进而能够连接它发起这个服务调用。
封装思想:
主要是管理起来网络通信的信道----将不同服务节点主机的通信信道管理起来
封装的是服务节点信道的管理,而不是rpc调用的管理。
封装:
1.指定服务的信道管理类:
一个服务可能会有多个节点提供服务,每个节点都有自己的channe
建立服务与信道的映射关系,并且关系是一对多,采用RR轮转策略进行获取
2.总体的服务信道管理类
将多个服务的信道管理对象管理起来

二、封装单个服务的信道管理类

1.成员变量

1.需要一个string来表视当前服务的信道管理类的服务名称因为我们是把一个服务的信道管理起来,所以需要知道该服务的名称。
2.一个服务可能会有多个主机,也就是一个服务可能会有多个信道。所以我么需要一个数组来存储当前服务节点的信道。
3.当一个节点主机下线后,我们需要删除释放这个channel,所以我们需要一个哈希表来进行主机地址与channel的映射关系,方便我们进行删除。
4.需要一个rr轮转下标,当需要对该服务进行rpc调用时,可以使用rr轮转负载均衡式的返回channel.
5.需要一个互斥锁,保证容器的线程安全。

 std::mutex _mutex;int32_t _index; //rr轮转下标std::string _service_name;  //服务名称std::vector<ChannelPtr> _channels;  //当前服务对应的通信集合std::unordered_map<std::string,ChannelPtr> _hosts;  //主机地址与信道映射关系

2.成员函数

构造函数中,只需要初始化服务名称,然后轮转下标初始化为0.

ServiceChannel(const std::string& service_name):_index(0),_service_name(service_name){}

当服务上线一台主机时,需要创建一个channel进行管理,那么该函数中需要有一个参数,就是主机的地址。

void append(const std::string& host){//创建一个channel,并进行连接ChannelPtr channel = std::make_shared<brpc::Channel>();brpc::ChannelOptions options;options.connect_timeout_ms = -1;options.timeout_ms = -1;options.max_retry = 3;options.protocol = "baidu_std";int ret = channel->Init(host.c_str(), &options);if (ret == -1) {LOG_ERROR("初始化{}-{}信道失败!", _service_name, host);return;}std::unique_lock<std::mutex> lock(_mutex);_hosts.insert(std::make_pair(host, channel));_channels.push_back(channel);}

对应的,当主机下线时,需要将该主机的channel删除并释放

 void remove(const std::string& host){std::unique_lock<std::mutex> lock(_mutex);auto it = _hosts.find(host);if (it == _hosts.end()) {LOG_WARN("{}-{}节点删除信道时,没有找到信道信息!", _service_name, host);return;}for (auto vit = _channels.begin(); vit != _channels.end(); ++vit) {if (*vit == it->second) {_channels.erase(vit);break;}}_hosts.erase(it);}

当需要对该服务发起rpc调用时,我们可以返回该服务的channel。通过rr轮转方式实现一个负载均衡。

ChannelPtr choose(){std::unique_lock<std::mutex> lock(_mutex);if (_channels.size() == 0) {LOG_ERROR("当前没有能够提供 {} 服务的节点!", _service_name);return ChannelPtr();}int32_t idx = _index++ % _channels.size();return _channels[idx];}

三、封装总体的服务信道管理类

我们的服务有多个,而每一个服务也会有多个主机节点。因此我们需要把这些服务总的管理起来。

1.成员变量

1.需要一个哈希表来实现服务名称和该服务的信道管理类的映射关系。
2.项目有多个服务,而我们并不是对所有的服务都要关心的。我们需要使用一个unordered_set来记录我们关心的服务。
3.一个互斥锁,保证容器的线程安全。

2.成员函数

添加一个关心的服务,只需要把服务名称传递进来,进行一个插入即可

 void declared(const std::string& service_name){std::unique_lock<std::mutex> lock(_mutex);_follow_services.insert(service_name);}

当一个对应的服务上线一个主机节点时,为该服务节点主机创建一个channel,并管理起来。这里直接调用上面的服务的信道管理类就行。

void onServiceOnline(const std::string &service_instance,const std::string& host){std::string service_name = getServiceName(service_instance);ServiceChannel::ptr service;{std::unique_lock<std::mutex> lock(_mutex);auto fit = _follow_services.find(service_name);if (fit == _follow_services.end()) {LOG_DEBUG("{}-{} 服务上线了,但是当前并不关心!", service_name, host);return;}//先获取管理对象,没有则创建,有则添加节点auto sit = _services.find(service_name);if (sit == _services.end()) {service = std::make_shared<ServiceChannel>(service_name);_services.insert(std::make_pair(service_name, service));}else {service = sit->second;}}if (!service) {LOG_ERROR("新增 {} 服务管理节点失败!", service_name);return ;}service->append(host);LOG_DEBUG("{}-{} 服务上线新节点,进行添加管理!", service_name, host);}

服务主机节点下线时,需要找到对应服务的信道管理类,来进行一个对应主机节点的channel的删除和释放。

void onServiceOffline(const std::string &service_instance,const std::string& host){std::string service_name = getServiceName(service_instance);ServiceChannel::ptr service;{std::unique_lock<std::mutex> lock(_mutex);auto fit = _follow_services.find(service_name);if (fit == _follow_services.end()) {LOG_DEBUG("{}-{} 服务下线了,但是当前并不关心!", service_name, host);return;}//先获取管理对象,没有则创建,有则添加节点auto sit = _services.find(service_name);if (sit == _services.end()) {LOG_WARN("删除{}服务节点时,没有找到管理对象", service_name);return;}service = sit->second;}service->remove(host);LOG_DEBUG("{}-{} 服务下线节点,进行删除管理!", service_name, host);}

上面的两个接口,是当服务的主机节点上线和下线时调用的,形参中的两个参数就是服务名称和主机节点。
那么什么时候会调用这两个函数呢?我们怎么知道什么时候主机上线下线呢?在前面我们封装了一个etcd,在服务发现客户端中,我们是同watcher来监控一个服务。当服务的数据发送改变时,就会调用我们传入的两个回调函数,那么这里的两个函数就可以作为俩个回调函数传入。也就是当服务新增节点和删除节点时调用。

获取指定服务的节点信道,传入指定服务,返回一个服务的信道。

//获取指定服务的节点信道ServiceChannel::ChannelPtr choose(const std::string& service_name){std::unique_lock<std::mutex> lock(_mutex);auto sit = _services.find(service_name);if (sit == _services.end()) {LOG_ERROR("当前没有能够提供 {} 服务的节点!", service_name);return ServiceChannel::ChannelPtr();}return sit->second->choose();}

四.etcd和brpc联合测试

前面我们封装了etcd,有一个服务注册客户端和一个服务发现客户端。那么我们可以用刚刚封装的brpc和etcd进行一个连接测试。

我们是可以用brpc进行rpc调用的,但是向谁进行rpc调用呢?我们可以使用服务发现客户端对服务进行一个监控,当服务注册客户端注册一个服务后,服务发现客户端就会触发一个回调函数,在这个回调函数就会对触发的事件进行一个判断,如果是新增事件,则调用用户设置的put_cb,如果是删除事件,则调用用户的del_cb;那么我们是不是可以把我们刚刚封装的bprc中的onServiceOnline和onServiceOffline作为对应的回调函数设置进去呢?

大体思路:

  • 服务注册客户端这边,首先创建一个brpc服务器,然后新增EchoService服务,并启动服务器。最后向etcd中注册一个/service/echo/instance服务,并指明自己的主机地址,表明自己的主机可以提供echo服务。
  • 服务发现客户端这边,首先创建一个总的信道管理类对象,然后构造一个服务发现对象,对/service/echo服务进行监控。接着通过总的信道管理类对象获取到一个channel,通过这个channel,就可以创建stub进行服务调用。

1.服务注册客户端

首先创建一个服务器对象,因为我们要提供rpc服务。

    brpc::Server server;

接着新增服务。

//3.向服务器对象中,新增EchoService服务EchoServiceImpl echo_service;int ret = server.AddService(&echo_service,brpc::ServiceOwnership::SERVER_DOESNT_OWN_SERVICE);if (ret == -1) {std::cout << "添加Rpc服务失败!\n";return -1;}

启动服务器,监听8085端口。

    brpc::ServerOptions options;options.idle_timeout_sec = -1;options.num_threads = 1;ret = server.Start(8085,&options);if (ret == -1) {std::cout << "启动服务器失败!\n";return -1;}

向etcd注册中心注册一个/service/echo/instance服务,设置val为自己的主机地址。代表当前主机可以提供echo服务。
此时etcd注册中心就会有一个{key:/service/echo/instance ; val:127.0.0.1:8085};

//5.注册服务Registry::ptr rClient = std::make_shared<Registry>("127.0.0.1:2379");rClient->registry("/service/echo/instance","127.0.0.1:8085");

2.服务发现客户端

先构建以恶个总的信道管理对象,需要通过这个对象来"知道"哪个主机可以提供服务。但具体是怎么发现的呢?可以看到,我们调用了declared这个函数代表我们关心/service/echo这个服务。此时这个字符串就会被插入到一个unordered_set中。其次我们将这个对象中的两个成员函数获取到了。这两个函数后续我们要设置进服务发现客户端中。

 //1.先构建Rpc信道管理对象auto sm = std::make_shared<ServiceManager>();sm->declared("/service/echo");auto put_cb = std::bind(&ServiceManager::onServiceOnline,sm.get(),std::placeholders::_1,std::placeholders::_2);auto del_cb = std::bind(&ServiceManager::onServiceOffline,sm.get(),std::placeholders::_1,std::placeholders::_2);

构造服务发现对象,这里的构造函数我们传入了四个参数,第一个参数是etcd注册中心的地址,因为我们要和etcd通信.
第二个参数是我们要监控的服务。我们的服务发现对象中有一个成员watcher,他会对指定的服务进行监控,如服务数据有变化,就会进行处理。而这里的第二个参数就是watcher进行监控的服务。另外这个监控它是会进行递归的,/service/下的所有目录发现变化,他都可以感知到。第三个第四个函数就是我们设置进的回调函数,当watcher感知到服务发送变化,会获取到发送变化的服务的key和val,也就是服务名称和主机地址。这是就可以调用我们设置的两个回调函数,我们的回调函数刚好可以接受两个参数。

而这两个参数一个是/service/echo/instance,一个是127.0.0.1:8085。我们的服务信道管理类是管理 服务的信道的。我们服务发现客户端关心的是/service/echo服务。而这里的/service/echo/instance本质上是一个/service/echo服务。所以在onServiceOnline和onServiceOffline中我们对这个参数进行了一个劫取子串,只需要/service/echo这一部分。然后为这一部分创建一个服务信道管理类对象。

//2. 构造服务发现对象Discovery::ptr dclient = std::make_shared<Discovery>("127.0.0.1:2379","/service", put_cb, del_cb);

接着就是获取服务的channel,我们关心哪个服务就获取什么服务的channel.

 //3. 通过Rpc信道管理对象,获取提供Echo服务的信道ServiceChannel::ChannelPtr channel = sm->choose("/service/echo");if(channel == nullptr){return -1;}

通过channel来发起rpc调用

//4.发起rpc调用example::EchoService_Stub stub(channel.get());example::EchoRequest req;req.set_message("你好,Lkm");brpc::Controller *cntl = new brpc::Controller();example::EchoResponse *rsp = new example::EchoResponse();stub.Echo(cntl, &req, rsp, nullptr);if(cntl->Failed() == true){std::cout << "Rpc调用失败:" << cntl->ErrorText() << std::endl;return;}std::cout << "收到响应: " << rsp->message() << std::endl;std::this_thread::sleep_for(std::chrono::seconds(1));

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/879733.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

透明屏幕有普通屏幕有哪些优点

针对透明玻璃屏幕的安装方案&#xff0c;我们需要综合考虑多个因素&#xff0c;包括安装环境、屏幕尺寸、重量、安全要求以及视觉效果等。以下是一个概括性的安装方案框架&#xff0c;供您参考&#xff1a; 一、前期准备 1.1 需求分析 明确透明玻璃屏幕的使用场景&#xff08…

C++速通LeetCode中等第4题-三数之和

解题思路&#xff1a;先排序&#xff0c;固定第一个数&#xff0c;用两个指针分别指向右侧剩余数列的两端&#xff0c;右侧向左移动直到两指针重合&#xff0c;看三数合有没有解&#xff0c;指针遇到相同数字跳过。 class Solution { public:vector<vector<int>> …

【数据库】MySQL内置函数

本篇分享一些在MySQL中常见的一些内置函数&#xff0c;如日期函数&#xff0c;字符串函数和数学函数&#xff0c;以方便于操作数据库中的数据。 1.日期函数 我们先整体观察一下这些函数再讲解案例 日期函数使用起来都非常就简单 获得年月日&#xff1a; select current_dat…

为解决bypy大文件上传报错—获取百度云文件直链并使用Aria2上传文件至服务器

问题描述 一方面组内的服务器的带宽比较小&#xff0c;另一方面使用bypy方式进行大文件(大于15G)上传时会报错&#xff08;虽然有时可以成功上传&#xff0c;但是不稳定&#xff09;&#xff1a; 解决方式 总体思路: 获得云盘需要下载文件的直链复制直链到服务器中使用自带…

24年蓝桥杯及攻防世界赛题-MISC-3

21 reverseMe 复制图片&#xff0c;在线ocr识别&#xff0c;https://ocr.wdku.net/&#xff0c;都不费眼睛。 22 misc_pic_again ┌──(holyeyes㉿kali2023)-[~/Misc/tool-misc/zsteg] └─$ zsteg misc_pic_again.png imagedata … text: “$$KaTeX parse error: Undefined…

Excel快速填充颜色,快捷键真香

大家好&#xff0c;这里是效率办公指南&#xff01; &#x1f3a8; 在Excel中工作时&#xff0c;我们经常需要对单元格进行颜色填充&#xff0c;以突出显示重要数据或增加视觉可读性。今天&#xff0c;我们将分享几种快速填充颜色的方法&#xff0c;帮助你提高工作效率&#x…

Golang使用ReverseProxy实现反向代理

目录 1.源码结构体 2.官方单机示例 3.使用示例 4.简单的http服务&#xff08;用于测试&#xff09; 1.源码结构体 type ReverseProxy struct {// Rewrite 必须是一个函数&#xff0c;用于将请求修改为要使用 Transport 发送的新请求。然后&#xff0c;其响应将原封不动地…

用ASR PRO离线语音芯片和月饼盒做一个会跑会跳会说话的机器狗

中秋节刚过&#xff0c;大家月饼盒应该还有&#xff0c;不要扔&#xff0c;可以做点小玩意。 机器狗的创意来自B站石桥北的视频&#xff0c;他使用了一块ESP32芯片和打印件加四个舵机实现&#xff0c;应该说是比较复杂的&#xff0c;需要有3D打印机打印外壳&#xff0c;还得会…

Linux标准IO-系统调用详解

1.1 系统调用 系统调用&#xff08;system call&#xff09;其实是 Linux 内核提供给应用层的应用编程接口&#xff08;API&#xff09;&#xff0c;是 Linux 应用层进入内核的入口。不止 Linux 系统&#xff0c;所有的操作系统都会向应用层提供系统调用&#xff0c;应用程序通…

【Harmony】轮播图特效,持续更新中。。。。

效果预览 swiper官网例子 Swiper 高度可变化 两边等长露出&#xff0c;跟随手指滑动 Swiper 指示器导航点位于 Swiper 下方 一、官网 例子 参考代码&#xff1a; // xxx.ets class MyDataSource implements IDataSource {private list: number[] []constructor(list: nu…

软考高级:嵌入式系统调度算法 AI 解读

嵌入式系统中的调度算法用于管理任务的执行顺序&#xff0c;确保系统资源能够有效分配。以下是几种常见的调度算法的通俗讲解。 生活化例子 想象你是一位超市收银员&#xff0c;有很多顾客排队&#xff0c;每位顾客都可以看作一个任务&#xff0c;收银台就是你的处理器。你需…

PostgreSQL技术内幕10:PostgreSQL事务原理解析-日志模块介绍

文章目录 0.简介1.PG日志介绍2.事务日志介绍3.WAL分析3.1 WAL概述3.2 WAL设计考虑3.2.1 存储格式3.2.2 实现方式3.2.3 数据完整性校验3.3 check ponit 4.事务提交日志&#xff08;CLOG&#xff09;4.1 clog存储使用介绍4.2 slru缓冲池并发控制 0.简介 本文将延续上一篇文章内容…

【无标题】Java_Se 数据变量与运算符

标识符、变量、常量、数据类型、运算符、基本数据类型的类型转换等。这些是编程中的“砖块”&#xff0c;是编程的基础。要想开始正式编程&#xff0c;还需要再学“控制语句”&#xff0c;控制语句就像“水泥”&#xff0c;可以把“砖块”粘到一起&#xff0c;最终形成“一座大…

华为OD机试 - 二维伞的雨滴效应(Python/JS/C/C++ 2024 E卷 200分)

华为OD机试 2024E卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试真题&#xff08;Python/JS/C/C&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;私信哪吒&#xff0c;备注华为OD&#xff0c;加入华为OD刷题交流群&#xff0c;…

ClickHouse-Kafka Engine 正确的使用方式

Kafka 是大数据领域非常流行的一款分布式消息中间件&#xff0c;是实时计算中必不可少的一环&#xff0c;同时一款 OLAP 系统能否对接 Kafka 也算是考量是否具备流批一体的衡量指标之一。ClickHouse 的 Kafka 表引擎能够直接与 Kafka 系统对接&#xff0c;进而订阅 Kafka 中的 …

镀金引线---

一、沉金和镀金 沉金和镀金都是常见的PCB金手指处理方式&#xff0c;它们各有优劣势&#xff0c;选择哪种方式取决于具体的应用需求和预算。 沉金&#xff08;ENIG&#xff09;是一种常用的金手指处理方式&#xff0c;它通过在金手指表面沉积一层金层来提高接触性能和耐腐蚀性…

【C++】模拟实现vector

在上篇中我们已经了解过的vector各种接口的功能使用&#xff0c;接下来我们就试着模拟实现一下吧&#xff01; 注意&#xff1a;我们在此实现的和C标准库中实现的有所不同&#xff0c;其目的主要是帮助大家大概理解底层原理。 我们模拟vector容器的大致框架是&#xff1a; t…

2024年【四川省安全员B证】新版试题及四川省安全员B证考试试卷

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 四川省安全员B证新版试题参考答案及四川省安全员B证考试试题解析是安全生产模拟考试一点通题库老师及四川省安全员B证操作证已考过的学员汇总&#xff0c;相对有效帮助四川省安全员B证考试试卷学员顺利通过考试。 1、…

【webpack4系列】webpack基础用法(二)

文章目录 entryoutputloaderpluginmode前端构建基础配置关联HTML插件html-webpack-plugin构建 CSS 解析 ES6和React JSX解析 ES6解析 React JSX 解析CSS、Less和Sass解析CSS解析Less解析sass 解析图片和字体资源解析&#xff1a;解析图片资源解析&#xff1a;解析字体资源解析&…

JS - 获取剪切板内容 Clipboard API

目录 1&#xff0c;需求最终效果 2&#xff0c;实现示例 3&#xff0c;注意点1&#xff0c;只支持安全上下文环境2&#xff0c;只能读取当前页面的剪切板3&#xff0c;权限获取问题4&#xff0c;获取内容的 MIME_TYPE 问题1&#xff0c;文本内容2&#xff0c;图片内容 5&#x…