RPC分布式网络通信框架(三)—— 服务配置中心Zookeeper模块

news2024/11/25 13:47:43

文章目录

  • 一、使用Zookeeper的意义
  • 二、Zookeeper基础
    • 1 文件系统
    • 2 通知机制
    • 3 原生zkclient API存在的问题
    • 4 服务配置中心Zookeeper模块
  • 三、Zk类实现
    • ==Start方法==
    • 创建节点、get节点值方法
  • 四、框架应用
    • rpc提供端框架
    • rpc调用端(客户端)框架
  • 总结


一、使用Zookeeper的意义

分布式系统存在的问题:
为了支持高并发,每个客户端都保存了一份服务提供者的列表。但是如果列表有更新,想要得到最新的URL列表(rpc服务的ip和端口号),必须要手动更新配置文件,很不方便。
如图所示,实例3挂掉了,但是列表并没有得到更新。
在这里插入图片描述
故需要动态的更新URL列表,由此引入Zookeeper服务配置中心。

二、Zookeeper基础

zookeeper是为分布式应用提供一致性协调服务的中间件

本质:类似linux的文件系统
调用者真实运行的情况下其实并不知道自已想要调用的函数在哪台机器上(不知道ip和端口)
所以调用者需要在调用前先去服务配置中心去问一下想调用API所在机器上的ip和端口。
同时他还提供全局分布式锁,起到协调控制管理各个分布式节点的功能。

1 文件系统

Zookeeper提供一个多层级的节点命名空间(节点称为znode)。与文件系统不同的是,这些节点都可以设置关联的数据,而文件系统中只有文件节点可以存放数据,目录节点不能存放数据。
Zookeeper为了保证高吞吐和低延迟,在内存中维护了这个树状的目录结构,这种特性使得Zookeeper不能用于存放大量的数据,每个节点的存放数据上限为1M。

一个节点可以存储1Mb的数据
在这里插入图片描述

znode节点操作指令:

  • ls 罗列节点
  • get 查看节点
  • create 创建节点
  • set 修改节点的值
  • delete 删除节点(要先删子节点)

2 通知机制

client端会对某个znode建立一个watcher事件,当该znode发生变化时,这些client会收到zk的通知,然后client可以根据znode变化来做出业务上的改变等。

zk的watcher机制
客户端通过watcher机制监听zk节点(变化)
客户端维护的map,键是节点的名字,值是节点的内容(ip+端口)
通过通知和回调机制,由zk主动向客户端汇报节点的变化(节点死掉、新节点加入)

3 原生zkclient API存在的问题

1、设置监听的watcher是一次性的
2、znode 节点只存储简单的byte字节数组(如果想存对象,需要转换对象生成字节数组)
注意:
原生zkclient会自动发送心跳消息(维护session),源码会在1/3的Timeout时间发送ping心跳。
抓包验证:
sudo tcpdump -I lo port 2181 // 抓2181这个端口的所有包

4 服务配置中心Zookeeper模块

所以,需要一个项目注册节点中心配置,维护session会话相当于检测tcp连接的心跳消息,以此来确定链接是否断开。
项目中的由每个服务创建的节点为临时性节点
总结:每个rpc服务器端都会向zookeeper服务注册配置中心 传入 ip + port + 服务名字,客户端进程查询获得ip+port
在这里插入图片描述

三、Zk类实现

封装的ZkClient客户端类头文件

#pragma once

class ZkClient
{
public:
    ZkClient();
    ~ZkClient();
    void Start();  // zkclient启动连接zkserver
    void Create(const char *path, const char *data, int datalen, int state=0); // 在zkserver上根据指定的path创建znode节点
    std::string GetData(const char *path);  // 根据参数指定的znode节点路径,或者znode节点的值
private:
   zhandle_t *m_zhandle;  // zk的客户端句柄
};

构造、析构实现:

ZkClient::ZkClient() : m_zhandle(nullptr)
{
}

ZkClient::~ZkClient()
{
    if (m_zhandle != nullptr)
    {
        zookeeper_close(m_zhandle); // 关闭句柄,释放资源  MySQL_Conn
    }
}

