唐山网站建设唐山,网站建设卖点,每天看七个广告赚40元的app,网站建设发好处引言
承接 手写分布式存储系统v0.2版本 #xff0c;今天开始新的迭代开发。主要实现 服务发现功能
一、什么是服务发现
由于咱们的服务是分布式的#xff0c;那从服务管理的角度来看肯定是要有一个机制来知道具体都有哪些实例可以提供服务。举个例子就是#xff0c;张三家…引言
承接 手写分布式存储系统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);ListString 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);ListString 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 ListString getChildren(String path) throws KeeperException, InterruptedException{ListString 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 ListACL 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 dont exist// create mode is persistent since ephemeral nodes cant 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多年并且也翻过它的代码却连这个基本的点都不知晓。因此进一步验证上一篇的想法就是很多东西真的要自己去实现一遍否则只沉浸理论容易陷入一种“什么都懂”、“什么都是理所当然”的幻觉并自我感觉良好但这很有可能会令我们的技术止步不前