手写分布式存储系统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多年,并且也翻过它的代码,却连这个基本的点都不知晓。因此进一步验证上一篇的想法,就是很多东西真的要自己去实现一遍,否则只沉浸理论容易陷入一种“什么都懂”、“什么都是理所当然”的幻觉并自我感觉良好,但这很有可能会令我们的技术止步不前

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

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

相关文章

让IIS支持SSE (Server Sent Events)

本文只探讨IISPython网站的情况&#xff0c;对于asp.net也应该不用这么麻烦。 先上结论&#xff1a;用反向代理&#xff1a; IIS URL Rewrite waitress Waitress是一个纯python编写独立的WSGI服务器&#xff0c;功能比Gunicorn弱一些&#xff0c;但可以运行在windows平台上&…

【初识爬虫+requests模块】

爬虫又称网络蜘蛛、网络机器人。本质就是程序模拟人使用浏览器访问网站&#xff0c;并将需要的数据抓取下来。爬虫不仅能够使用在搜索引擎领域&#xff0c;在数据分析、商业领域都得到了大规模的应用。 URL 每一个URL指向一个资源&#xff0c;可以是一个html页面&#xff0c;一…

数字图像处理(实践篇)四十五 OpenCV-Python 使用ORB算法(包括算法概述)检测图像上的特征点

目录 一 ORB算法 二 涉及的函数 三 实践 ORB: An efficient alternative to SIFT or SURF SIFT 和 SURF 已获得专利,使用需要付费。但是ORB并不需要。ORB 最重要的一点是它来自“

【QT】opcuaServer 的构建

【QT】opcuaServer 的构建 前言opcuaServer实现测试 前言 在博文【opcua】从编译文件到客户端的收发、断连、节点查询等实现 中&#xff0c;我们已经介绍了如何在QT 中创建opucaClient 。在本期的博文中&#xff0c;我们基于之前的部署环境&#xff0c;介绍一下如何构建opcuaS…

Linux openKylin(开放麒麟)系统SSH服务安装配置与公网远程连接

文章目录 前言1. 安装SSH服务2. 本地SSH连接测试3. openKylin安装Cpolar4. 配置 SSH公网地址5. 公网远程SSH连接6. 固定SSH公网地址7. SSH固定地址连接8. 结语 前言 openKylin是中国首个基于Linux 的桌面操作系统开发者平台&#xff0c;通过开放操作系统源代码的方式&#xff…

【并发编程】原子累加器

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;并发编程 ⛺️稳重求进&#xff0c;晒太阳 JDK8之后有专门做累加的类&#xff0c;效率比自己做快数倍以上 累加器性能比较 参数是方法 // supplier 提供者 无中生有 ()->结果// func…

Android性能优化全攻略:让你的应用飞起来

Android性能优化全攻略&#xff1a;让你的应用飞起来 引言 在移动设备上&#xff0c;用户期望获得流畅、响应迅速的应用体验。对于Android开发者而言&#xff0c;优化应用的性能不仅能提升用户满意度&#xff0c;还能显著增加应用的市场竞争力。性能瓶颈可能来源于多方面&…

大卫·芬奇《消失的她》电影解读

《消失的爱人》&#xff08;Gone Girl&#xff09;是一部由大卫芬奇&#xff08;David Fincher&#xff09;执导的心理悬疑电影&#xff0c;改编自吉莉恩弗林&#xff08;Gillian Flynn&#xff09;的同名小说。这部影片于2014年上映&#xff0c;通过其精巧的剧本、紧张的氛围以…

如何在HA智能家居系统中添加HACS集成并实现异地控制家中苹果与小米设备

文章目录 基本条件一、下载HACS源码二、添加HACS集成三、绑定米家设备 ​ 上文介绍了如何实现群晖Docker部署HomeAssistant&#xff0c;通过内网穿透在户外控制家庭中枢。本文将介绍如何安装HACS插件商店&#xff0c;将米家&#xff0c;果家设备接入 Home Assistant。 基本条件…

for循环的多重跳出

for的多重跳出 1.前言2.标签使用3.使用异常的方式 本文在jdk17中测试通过 1.前言 前段时间面试时&#xff0c;面试官问我多重for循环如何跳出&#xff0c;我懵了&#xff0c;今天特别的研究了一下 本文主要说的不是continue与break&#xff0c;而是少用的另类操作 1.continue:…

《大魔界村》中的人物性格——亚瑟

《大魔界村》作为一款经典的街机动作游戏,其主角——勇敢的骑士亚瑟,以其独特的性格特点和坚定的信念,在玩家心中留下了深刻印象。本文将深入探讨亚瑟这一角色的性格特质,通过分析他在游戏中的行为表现及决策过程,展现他身上的勇气、坚韧与智慧三大要点。 一、无畏挑战的…

树型结构构建,模糊查询,过滤

一、前言 1、最近在做甘特图&#xff0c;有些需求和树型结构要求很大&#xff0c;看的是 pingCode&#xff0c;有搜索 2、还有抽取一部分树型结构的&#xff0c;如下是抽取上面的结构类型为需求的&#xff0c;重新组成树型 二、构建多颗树型结构 1、某些业务下&#xff0c;从…