Start方法

首先从配置文件中读取zookeeper客户端ip和port。

std::string host = MprpcApplication::GetInstance().GetConfig().Load("zookeeperip");
std::string port = MprpcApplication::GetInstance().GetConfig().Load("zookeeperport");
std::string connstr = host + ":" + port;

使用zookeeper_mt的多线程版本

zk的客户端提供了三个线程
1、API调用线程:zookeeper_init,直接导致下面两个线程的开辟
2、网络I/O收发线程:pthread_create(底层为poll,且会在1/3的Timeout时间发送ping心跳保持与zkserver的通信)
3、watcher回调线程:pthread_create当zkclient接收zkserver的响应后,zkserver给zkclient通知
zookeeper是异步连接过程,需要绑定一个全局回调函数global_watcher(新线程连接)

m_zhandle = zookeeper_init(connstr.c_str(), global_watcher, 30000, nullptr, nullptr, 0);
之后检查创建的m_zhandle是否为空指针

输入:(127.0.0.1: 2181, 回调函数, session超时时间30s, null, null, 0)

注:

  • zk的端口号2181。
  • 上述代码只是成功创建句柄资源,并不代表zkserver的连接成功与否。
  • 全局回调函数global_watcher决定是否连接成功。

全局的watcher观察器:

// 全局的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)  // zkclient和zkserver连接成功
		{
			sem_t *sem = (sem_t*)zoo_get_context(zh);
            sem_post(sem);
		}
	}
}

直到收到state == ZOO_CONNECTED_STATE消息才算连接成功。这时sem信号量置1。
也就说sem的值是由全局观察者在连接状态变为已连接时通过调用sem_post()或类似的函数来增加的。

Start方法中等待信号量sem为1,连接成功,打印信息。

sem_t sem;
sem_init(&sem, 0, 0);
zoo_set_context(m_zhandle, &sem);

sem_wait(&sem);  // 阻塞,直到sem为1
std::cout << "zookeeper_init success!" << std::endl;

创建节点、get节点值方法

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

// 根据指定的path,获取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;
	}
}

四、框架应用

rpc提供端框架

首先调用Start方法。
之后将当前rpc节点上要发布的服务全部注册到zk上,让rpc调用端可以从zk上发现服务。

  • 先创建永久父节点/UserServiceRpc
  • 再根据提供端维护的rpc方法map表,创建临时子节点/UserServiceRpc/Login(Login方法…),也就是将要发布的服务全部注册到zk上。
// session timeout   30s     zkclient 网络I/O线程  1/3 * timeout 时间发送ping消息
ZkClient zkCli;
zkCli.Start();
// service_name为永久性节点    method_name为临时性节点
for (auto &sp : m_serviceMap) 
{
    // /service_name   /UserServiceRpc
    std::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和port
        std::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);
    }
}

rpc调用端(客户端)框架

首先,同样是调用Start方法:

ZkClient zkCli;
zkCli.Start();

然后,CallMethod中通过zk获取ip:port。也就是说,通过要调用方法的名称(Login)在zk的节点中寻找对应的ip和port。

//  /UserServiceRpc/Login
std::string method_path = "/" + service_name + "/" + method_name;
// 127.0.0.1:8000
std::string host_data = zkCli.GetData(method_path.c_str());

因为读取的地址是host_data = 127.0.0.1:8000,所以将其分离:

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()); 

总结

Zookeeper功能如下:

  • master节点选举, 主节点down掉后, 从节点就会接手工作, 并且保证这个节点是唯一的,这也就是所谓首脑模式,从而保证我们集群是高可用的
  • 统一配置文件管理, 即只需要部署一台服务器, 则可以把相同的配置文件同步更新到其他所有服务器, 此操作在云计算中用的特别多(例如修改了redis统一配置)
  • 数据发布与订阅, 类似消息队列MQ
  • 分布式锁,分布式环境中不同进程之间争夺资源,类似于多进程中的锁
  • 集群管理, 保证集群中数据的强一致性

服务配置中心用法:

  • 每个rpc服务器端都会向zookeeper服务注册配置中心 传入 ip + port + 服务名字 客户端进程查询获得ip+port

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

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

