RPC框架引入zookeeper服务注册与服务发现

Zookeeper概念及其作用

ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是大数据生态中的重要组件。它是集群的管理者,监视着集群中各个节点的状态根据节点提交的反馈进行下一步合理操作。最终,将简单易用的接口和性能高效、功能稳定的系统提供给用户。

它是一个为分布式应用提供一致性协调服务的中间件
zookeeper入门参考链接:https://www.cnblogs.com/xinyonghu/p/11031729.html

在分布式系统中,zookeeper提供了非常丰富的应用,本文只是剖析其中一小部分,但也是非常重要的一个部分,即服务注册和服务发现。
在RPC框架中,如果没有服务注册和服务发现,那么这个RPC框架几乎变得不实用,浅显的思路是在RPCConsumer(服务调用端)维护一个服务的列表,这个列表包含了所有分布式节点服务的ip和端口,但考虑这么一种情况,如果其中某个节点由于某种原因down掉了或者将这个节点的服务删除了,但是RPCConsumer本地还维护的列表中还存在这个服务结点,并且还尝试请求这个服务,那么显然会调用出错。
在这里插入图片描述

类似这种肯定需要动态的维护每个分布式服务节点的状态,在该节点down掉或者被撤销时应及时删除这个服务,避免RPC调用端继续请求不存在的服务。这就是zookeeper服务注册和服务发现所做的事。

在这里插入图片描述
Zookeeper组织数据的格式类似于一个文件系统,每个znode结点都可以是一个分布式服务结点,一般组织的结构是XXXXService/login、 XXXXService/registe,即service_name/method_name,znode结点的数据就是该服务所在节点的ip和port
在这里插入图片描述

Zookeeper服务注册和发现的流程:

step1:Rpc服务端先通过zkClient向zkServer端注册服务,也即创建XXXXService/login、 XXXXService/registe节点,并填充相应的数据。
step2:Rpc调用端再调用某个服务之前,通过zkClient向ZkServer查询这个服务节点是否存在,如果存在则返回这个服务节点的ip和port。然后进行远程rpc调用,否则返回错误终止调用过程。
step3:这一步其实zookeeper已经帮我们做了,step1中注册服务的过程中,zkServer会与这个节点建立一个session,并且zkServer以1/3 * timeout 的时间定期为每个与之简历的节点发送心跳包,如果得不到回应那么zkServer会认为这个节点已经不存在了,会动态的把这个节点上的所有服务都进行删除。

RPC框架引入zookeeper

1、封装zkclient(用于与zkServer通信的句柄、例如创建结点和删除结点、以及一些心跳回调操作)

