当前位置: 首页 > news >正文

手写分布式存储系统v0.3版本

引言

承接 手写分布式存储系统v0.2版本 ,今天开始新的迭代开发。主要实现 服务发现功能

一、什么是服务发现

由于咱们的服务是分布式的,那从服务管理的角度来看肯定是要有一个机制来知道具体都有哪些实例可以提供服务。举个例子就是,张三家里在全国各地有不少火锅加盟店,那张三肯定要有一个方式知道这些火锅店加盟店的情况。例如上海又新开了一家加盟店,那么这家加盟店肯定要先通过某种方式联系张三,这样张三才能将配方以及食材供应给这家新的加盟店等等。

疑问

  1. 为什么不能通过域名映射的方式来做映射,客户端通过域名调用服务就好了为啥要专门做服务发现

    答:域名映射是对外提供服务时使用的,而我们的系统还有很多场景要做内部的服务管理,例如某个节点故障了,为了服务能够继续保证高可用,咱们的分布式存储系统就要将这个节点上所管理的数据分给其余的节点进行管理等,这个时候系统内部就需要明确知道各个分布式节点的信息。

二、服务发现设计

目前服务发现设计主要有以下几种

  • 配置化:将所有节点的信息写在服务配置里,像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);}
}

四、功能演示

整个功能验证逻辑如下

  1. 服务启动前观测zookeeper对应目录下不存在数据
    在这里插入图片描述

  2. 启动服务,从控制台能看到服务正常启动
    在这里插入图片描述

  3. 再观测zookeeper对应目录下注册了服务的主机名
    在这里插入图片描述

  4. 通过打印输出,能看到存在该目录下的服务信息(当前存的是测试样例数据)
    在这里插入图片描述

  5. 停止服务,并持续观测一段时间,可以看到目录已被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版本 &#xff0c;今天开始新的迭代开发。主要实现 服务发现功能 一、什么是服务发现 由于咱们的服务是分布式的&#xff0c;那从服务管理的角度来看肯定是要有一个机制来知道具体都有哪些实例可以提供服务。举个例子就是&#xff0c;张三家…...

除夕快乐!

打印的简单实现&#xff0c;祝大家新的一年万事顺意&#xff01; 龙年大吉&#xff01; #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函数实现的&#xff0c;在delay的过程中CPU要一直耗在这里不能去做别的事情。这是之前的缺点 (3)本节用定时器来定一个时间&#xff08;譬如0.3s&#xff09;&#xff0c;在这个定时器定时时间内…...

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原始套接字多线程负载均衡

原始套接字多线程负载均衡是一种在网络编程中常见的技术&#xff0c;特别是在高性能网络应用或网络安全工具中。这种技术允许应用程序在多个线程之间有效地分配和处理网络流量&#xff0c;提高系统的并发性能。以下是关于原始套接字多线程负载均衡技术的一些介绍&#xff1a; …...

leetcode (算法)66.加一(python版)

需求 给定一个由 整数 组成的 非空 数组所表示的非负整数&#xff0c;在该数的基础上加一。 最高位数字存放在数组的首位&#xff0c; 数组中每个元素只存储单个数字。 你可以假设除了整数 0 之外&#xff0c;这个整数不会以零开头。 示例 1&#xff1a; 输入&#xff1a;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&#xff1a;traceroute 用法简单、最常用的用法 实例2&#xff1a;跳数设置 实例3&#xff1a;设置探测数据包数量 实例4&#xff1a;显示IP地址&#xff0c…...

c#cad 创建-圆(二)

运行环境 vs2022 c# cad2016 调试成功 一、代码说明 这段代码是一个AutoCAD插件&#xff0c;用于在模型空间中创建一个圆形。 首先&#xff0c;我们需要定义一个命令类CreateCircleCommand&#xff0c;并在命名空间CreateCircleInCad中声明。 在CreateCircleCommand类中&a…...

面试高频知识点:2线程 2.1.5如何自定义实现一个线程池

在Java中&#xff0c;线程池是一种用于管理线程的机制&#xff0c;它可以有效地管理多个线程并且可以重复使用它们&#xff0c;从而减少了线程创建和销毁的开销&#xff0c;提高了线程的利用率。本文将介绍如何自定义实现一个简单的线程池&#xff0c;并提供相应的Java代码示例…...

【stm32】hal库学习笔记-ADC模数转换(超详细)

【stm32】hal库学习笔记-ADC模数转换&#xff08;超详细&#xff09; 本篇章介绍了ADC实现电压检测的三种方式 ADC原理及选型 ADC将连续的模拟电压信号转换为二进制的数字信号 选型参数 速度&#xff08;采样频率&#xff09; 功耗 精度 转换原理 ADC hal库驱动函数 普通…...