相关文章

平均精度 (mAP):常见定义、误区和误解

我们分解并揭开了常见对象检测指标的神秘面纱,包括平均精度 (mAP) 和平均平均召回率 (mAR)。 这篇文章深入介绍了如何正确计算和使用平均平均精度 (mAP) 和平均平均召回率 (mAR) 进行对象检测,同时消除对 AP、mAP 和第三方库(例如 TorchMetrics 或 pycocotools)的常见误解。…

基于冻土水文模拟的松花江流域水资源演变规律

原文信息 题目&#xff1a;基于冻土水文模拟的松花江流域水资源演变规律 作者&#xff1a;刘水清 周祖昊 刘佳嘉 李佳 谢新民 贾仰文 王浩 期刊&#xff1a;《南水北调与水利科技&#xff08;中英文&#xff09;》23年1期 摘要 为分析松花江流域水资源的演变规律&#…

基于SpringCloud微服务图书管理系统设计与实现

一、引言 本次设计基于JavaEE和SpringCloud微服务的图书馆管理系统。利用当前计算机技术的快速发展来构建图书馆管理系统。 随着计算机技术和网络的飞速发展,互联网与互联网加的程序应用在世界范围内越来越流行,当今社会正迅速进入信息社会,信息自动化的作用也日益增强。…

MySQL基础篇第7章(单行函数)

文章目录 1、函数的理解1.1 什么是函数1.2 不同DBMS函数的差异1.3 MySQL的内置函数分类 2、数值函数2.1 基本函数2.2 角度与弧度互转函数2.3 三角函数2.4 指数和对数2.5 进制间的转换 3、字符串函数4、日期和时间函数4.1 获取日期、时间4.2 日期与时间戳的转换4.3 获取月份、星…

815. 打印字符串

链接&#xff1a; 链接 题目&#xff1a; 给定一个字符串&#xff0c;请你编写一个函数&#xff0c;void print(char str[])&#xff0c;将这个字符串打印出来。 输入格式 共一行&#xff0c;包含一个字符串。 输出格式 共一行&#xff0c;表示打印出的字符串。 数据范围 1≤字…

STM32 Proteus仿真ili9341 TFT2048小游戏 -0067

STM32 Proteus仿真ili9341 TFT2048小游戏 -0067 Proteus仿真小实验&#xff1a; STM32 Proteus仿真ili9341 TFT2048小游戏 -0067 功能&#xff1a; 硬件组成&#xff1a;STM32F103R6单片机 ili9341 TFT显示器上下左右方向键赢了按键输了按键 1.标准2048经典游戏玩法&#…

LayUI之动态选项卡Tabiframe使用

目录 一.LayUI之动态选项卡 1.什么是LayUI之选项卡&#xff1f; 1.2layui选项卡使用语法 2. 在企业中LayUI选项卡的使用 二.将layui选项卡部署在web项目中 1.首先查看layui官方文档 2.加入Jsp 3.加入数据&#xff0c;并进行bug修复 3.1首先在我们的二级菜单添加一个点击…

GPT-4的详细信息已经泄露

这位作者说GPT-4的详细信息已经泄露&#xff0c;不知道可信度如何。一些关键信息&#xff1a;- GPT-4的大小是GPT-3的10倍以上。我们认为它在120层中总共有大约1.8万亿个参数。- GPT-4是多个专家模型混合在一起&#xff0c;但不是之前说的8个专家&#xff0c;而是16个。研究人员…

Linux获取文件夹下的所有文件名称

用shell脚本的方式实现 新建脚本文件 fapiao.sh [rootiZbp1bjm0o6frv1c7pp8uaZ home]# vim fapiao.sh编写脚本内容 #!/bin/bash# path文件夹路径 path/home/发票文件 #列出文件名 files$(ls $path)for filename in $files do#将文件名打印至filename.txt 或者路径文件 /hom…

Java找实习经历

