手写分布式存储系统v0.3版本
引言
承接 手写分布式存储系统v0.2版本 ,今天开始新的迭代开发。主要实现 服务发现功能
一、什么是服务发现
由于咱们的服务是分布式的,那从服务管理的角度来看肯定是要有一个机制来知道具体都有哪些实例可以提供服务。举个例子就是,张三家里在全国各地有不少火锅加盟店,那张三肯定要有一个方式知道这些火锅店加盟店的情况。例如上海又新开了一家加盟店,那么这家加盟店肯定要先通过某种方式联系张三,这样张三才能将配方以及食材供应给这家新的加盟店等等。
疑问
-
为什么不能通过域名映射的方式来做映射,客户端通过域名调用服务就好了为啥要专门做服务发现
答:域名映射是对外提供服务时使用的,而我们的系统还有很多场景要做内部的服务管理,例如某个节点故障了,为了服务能够继续保证高可用,咱们的分布式存储系统就要将这个节点上所管理的数据分给其余的节点进行管理等,这个时候系统内部就需要明确知道各个分布式节点的信息。
二、服务发现设计
目前服务发现设计主要有以下几种
- 配置化:将所有节点的信息写在服务配置里,像ES等
- 使用能保证一致性的外部服务:如kafka、bookkeeper等,外部服务有zookeeper、etcd、consul等
- 主从架构里,所有从节点启动时自动向主服务注册自己的节点信息:如hdfs、yarn等
为了方便扩展,同时咱们的存储服务能够设计成无主架构,因此采用第二种采用外部服务zookeeper来进行实现。实现的大致流程如下图