#pragma once#include <semaphore.h>
#include <zookeeper/zookeeper.h>
#include <string>class ZkClient
{
public: ZkClient();~ZkClient();// zkClient启动连接zkservervoid Start();// 在zkserver上根据指定的path创建Znode节点void Create(const char *path, const char* data, int datalen, int state=0);// 根据参数指定的znode节点路径,获取znode节点的值std::string GetData(const char* path);private:// zk客户端句柄zhandle_t *m_zhandle;
};// .cc
#include "zookeeperutil.h"
#include "rpcapplication.h"
#include  <iostream>//全局的watcher观察器     zkserver给zkclient的通知回调
void global_watcher(zhandle_t *zh, int type, int state, const char* path, void *watcherCtx)
{if(type == ZOO_SESSION_EVENT)    //回调的消息类型是和会话相关的消息类型{if(state == ZOO_CONNECTED_STATE)  //zkserver和zkclient连接成功{sem_t *sem = (sem_t*) zoo_get_context(zh);sem_post(sem);}}
}ZkClient::ZkClient():m_zhandle(nullptr)
{}
ZkClient::~ZkClient()
{if(m_zhandle != nullptr){zookeeper_close(m_zhandle);   //关闭句柄, 释放资源}
}
// zkClient启动连接zkserver
void ZkClient::Start()
{std::string host = RpcApplication::GetInstance().GetConfig().Load("zookeeperip");std::string port = RpcApplication::GetInstance().GetConfig().Load("zookeeperport");std::string connstr = host + ":" + port;/*zookeeper_mt:多线程版本zookeeper的API客户端程序提供了三个线程APT调用线程网络I/O线程  pthread_create (使用的poll-IO多路复用)watcher回调线程 pthread_create*/m_zhandle = zookeeper_init(connstr.c_str(), global_watcher, 30000, nullptr, nullptr, 0);if(nullptr == m_zhandle){std::cout << "zookeeper_init error !" << std::endl;exit(EXIT_FAILURE);}sem_t sem;sem_init(&sem, 0, 0);zoo_set_context(m_zhandle, &sem);sem_wait(&sem);std::cout << "zookeeper_init success !" << std::endl;
}// 在zkserver上根据指定的path创建Znode节点
void ZkClient::Create(const char *path, const char* data, int datalen, int state)
{char path_buffer[128];int bufferlen = sizeof(path_buffer);int flag;//先判断path表示的znode节点是否存在, 如果存在, 就不能重复创建了flag = zoo_exists(m_zhandle, path, 0, nullptr);if(ZNONODE == flag)   //表示path的znode节点不存在{// 创建指定path的znode节点flag = zoo_create(m_zhandle, path, data, datalen, &ZOO_OPEN_ACL_UNSAFE, state, path_buffer, bufferlen);if(flag == ZOK){std::cout << "znode create success .... path:" << path << std::endl;}else{std::cout << "flag : " << flag <<std::endl;std::cout << "znode create error...path: " << path << std::endl;exit(EXIT_FAILURE);}}
}// 根据参数指定的znode节点路径,获取znode节点的值
std::string ZkClient::GetData(const char* path)
{char buffer[64];int bufferlen = sizeof(buffer);int flag = zoo_get(m_zhandle, path, 0, buffer, &bufferlen, nullptr);if(flag != ZOK){std::cout << "get znode error ...... path" << path << std::endl;return "";}else{return buffer;}
}

2、在RPCProvider端进行服务注册

//把当前rpc节点上要发布的服务全部注册到zk上面, 让rpc client可以从zk上发现服务// session timeout 30s          zkclient 的网络I/O线程 会定时以1/3 * timeout 时间去给zkserver发送ping心跳包ZkClient zkCli;zkCli.Start();//service name为永久性节点      method name 为临时性节点for(auto& sp : m_serviceMap){// /service_name   ---> /UserServiceRPcstd::string service_path = "/" + sp.first;zkCli.Create(service_path.c_str(), nullptr, 0);for(auto &mp : sp.second.m_methodMap){// /service_name/method_name /UserServiceRPc/Login 存储当前这个rpc服务节点主机的ip和portstd::string method_path = service_path + "/" + mp.first;char method_path_data[128] = {0};sprintf(method_path_data, "%s:%d", ip.c_str(), port);//ZOO_EPHEMERAL 表示znode是一个临时性节点zkCli.Create(method_path.c_str(), method_path_data, strlen(method_path_data), ZOO_EPHEMERAL);}}

3、RPCConsumer端进行服务发现

//rpc调用方想调用service_name的method_name的服务, 需要查询zk上该服务所在的host信息ZkClient zkCli;zkCli.Start();// /UserServiceRpc/Loginstd::string method_path = "/" + service_name + "/" + method_name;// 127.0.0.1:8000std::string host_data = zkCli.GetData(method_path.c_str());if(host_data == ""){controller->SetFailed(method_path + "is not exist!");return;}int idx = host_data.find(":");if(idx == -1){controller->SetFailed(method_path + "address is invalid!");return;}std::string ip = host_data.substr(0, idx);uint16_t port = atoi(host_data.substr(idx + 1, host_data.size() - idx).c_str());

至此基本上完整RPC应该具备的核心东西都有了。

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

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

相关文章

使用 GitHub Copilot 进行 Prompt Engineering 的初学者指南(译)

文章目录 什么是 GitHub Copilot ?GitHub Copilot 可以自己编码吗&#xff1f;GitHub Copilot 的底层是如何工作的&#xff1f;什么是 prompt engineering?这是 prompt engineering 的另一个例子 使用 GitHub Copilot 进行 prompt engineering 的最佳实践提供高级上下文&…

Springboot -- 按照模板生成docx、pdf文件,docx转pdf格式

使用 poi-tl 根据模板生成 word 文件。 使用 xdocreport 将 docx 文件转换为 pdf 文件。 xdocreport 也支持根据模板导出 word &#xff0c;但是 poi-tl 的功能更齐全&#xff0c;操作更简单&#xff0c;文档清晰。 poi-tl 、xdocreport 内部均依赖了 poi &#xff0c;要注意两…

uniapp 全局数据(globalData)的设置,获取,更改

globalData&#xff0c;这是一种简单的全局变量机制。这套机制在uni-app里也可以使用&#xff0c;并且全端通用 因为uniapp基本上都是将页面&#xff0c;或者页面中相同的部分&#xff0c;进行组件化&#xff0c;所以会存在父&#xff0c;子&#xff0c;&#xff08;子&#xf…

【数据挖掘竞赛】——科大讯飞:锂离子电池生产参数调控及生产温度预测挑战赛

🤵‍♂️ 个人主页:@Lingxw_w的个人主页 ✍🏻作者简介:计算机科学与技术研究生在读 🐋 希望大家多多支持,我们一起进步!😄 如果文章对你有帮助的话, 欢迎评论 💬点赞👍🏻 收藏 📂加关注+ ​ 【科大讯飞】报名链接:https://challenge.xfyun.cn?invitaC…

在windows下安装ruby使用gem

在windows下安装ruby使用gem 1.下载安装ruby环境2.使用gem3.gem换源 1.下载安装ruby环境 ruby下载地址 选择合适的版本进行下载和安装&#xff1a; 在安装的时候&#xff0c;请勾选Add Ruby executables to your PATH这个选项&#xff0c;添加环境变量&#xff1a; 安装Ruby成…

【Ansible 的脚本 --- playbook 剧本】

目录 一、playbook 剧本介绍二、示例1、运行playbook2、定义、引用变量 三、使用playbook部署lnmp集群 一、playbook 剧本介绍 playbooks 本身由以下各部分组成 &#xff08;1&#xff09;Tasks&#xff1a;任务&#xff0c;即通过 task 调用 ansible 的模板将多个操作组织在…

通过私有化部署的企业智能名片,作用究竟有多大?

“在21世纪最为重要的是什么&#xff1f;” “是人才&#xff0c;更是人脉。” 为了拓展人际关系&#xff0c;建立更密切的联系和合作联络。在商务交流中&#xff0c;互相交换正式、可信的名片是必不可少的一环。 但是&#xff0c;你有没有意识到呢&#xff1f; 每年全球交换…

2023年08月数据库流行度最新排名

点击查看最新数据库流行度最新排名&#xff08;每月更新&#xff09; 2023年08月数据库流行度最新排名 TOP DB顶级数据库索引是通过分析在谷歌上搜索数据库名称的频率来创建的 一个数据库被搜索的次数越多&#xff0c;这个数据库就被认为越受欢迎。这是一个领先指标。原始数…

使用DataX实现mysql与hive数据互相导入导出

一、概论 1.1 什么是DataX DataX 是阿里巴巴开源的一个异构数据源离线同步工具&#xff0c;致力于实现包括关系型数据库(MySQL、Oracle 等)、HDFS、Hive、ODPS、HBase、FTP 等各种异构数据源之间稳定高效的数据同步功能。 1.2 DataX 的设计 为了解决异构数据源同步问题&#xf…

Vue2 第十三节 使用Vue脚手架 (二)

1. ref属性 2. props配置项 3.mixin混入 4.plugin插件 一. ref属性 ① 作用&#xff1a;用于给节点打标识&#xff08;给元素或者组件注册引用信息&#xff0c;id的替代者&#xff09; ② 语法&#xff1a; 应用在html标签上获取的是真实的DOM元素&#xff0c;应用在组件…

JsonPath使用和示例

JsonPath使用和示例 1 简介2 官方实例3 JsonPath与XPath语法对比4 实例说明JsonPath与XPath语法5 Python中JsonPath模块6 Python中JsonPath使用7 结合接口测试的实例 1 简介 官网&#xff1a;https://goessner.net/articles/JsonPath/&#xff1b;JsonPath 是一种简单的方法来…

vite babel 获取组件的 children 代码, 填写到 jsxCode 属性中

最终效果 <DocsModule title"类型"><Button>默认按钮</Button><Button type"primary">主要按钮</Button><Button type"success">成功按钮</Button><Button type"danger">危险按钮&l…

2023年中职组“网络安全”赛项吉安市竞赛任务书

2023年中职组“网络安全”赛项 吉安市竞赛任务书 一、竞赛时间 总计&#xff1a;360分钟 竞赛阶段 竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 A模块 A-1 登录安全加固 180分钟 200分 A-2 本地安全策略配置 A-3 流量完整性保护 A-4 事件监控 A-5 服务加固…

docker logs 使用说明

docker logs 可以查看某个容器内的日志情况。 前置参数说明 c_name容器名称 / 容器ID logs 获取容器的日志 , 命令如下&#xff1a; docker logs [options] c_name option参数&#xff1a; -n 查看最近多少条记录&#xff1a;docker logs -n 5 c_name--tail与-n 一样 &#…

【iOS】锁

线程安全 当一个线程访问数据的时候&#xff0c;其他的线程不能对其进行访问&#xff0c;直到该线程访问完毕。简单来讲就是在同一时刻&#xff0c;对同一个数据操作的线程只有一个。而线程不安全&#xff0c;则是在同一时刻可以有多个线程对该数据进行访问&#xff0c;从而得…

openCV C++环境配置

文章目录 一、openCV 安装二、新建项目三、配置环境变量四、测试使用 编译器:vs2017 OpenCV:4.5.4 一、openCV 安装 将openCV安装到一个路径下&#xff0c;我安装到了D盘根目录下 二、新建项目 在vs2017新建控制台空项目&#xff0c;打开项目属性 在VC目录 -> 包含目录下…

智慧林业~经典开源项目数字孪生智慧林业——开源工程及源码

东北林业局的工程和源码免费赠送&#xff0c;帮您实现深林防火的智慧林业。 项目介绍 东北林业局作为东北地区林业管理的重要机构&#xff0c;致力于森林资源保护和防火工作。他们的项目通过先进的技术手段&#xff0c;为林业管理提供可靠的解决方案。 本项目使用数字孪生技术&…

【实操教程】如何开始用Qt Widgets编程?(一)

Qt 是目前最先进、最完整的跨平台C开发工具。它不仅完全实现了一次编写&#xff0c;所有平台无差别运行&#xff0c;更提供了几乎所有开发过程中需要用到的工具。如今&#xff0c;Qt已被运用于超过70个行业、数千家企业&#xff0c;支持数百万设备及应用。 在本文中&#xff0…

轮足机器人硬件总结

简介 本文主要根据“轮腿机器人Hyun”总结的硬件部分。 轮腿机器人Hyun开源地址&#xff1a;https://github.com/HuGuoXuang/Hyun 1 电源部分 1.1 78M05 78M05是一款三端稳压器芯片&#xff0c;它可以将输入电压稳定输出为5V直流电压. 1.2 AMS1117-3.3 AMS1117-3.3是一种输…

Vue实现leafletMap自定义绘制线段 并且删除指定的已绘制的点位

效果&#xff1a;点击表格可实现选中地图点位&#xff0c;删除按钮点击可删除对应点位并且重新绘制线段&#xff0c;点击确定按钮 保存已经绘制的点位信息传给父组件 并且该组件已实现回显 完整的组件代码如下 文件名称为&#xff1a; leafletMakePointYt <!--* Descripti…