Java实习 我开始找实习的时间是2023.7.7&#xff0c;第一个面试是2023.7.11&#xff0c;话不多说&#xff0c;先上图 boss 沟通了300个投递简历也就18份&#xff0c;也就说差不多有20个回复了我&#xff0c;其中约面试的又很少 51job 申请了65份&#xff0c;其中查看的大概10…

《数学模型(第五版)》学习笔记(1) 第1章 建立数学模型 第2章 初等模型

参考数学建模论坛《数学模型(第三版)》学习笔记 http://www.madio.net/thread-146480-1-1.html 参考视频 数模视频&#xff08;姜启源、谢金星&#xff09; https://www.bilibili.com/video/BV1VJ411w7r3/?spm_id_from333.788.recommend_more_video.0&vd_source3ef6540f84…

C语言联合体

一、联合体的概念 联合 (union) 是一个能在同一个存储空间里 ( 但不同时) 存储不同类型数据的复合数据类型。 大致结构如下&#xff1a; n union foo /* 定义一个联合类型foo */ n { q int digit; q double bigfl[10]; q char letter; n }baz; /* 定义一个example类型的联合变量…

还不习惯用软件管理工作项?体验“自动化规则”解决“痛点”

随着AI&#xff0c;ChatGPT等技术的飞速发展&#xff0c;一些科技界人士提出“程序员已死”&#xff0c;其大概意思是讲在未来AI将替代程序员的一些工作&#xff0c;但其实人工智能来代替程序员工作&#xff0c;并非想象中那么容易&#xff0c;在程序员的核心能力中&#xff0c…

路径规划算法:基于跳蛛优化的路径规划算法- 附代码

路径规划算法&#xff1a;基于跳蛛优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于跳蛛优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能优化算法跳蛛…

尚硅谷03:前端开发之ES | Vue_es6 Axios Node Npm

目录 内容介绍 统一异常处理 统一日志处理 前端介绍、工具使用 ES6入门 Vue入门 Vue语法 Vue语法高级 内容介绍 1、统一异常处理 2、统一日志处理&#xff08;了解&#xff09; 3、前端介绍 4、ES6 5、VUE入门、基本语法 6、VUE高级语法 7、axios&#xff08;重点…

Pod:Kubernetes里最核心的概念

为了解决这样多应用联合运行的问题&#xff0c;同时还要不破坏容器的隔离&#xff0c;就需要在容器外面再建立一个“收纳舱”&#xff0c;让多个容器既保持相对独立&#xff0c;又能够小范围共享网络、存储等资源&#xff0c;而且永远是“绑在一起”的状态。 Pod 的概念也就呼…

【Ajax】笔记-Ajax案例准备与请求基本操作

案例准备HTML 按钮div <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>AJAX GET 请求</title&g…

C语言进阶之自定义类型(结构体,枚举,联合)

这里写目录标题 1.结构体1.1 结构的基础知识1.2 结构的声明1.3 特殊的声明1.4 结构的自引用1.5 结构体变量的定义和初始化1.6 结构体内存对齐1.7 修改默认对齐数1.8 结构体传参 2. 位段2.1 什么是位段2.2 位段的内存分配2.3 位段的跨平台问题2.4 位段在网络传输中的应用3. 枚举…

【科普贴】UWB定位详解:0维定位、一维定位、二维定位、三维定位

室内定位系统方案中&#xff0c;UWB定位技术目前应用较多&#xff0c;得益于UWB定位10-30厘米的超高定位精度。目前根据使用场景的不同&#xff0c;UWB TDOA定位系统的定位维度分为以下4种&#xff1a;0维定位&#xff08;存在性检测&#xff09;、一维定位、二维定位、三维定位…

spring cloud 搭建消息中间件 RabbitMQ 环境、Mac/Windows下载安装RabbitMQ、配置RabbitMQ环境变量

主要内容概述&#xff1a;spring cloud工程&#xff0c;Mac/Windows下载安装RabbitMQ&#xff0c;并配置环境变量 前言 这里学习如何安装 RabbitMQ&#xff0c;因为远程配置中心的动态更新需要结合 RabbitMQ 来使用。 什么是 RabbitMQ RabbitMQ 是消息队列中间件&#xff0c…