所有节点实例在启动时,都去zookeeper上创建属于自己的目录,在节点下线时就将自己对应的目录进行删除。这样只需要监听“服务发现目录”就能知道是否有节点上下线。同时为了避免服务故障时没能正确删除自己的目录,因此咱们采用zookeeper临时目录的功能,例如节点1启动并在zookeeper创建对应临时目录后,会每隔一小段时间向zookeeper发送请求也就是心跳,证明自己的服务还正常;如果zookeeper在等待一段时间后,没收到某个节点的心跳,就会默认这个服务已经挂了并将其对应的临时目录进行删除。
三、代码实现
由于把全部代码贴上来不太现实且不易于阅读,就将开发时核心测试样例贴上来供大家伙参考
package com.sherlock;import org.apache.zookeeper.*;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Stat;import java.io.File;
import java.util.List;
import java.util.concurrent.CountDownLatch;/*** author: shalock.lin* date: 2024/2/4* describe:*/
public class BaseZookeeper implements Watcher {private static ZooKeeper zookeeper;public static void main(String[] args) throws Exception {BaseZookeeper baseZookeeper = new BaseZookeeper();baseZookeeper.connectZookeeper("127.0.0.1:2181");List<String> children = baseZookeeper.getChildren("/");System.out.println(children);AsyncCallback.StringCallback scb = new AsyncCallback.StringCallback() {@Overridepublic void processResult(int rc, String path, Object ctx, String name) {System.out.println(rc);}};asyncCreateFullPathOptimistic(zookeeper,"/distributed-storage-system/available/shalocklindeMacBook-Pro.local", "testData".getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT,scb, null);Thread.sleep(5000);List<String> afterChildren = baseZookeeper.getChildren("/");System.out.println(afterChildren);}/*** 超时时间*/private static final int SESSION_TIME_OUT = 2000;private CountDownLatch countDownLatch = new CountDownLatch(1);@Overridepublic void process(WatchedEvent event) {if (event.getState() == Event.KeeperState.SyncConnected) {System.out.println("Watch received event");countDownLatch.countDown();}}/**连接zookeeper* @param host* @throws Exception*/public void connectZookeeper(String host) throws Exception{zookeeper = new ZooKeeper(host, SESSION_TIME_OUT, this);countDownLatch.await();System.out.println("zookeeper connection success");}/*** 获取路径下所有子节点* @param path* @return* @throws KeeperException* @throws InterruptedException*/public List<String> getChildren(String path) throws KeeperException, InterruptedException{List<String> children = zookeeper.getChildren(path, false);return children;}/*** 获取节点上面的数据* @param path 路径* @return* @throws KeeperException* @throws InterruptedException*/public String getData(String path) throws KeeperException, InterruptedException{byte[] data = zookeeper.getData(path, false, null);if (data == null) {return "";}return new String(data);}/*** 设置节点信息* @param path 路径* @param data 数据* @return* @throws KeeperException* @throws InterruptedException*/public Stat setData(String path, String data) throws KeeperException, InterruptedException{Stat stat = zookeeper.setData(path, data.getBytes(), -1);return stat;}/*** 删除节点* @param path* @throws InterruptedException* @throws KeeperException*/public void deleteNode(String path) throws InterruptedException, KeeperException{zookeeper.delete(path, -1);}/*** 获取某个路径下孩子的数量* @param path* @return* @throws KeeperException* @throws InterruptedException*/public Integer getChildrenNum(String path) throws KeeperException, InterruptedException{int childenNum = zookeeper.getChildren(path, false).size();return childenNum;}/*** 关闭连接* @throws InterruptedException*/public void closeConnection() throws InterruptedException{if (zookeeper != null) {zookeeper.close();}}public static void asyncCreateFullPathOptimistic(final ZooKeeper zk, final String originalPath, final byte[] data,final List<ACL> acl, final CreateMode createMode,final AsyncCallback.StringCallback callback, final Object ctx) {zk.create(originalPath, data, acl, createMode, new AsyncCallback.StringCallback() {@Overridepublic void processResult(int rc, String path, Object ctx, String name) {if (rc != KeeperException.Code.NONODE.intValue()) {callback.processResult(rc, path, ctx, name);return;}// Since I got a nonode, it means that my parents don't exist// create mode is persistent since ephemeral nodes can't be// parentsString parent = new File(originalPath).getParent().replace("\\", "/");asyncCreateFullPathOptimistic(zk, parent, new byte[0], acl,CreateMode.PERSISTENT, new StringCallback() {@Overridepublic void processResult(int rc, String path, Object ctx, String name) {if (rc == KeeperException.Code.OK.intValue() || rc == KeeperException.Code.NODEEXISTS.intValue()) {// succeeded in creating the parent, now// create the original pathasyncCreateFullPathOptimistic(zk, originalPath, data,acl, createMode, callback, ctx);} else {callback.processResult(rc, path, ctx, name);}}}, ctx);}}, ctx);}
}
四、功能演示
整个功能验证逻辑如下
-
服务启动前观测zookeeper对应目录下不存在数据

-
启动服务,从控制台能看到服务正常启动

-
再观测zookeeper对应目录下注册了服务的主机名

-
通过打印输出,能看到存在该目录下的服务信息(当前存的是测试样例数据)

-
停止服务,并持续观测一段时间,可以看到目录已被zookeeper删除

五、总结
终于开发一点跟“分布式”相关的内容了,在使用zookeeper时踩了一点坑, 启动服务时报下述异常org.apache.zookeeper.KeeperException$SessionExpiredException: KeeperErrorCode = Session expired for,通过调试发现zookeeper服务端返回的信息非常有限无法得出有用的信息。结果网上的答案,排除了是防火墙、超时配置等问题后,最终发现是自己在调用zookeeper创建路径是直接传了完整的路径也就是多级目录/distributed-storage-system/available/shalocklindeMacBook-Pro.local导致的报错,原因是zookeeper不支持递归创建多级目录,只能参考bookkeeper开发工具类从代码层面递归去zookeeper创建路径。惭愧的是,已经接触zookeeper多年,并且也翻过它的代码,却连这个基本的点都不知晓。因此进一步验证上一篇的想法,就是很多东西真的要自己去实现一遍,否则只沉浸理论容易陷入一种“什么都懂”、“什么都是理所当然”的幻觉并自我感觉良好,但这很有可能会令我们的技术止步不前
相关文章:
手写分布式存储系统v0.3版本
引言 承接 手写分布式存储系统v0.2版本 ,今天开始新的迭代开发。主要实现 服务发现功能 一、什么是服务发现 由于咱们的服务是分布式的,那从服务管理的角度来看肯定是要有一个机制来知道具体都有哪些实例可以提供服务。举个例子就是,张三家…...
除夕快乐!
打印的简单实现,祝大家新的一年万事顺意! 龙年大吉! #include <stdio.h> #include <windows.h> #include <string.h>int main() {const char* message "除夕快乐!";int i;for (i 0; i < strlen(message);…...
17:定时器编程实战
1、实验目的 (1)使用定时器来完成LED闪烁 (2)原来实现闪烁时中间的延迟是用delay函数实现的,在delay的过程中CPU要一直耗在这里不能去做别的事情。这是之前的缺点 (3)本节用定时器来定一个时间(譬如0.3s),在这个定时器定时时间内…...
Fink CDC数据同步(五)Kafka数据同步Hive
6、Kafka同步到Hive 6.1 建映射表 通过flink sql client 建Kafka topic的映射表 CREATE TABLE kafka_user_topic(id int,name string,birth string,gender string ) WITH (connector kafka,topic flink-cdc-user,properties.bootstrap.servers 192.168.0.4:6668…...
ubuntu原始套接字多线程负载均衡
原始套接字多线程负载均衡是一种在网络编程中常见的技术,特别是在高性能网络应用或网络安全工具中。这种技术允许应用程序在多个线程之间有效地分配和处理网络流量,提高系统的并发性能。以下是关于原始套接字多线程负载均衡技术的一些介绍: …...
leetcode (算法)66.加一(python版)
需求 给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。 最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。 你可以假设除了整数 0 之外,这个整数不会以零开头。 示例 1: 输入:digi…...
DataX源码分析 TaskGroupContainer
系列文章目录 一、DataX详解和架构介绍 二、DataX源码分析 JobContainer 三、DataX源码分析 TaskGroupContainer 四、DataX源码分析 TaskExecutor 五、DataX源码分析 reader 六、DataX源码分析 writer 七、DataX源码分析 Channel 文章目录 系列文章目录TaskGroupContainer初始…...
2024年华为OD机试真题-螺旋数字矩阵-Java-OD统一考试(C卷)
题目描述: 疫情期间,小明隔离在家,百无聊赖,在纸上写数字玩。他发明了一种写法: 给出数字个数n和行数m(0 < n ≤ 999,0 < m ≤ 999),从左上角的1开始,按照顺时针螺旋向内写方式,依次写出2,3...n,最终形成一个m行矩阵。 小明对这个矩阵有些要求: 1.每行数字的…...
红队打靶练习:PHOTOGRAPHER: 1
目录 信息收集 1、arp 2、nmap 3、nikto 目录扫描 1、gobuster 2、dirsearch WEB 信息收集 enum4linux smbclient 8000端口 CMS利用 信息收集 文件上传漏洞利用 提权 信息收集 get user.txt get flag 信息收集 1、arp ┌──(root㉿ru)-[~/kali] └─# a…...
【Linux】网络诊断 traceroute命令详解
目录 一、traceroute概述 1.1 traceroute命令简介 1.2 命令格式 1.3 原理 1.4 命令功能 二、使用实例 实例1:traceroute 用法简单、最常用的用法 实例2:跳数设置 实例3:设置探测数据包数量 实例4:显示IP地址,…...
c#cad 创建-圆(二)
运行环境 vs2022 c# cad2016 调试成功 一、代码说明 这段代码是一个AutoCAD插件,用于在模型空间中创建一个圆形。 首先,我们需要定义一个命令类CreateCircleCommand,并在命名空间CreateCircleInCad中声明。 在CreateCircleCommand类中&a…...
面试高频知识点:2线程 2.1.5如何自定义实现一个线程池
在Java中,线程池是一种用于管理线程的机制,它可以有效地管理多个线程并且可以重复使用它们,从而减少了线程创建和销毁的开销,提高了线程的利用率。本文将介绍如何自定义实现一个简单的线程池,并提供相应的Java代码示例…...
【stm32】hal库学习笔记-ADC模数转换(超详细)
【stm32】hal库学习笔记-ADC模数转换(超详细) 本篇章介绍了ADC实现电压检测的三种方式 ADC原理及选型 ADC将连续的模拟电压信号转换为二进制的数字信号 选型参数 速度(采样频率) 功耗 精度 转换原理 ADC hal库驱动函数 普通…...
蓝桥杯基础知识6 pair
蓝桥杯基础知识6 pair pair 的定义和结构:在C中,pair是一个模板类,用于表示一对值的组合,头文件<utility>。 pair类 的定义: template<class T1, class T2> struct pair{T1 first; // 第一个值T2 seco…...
后端返回给前端的数据格式有哪些?
后端返回的数据格式有很多种,常见的包括JSON、XML、HTML、CSV等。这些格式各有特点,适用于不同的应用场景。 JSON(JavaScript Object Notation):JSON是一种轻量级的数据交换格式,易于阅读和编写,…...
Transformer的PyTorch实现之若干问题探讨(一)
《Transformer的PyTorch实现》这篇博文以一个机器翻译任务非常优雅简介的阐述了Transformer结构。在阅读时存在一些小困惑,此处权当一个记录。 1.自定义数据中enc_input、dec_input及dec_output的区别 博文中给出了两对德语翻译成英语的例子: # S: de…...
系统参数SystemParameters.MinimumHorizontalDragDistance
SystemParameters.MinimumHorizontalDragDistance 是一个系统参数,它表示在拖放操作中鼠标水平移动的最小距离。 当用户按下鼠标左键并开始移动鼠标时,系统会检查鼠标的水平移动距离是否超过了 SystemParameters.MinimumHorizontalDragDistance。只有当…...
平屋顶安装光伏需要注意哪些事项?
我国对于房屋建设的屋顶形式,主要有平屋顶、斜屋顶、曲面屋顶和多波式折板屋顶等。今天来讲讲在平屋顶安装光伏,需要注意的事项。 1.屋顶结构:在安装光伏系统之前,需要对屋顶结构进行评估,确保屋顶能够承受光伏系统的…...
《Git 简易速速上手小册》第7章:处理大型项目(2024 最新版)
文章目录 7.1 Git Large File Storage (LFS)7.1.1 基础知识讲解7.1.2 重点案例:在 Python 项目中使用 Git LFS 管理数据集7.1.3 拓展案例 1:使用 Git LFS 管理大型静态资源7.1.4 拓展案例 2:优化现有项目中的大文件管理 7.2 性能优化技巧7.2.…...
从0开始学Docker ---Docker安装教程
Docker安装教程 本安装教程参考Docker官方文档,地址如下: https://docs.docker.com/engine/install/centos/ 1.卸载旧版 首先如果系统中已经存在旧的Docker,则先卸载: yum remove docker \docker-client \docker-client-latest…...
保姆级教程:手把手教你将若依(RuoYi)项目从Java 8迁移到Java 17(含Spring Boot 3升级)
保姆级教程:手把手教你将若依(RuoYi)项目从Java 8迁移到Java 17(含Spring Boot 3升级) 最近几年Java生态发生了翻天覆地的变化,从Java 8到Java 17不仅仅是版本号的跳跃,更是一次技术栈的全面革新。作为国内广泛使用的…...
OpenClaw技能开发指南:为ollama-QwQ-32B编写自定义模块
OpenClaw技能开发指南:为ollama-QwQ-32B编写自定义模块 1. 为什么需要自定义技能开发 上周我需要每天手动查询三个城市的天气数据来生成日报,这种重复劳动让我开始思考:能否让OpenClaw帮我自动完成?当我发现现有的天气技能包都不…...
从漏极、栅极到源极开关:手把手教你选对单端电荷泵拓扑(基于噪声与速度权衡)
从漏极、栅极到源极开关:单端电荷泵拓扑的噪声与速度权衡实战指南 在锁相环(PLL)设计中,电荷泵的性能往往成为整个系统相位噪声和杂散特性的瓶颈。特别是当设计目标同时包含低带内相位噪声和高开关速度时,单端电荷泵的拓扑选择就变得尤为关键…...
Eye-in-Hand还是Eye-to-Hand?深入解读OpenCV手眼标定背后的四种经典算法(Tsai, Park, Horaud)
Eye-in-Hand还是Eye-to-Hand?深入解读OpenCV手眼标定背后的四种经典算法 在工业机器人视觉引导系统中,相机与机械臂的精确标定直接决定了整个系统的定位精度。当工程师第一次调用OpenCV的calibrateHandEye()函数时,面对CALIB_HAND_EYE_TSAI、…...
拆解 OA 系统:从需求梳理到核心执行,新手一看就会
你是不是觉得公司的OA系统特别难用?报销要填八百个字段,不知道哪个是必填;请假批完还得自己跑去找下一个人;找一个去年的合同,得翻十几层文件夹。更气人的是,提了意见根本没人管,说系统改不了。…...
AntiDupl.NET:数字资产管理师的智能图片去重解决方案
AntiDupl.NET:数字资产管理师的智能图片去重解决方案 【免费下载链接】AntiDupl A program to search similar and defect pictures on the disk 项目地址: https://gitcode.com/gh_mirrors/an/AntiDupl 在当今视觉内容爆炸的时代,无论是专业摄影…...
如何通过Akagi提升麻将水平:从新手到高手的智能助手指南
如何通过Akagi提升麻将水平:从新手到高手的智能助手指南 【免费下载链接】Akagi A helper client for Majsoul 项目地址: https://gitcode.com/gh_mirrors/ak/Akagi 你是否在麻将对局中常常面临这样的困境:面对复杂牌局不知如何抉择?想…...
OpenClaw+GLM-4.7-Flash极客玩法:浏览器自动化与RPA任务融合
OpenClawGLM-4.7-Flash极客玩法:浏览器自动化与RPA任务融合 1. 当OpenClaw遇见GLM-4.7-Flash 去年冬天的一个深夜,我正为重复性的网页数据抓取任务头疼不已。Selenium脚本频繁因页面结构变化而崩溃,每次都需要人工介入调整。直到发现OpenCl…...
电脑c盘变红了怎么清理?C盘清理工具与方法
电脑c盘变红了怎么清理?问题不难解决,关键是选对方法工具!下面介绍实用的清理C盘方法,便于你解决C盘变红的问题哦! 关于C盘清理工具,给大家安排一款针对C盘爆满的清理神器---Windows - Cleaner,…...
为什么92%的候选人栽在FastAPI流式响应题上?——基于137份大厂AI后端面试记录的深度复盘
第一章:FastAPI 2.0流式响应的核心机制与演进脉络FastAPI 2.0 对流式响应(Streaming Response)进行了底层重构,将原先依赖 Starlette 的 StreamingResponse 封装升级为原生异步生成器驱动模型,并深度整合 ASGI 3.0 规范…...