蓝桥杯基础知识6 pair

蓝桥杯基础知识6 pair pair 的定义和结构&#xff1a;在C中&#xff0c;pair是一个模板类&#xff0c;用于表示一对值的组合&#xff0c;头文件<utility>。 pair类 的定义&#xff1a; template<class T1, class T2> struct pair{T1 first; // 第一个值T2 seco…...

后端返回给前端的数据格式有哪些?

后端返回的数据格式有很多种&#xff0c;常见的包括JSON、XML、HTML、CSV等。这些格式各有特点&#xff0c;适用于不同的应用场景。 JSON&#xff08;JavaScript Object Notation&#xff09;&#xff1a;JSON是一种轻量级的数据交换格式&#xff0c;易于阅读和编写&#xff0c…...

Transformer的PyTorch实现之若干问题探讨(一)

《Transformer的PyTorch实现》这篇博文以一个机器翻译任务非常优雅简介的阐述了Transformer结构。在阅读时存在一些小困惑&#xff0c;此处权当一个记录。 1.自定义数据中enc_input、dec_input及dec_output的区别 博文中给出了两对德语翻译成英语的例子&#xff1a; # S: de…...

系统参数SystemParameters.MinimumHorizontalDragDistance

SystemParameters.MinimumHorizontalDragDistance 是一个系统参数&#xff0c;它表示在拖放操作中鼠标水平移动的最小距离。 当用户按下鼠标左键并开始移动鼠标时&#xff0c;系统会检查鼠标的水平移动距离是否超过了 SystemParameters.MinimumHorizontalDragDistance。只有当…...

平屋顶安装光伏需要注意哪些事项?

我国对于房屋建设的屋顶形式&#xff0c;主要有平屋顶、斜屋顶、曲面屋顶和多波式折板屋顶等。今天来讲讲在平屋顶安装光伏&#xff0c;需要注意的事项。 1.屋顶结构&#xff1a;在安装光伏系统之前&#xff0c;需要对屋顶结构进行评估&#xff0c;确保屋顶能够承受光伏系统的…...

《Git 简易速速上手小册》第7章:处理大型项目(2024 最新版)

文章目录 7.1 Git Large File Storage (LFS)7.1.1 基础知识讲解7.1.2 重点案例&#xff1a;在 Python 项目中使用 Git LFS 管理数据集7.1.3 拓展案例 1&#xff1a;使用 Git LFS 管理大型静态资源7.1.4 拓展案例 2&#xff1a;优化现有项目中的大文件管理 7.2 性能优化技巧7.2.…...

从0开始学Docker ---Docker安装教程

Docker安装教程 本安装教程参考Docker官方文档&#xff0c;地址如下&#xff1a; https://docs.docker.com/engine/install/centos/ 1.卸载旧版 首先如果系统中已经存在旧的Docker&#xff0c;则先卸载&#xff1a; yum remove docker \docker-client \docker-client-latest…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…...

设计模式和设计原则回顾

设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

dedecms 织梦自定义表单留言增加ajax验证码功能

增加ajax功能模块&#xff0c;用户不点击提交按钮&#xff0c;只要输入框失去焦点&#xff0c;就会提前提示验证码是否正确。 一&#xff0c;模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

质量体系的重要

质量体系是为确保产品、服务或过程质量满足规定要求&#xff0c;由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面&#xff1a; &#x1f3db;️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限&#xff0c;形成层级清晰的管理网络&#xf…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

Spring数据访问模块设计

前面我们已经完成了IoC和web模块的设计&#xff0c;聪明的码友立马就知道了&#xff0c;该到数据访问模块了&#xff0c;要不就这俩玩个6啊&#xff0c;查库势在必行&#xff0c;至此&#xff0c;它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据&#xff08;数据库、No…...

深度学习习题2

1.如果增加神经网络的宽度&#xff0c;精确度会增加到一个特定阈值后&#xff0c;便开始降低。造成这一现象的可能原因是什么&#xff1f; A、即使增加卷积核的数量&#xff0c;只有少部分的核会被用作预测 B、当卷积核数量增加时&#xff0c;神经网络的预测能力会降低 C、当卷…...

10-Oracle 23 ai Vector Search 概述和参数

一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI&#xff0c;使用客户端或是内部自己搭建集成大模型的终端&#xff0c;加速与大型语言模型&#xff08;LLM&#xff09;的结合&#xff0c;同时使用检索增强生成&#xff08;Retrieval Augmented Generation &#…...

接口自动化测试:HttpRunner基础

相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具&#xff0c;支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议&#xff0c;涵盖接口测试、性能测试、数字体验监测等测试类型…...