【服务的主从切换实现原理】

文章目录

  • 主从架构介绍
  • zookeeper
  • 利用ZK实现主从架构

主从架构介绍

主从服务架构是一种常见的分布式系统设计模式,常用于提高系统的性能、可用性和扩展性。在这种架构中,系统中的节点被分为两类:主节点(Master)和从节点(Slave)。
在这里插入图片描述

zookeeper

zookeeper有以下几个特点:
1.集群部署:一般是3~5台机器组成一个集群,每台机器都在内存保存了zk的全部数据,机器之间互相通信同步数据,客户端连接任何一台机器都可以。
2.顺序一致性:所有的写请求都是有序的;集群中只有leader机器可以写,所有机器都可以读,所有写请求都会分配一个zk集群全局的唯一递增编号:zxid,用来保证各种客户端发起的写请求都是有顺序的。
3.原子性:要么全部机器成功,要么全部机器都不成功。
4.数据一致性:无论客户端连接到哪台节点,读取到的数据都是一致的;leader收到了写请求之后都会同步给其他机器,保证数据的强一致,你连接到任何一台zk机器看到的数据都是一致的。
5.高可用:如果某台机器宕机,会保证数据不丢失。集群中挂掉不超过一半的机器,都能保证集群可用。比如3台机器可以挂1台,5台机器可以挂2台。
6.实时性:一旦数据发生变更,其他节点会实时感知到。
7.高性能:每台zk机器都在内存维护数据,所以zk集群绝对是高并发高性能的,如果将zk部署在高配置物理机上,一个3台机器的zk集群抗下每秒几万请求是没有问题的。
8.高并发:高性能决定的,主要是基于纯内存数据结构来处理,并发能力是很高的,只有一台机器进行写,但是高配置的物理机,比如16核32G,可以支撑几万的写入QPS。所有机器都可以读,选用3台高配机器的话,可以支撑十万+的QPS。

利用ZK实现主从架构

导入依赖

<properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version><zookeeper.version>3.4.14</zookeeper.version><curator-framework.version>2.12.0</curator-framework.version><curator-recipes.version>2.12.0</curator-recipes.version><ssdb.version>9.4</ssdb.version><jodatime.version>2.10</jodatime.version><binlog.version>0.21.0</binlog.version><disruptor.version>3.4.2</disruptor.version></properties><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions><!-- 去除旧log依赖 --><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-log4j2 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j2</artifactId></dependency><!-- log4j2异步日志需要加载disruptor-3.0.0.jar或者更高的版本 --><dependency><groupId>com.lmax</groupId><artifactId>disruptor</artifactId><version>3.4.2</version></dependency><!-- zookeeper --><dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId><version>${zookeeper.version}</version></dependency><dependency><groupId>org.apache.curator</groupId><artifactId>curator-framework</artifactId><version>${curator-framework.version}</version></dependency><dependency><groupId>org.apache.curator</groupId><artifactId>curator-recipes</artifactId><version>${curator-recipes.version}</version></dependency>

zk客户端:

public class ZookeeperClient {/*** 客户端*/private CuratorFramework client;/*** Leader选举*/private LeaderLatch leader;public ZookeeperClient(LeaderLatch leader,CuratorFramework client){this.client = client;this.leader = leader;}/*** 启动客户端* @throws Exception*/public void startZKClient() throws Exception {client.start();leader.start();}/*** 关闭客户端* @throws Exception*/public void closeZKClient() throws Exception {leader.close();client.close();}/*** 判断是否变为领导者* @return*/public boolean hasLeadership(){return leader.hasLeadership();}public CuratorFramework getClient() {return client;}public void setClient(CuratorFramework client) {this.client = client;}public LeaderLatch getLeader() {return leader;}public void setLeader(LeaderLatch leader) {this.leader = leader;}
public class ZookeeperClientInfo {/*** 是否是leader 默认为false*/public static boolean isLeader = false;/*** 客户端ID*/private String id;/*** 连接信息字符串*/private String connectString;/*** 节点路径*/private String path;/*** 连接超时时间*/private Integer connectTimeOut;/*** 最大连接次数*/private Integer maxRetries;/*** 重连休眠时间*/private Integer retrySleepTime;public static boolean isLeader() {return isLeader;}public static void setIsLeader(boolean isLeader) {ZookeeperClientInfo.isLeader = isLeader;}public String getId() {return id;}public void setId(String id) {this.id = id;}public String getConnectString() {return connectString == null ? null : connectString.replaceAll("//s+", "");}public void setConnectString(String connectString) {this.connectString = connectString;}public String getPath() {return path;}public void setPath(String path) {this.path = path;}public Integer getConnectTimeOut() {return connectTimeOut;}public void setConnectTimeOut(Integer connectTimeOut) {this.connectTimeOut = connectTimeOut;}public Integer getMaxRetries() {return maxRetries;}public void setMaxRetries(Integer maxRetries) {this.maxRetries = maxRetries;}@Overridepublic String toString() {StringBuilder sb = new StringBuilder();sb.append("ZookeeperClientInfo{ ").append("id=").append(id).append(",isLeader=").append(isLeader).append(", connectString=").append(connectString).append(", path=").append(path).append(",connectTimeOut=").append(connectTimeOut).append(", maxRetries=").append(maxRetries).append(", retrySleepTime=").append(retrySleepTime).append(" }");return sb.toString();}
}

监听器,来监听主节点变更,实现主要是继承LeaderLatchListener类

@Component
public class ZookeeperClientListener implements LeaderLatchListener {private final static Logger log = LoggerFactory.getLogger(ZookeeperClientListener.class);@Autowiredprivate ChangeLeaderService changeLeaderService;/***  将本服务达成jar包,部署到2台服务器上。启动两个服务。*  1、第一台服务(机器1)启动后抢到leader,会进入到该方法中。另外一台服务(机器2)会进入到notLeader()中。*  2、当机器1宕机后,连接断开后zookeeper会删除临时节点。机器2根据选举会成为leader,成为leader后会进入到isLeader()中*     然后在changeLeaderService.taskExecut() 再次将定时任务做补偿处理。*/@Overridepublic void isLeader() {log.error("{},当前服务已变为leader,将从事业务消费======>>>>", JodaDateUtil.date2String(new Date()));ZookeeperClientInfo.isLeader = true;// 切换机器后,继续执行上一个机器未完成的定时任务。changeLeaderService.taskExecut();}@Overridepublic void notLeader() {log.error("{},当前服务已退出leader,不再从事消费业务=====>>>", JodaDateUtil.date2String(new Date()));ZookeeperClientInfo.isLeader = false;}
}

当服务主从切换时的补偿措施:

public interface ChangeLeaderService {/*** 主从服务切换时,手动触发分配到leader机器上的定时任务中的业务逻辑*/void taskExecut();
}
@Service
public class ChangeLeaderServiceImpl implements ChangeLeaderService {private final static Logger log = LoggerFactory.getLogger(ChangeLeaderServiceImpl.class);
;@Autowiredprivate SSDB ssdb;@Overridepublic void taskExecut() {//TODO 从ssdb 中查询出定时任务的标识,是否正常执行完成,未完成的话,在这里再触发执行。log.info("===ChangeLeaderServiceImpl===taskExecut()===");/*Response response = ssdb.get(Constant.TEST_STATE);if (response.ok() && response.datas.size() > 0) {int tenantState = byteArrayToInt(response.datas.get(0));if (Constant.TASK_EXECUTING == tenantState) { //当前任务未完成,接着完成// service.do(); 伪代码log.info("service.do();");ssdb.set(Constant.TEST_STATE, Constant.TASK_END);}}*/}/*** byte数组转int* @param b* @return*/private int byteArrayToInt(byte[] b){String str = byteArrayToString(b);return StringToInt(str);}/*** byte数组转string* @param b* @return*/private String byteArrayToString(byte[] b) {if (null == b || b.length == 0) {return "";}return new String(b);}/*** string转int* @param str* @return*/private int StringToInt(String str){if (StringUtils.isEmpty(str)){return 0;}return Integer.parseInt(str);}
}

zk的配置信息

@Component
public class ZookeeperConfig {/*** zk 地址*/@Value("${spring.slaveof.zk.addr}")private String addr;/*** 重试策略----最大重试次数*/@Value("${spring.slaveof.zk.max}")private int max;/*** 重试策略-----sleepTime*/@Value("${spring.slaveof.zk.sleep}")private int sleepTime;/*** 连接超时时间*/@Value("${spring.slaveof.zk.connection}")private int connectionTime;/*** 会话超时时间*/@Value("${spring.slaveof.zk.session}")private int sessionTime;public String getAddr() {return addr;}public int getMax() {return max;}public int getSleepTime() {return sleepTime;}public int getConnectionTime() {return connectionTime;}public int getSessionTime() {return sessionTime;}
}

服务启动限制性的类,了解ApplicationRunner,SpringBoot项目启动时,若想在启动之后直接执行某一段代码,就可以用 ApplicationRunner这个接口,并实现接口里面的run(ApplicationArguments args)方法,方法中写上自己的代码逻辑。也就是:spring容器启动完成之后,就会紧接着执行这个接口实现类的run方法。

@Component
public class ZkDemoApplicationRunner implements ApplicationRunner {private final static Logger log = LoggerFactory.getLogger(ZkDemoApplicationRunner.class);@Autowiredprivate ZookeeperClientListener zkClientListener;@Autowiredprivate ZookeeperConfig zookeeperConfig;@Overridepublic void run(ApplicationArguments applicationArguments) throws Exception {log.info("====================>>>>>>>>启动执行zk>>>>>>>>==================");log.error("===>>>>>>>>zookeeper: addr:{}, sleepTime:{}, max:{}, connectionTime:{}=====", zookeeperConfig.getAddr(), zookeeperConfig.getSleepTime(), zookeeperConfig.getMax(), zookeeperConfig.getConnectionTime());CuratorFramework client = CuratorFrameworkFactory.builder().connectString(zookeeperConfig.getAddr()).retryPolicy(new ExponentialBackoffRetry(zookeeperConfig.getSleepTime(), zookeeperConfig.getMax())).connectionTimeoutMs(zookeeperConfig.getConnectionTime()).build();LeaderLatch leaderLatch = new LeaderLatch(client, "/diaoliwei", "client1", LeaderLatch.CloseMode.NOTIFY_LEADER);if (zkClientListener == null) {log.error("==================>>>>>>>>>>>>>>>>zkClientListener is null=====>>>>>>>>>>>");}leaderLatch.addListener(zkClientListener);ZookeeperClient zkClient = new ZookeeperClient(leaderLatch, client);try {zkClient.startZKClient();} catch (Exception e) {log.error("======>>>>>>zk客户端连接失败<<<<<=====error:{}===", e);return;}CuratorFrameworkState state = client.getState();if (CuratorFrameworkState.STOPPED == state) {log.error("zk客户端已关闭");return;}/*while (true) {  // 测试日志用try {if(!zkClient.hasLeadership()){log.info("2当前服务不是leader");Thread.sleep(2000);log.error("error:::::::Test02 do it...>>>>>>> ");continue;}  else {log.info("2当前服务是leader");}log.info("Test02 do it... ");log.error("Test02 do it...>>>>>>> ");} catch (Exception e) {log.error("Exception=====>>>>>>>>>>>>eeee:", e);}}*///log.info("======>>>>>zk客户端连接成功<<<<<<=======");}
}

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

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

相关文章

Java基础面试重点-1

0. 符号&#xff1a; *&#xff1a;记忆模糊&#xff0c;验证后特别标注的知识点。 &&#xff1a;容易忘记知识点。 *&#xff1a;重要的知识点。 1. 简述一下Java面向对象的基本特征&#xff08;四个&#xff09;&#xff0c;以及你自己的应用&#xff1f; 抽象&#…

R可视化:ggpubr包学习

欢迎大家关注全网生信学习者系列&#xff1a; WX公zhong号&#xff1a;生信学习者 Xiao hong书&#xff1a;生信学习者 知hu&#xff1a;生信学习者 CDSN&#xff1a;生信学习者2 介绍 ggpubr是我经常会用到的R包&#xff0c;它傻瓜式的画图方式对很多初次接触R绘图的人来…

提升你的编程体验:自定义 PyCharm 背景图片

首先&#xff0c;打开 PyCharm 的设置菜单&#xff0c;点击菜单栏中的 File > Settings 来访问设置&#xff0c;也可以通过快捷键 CtrlAItS 打开设置。 然后点击Appearance & Behavior > Appearance。 找到Background image...左键双击进入。 Image:传入自己需要设置…

常见的 EVM 版本以及它们的区别

EVM&#xff08;以太坊虚拟机&#xff09;版本的演进是为了引入新的特性和改进以太坊平台的安全性、效率和功能性。每个版本通常伴随着以太坊网络的硬分叉&#xff0c;这是以太坊协议的重大升级。以下是一些常见的EVM版本及其主要区别&#xff1a; Homestead (2016年3月)&…

中国首台!紧随美国,重磅发布100比特中性原子量子计算机

2024年6月11日上午&#xff0c;“武汉量子论坛—2024”隆重开幕&#xff0c;国家自然科学基金委员会主任窦贤康院士&#xff0c;武汉大学校长张平文院士&#xff0c;以及叶朝辉、徐红星、祝世宁等院士出席大会。在会议上&#xff0c;中科酷原重磅发布国内首台原子量子计算机——…

利用 HTML5 Canvas 实现在线签字功能

目录 前言 一、HTML5 Canvas 简介 二、签字功能的实现 效果演示 完整代码 前言 在现代互联网应用中&#xff0c;有时我们需要让用户在网页上进行签字操作&#xff0c;比如确认文件、填写电子表格或者签署合同。利用 HTML5 的 canvas 画布&#xff0c;我们可以轻松地实现这一…

图片转Base64

在Python中, 可以使用内置的base64模块以及图像处理库(如PIL, 也称为Pillow)来将图片转换为Base64编码的字符串. 以下是一个简单的示例, 说明如何实现这一过程:首先, 需要安装Pillow库(如果尚未安装), 可以使用pip来安装: pip install pillow然后, 可以使用以下Python代码将图片…

RabbitMQ实践——交换器(Exchange)和绑定(Banding)

大纲 direct型交换器默认交换器命名交换器 fanout型交换器topic型交换器headers型交换器 RabbitMQ在概念上由三部分组成&#xff1a; 交换器&#xff08;Exchange&#xff09;&#xff1a;负责接收消息发布者发布消息的结构&#xff0c;同时它会根据“绑定关系”&#xff08;Ba…

基于SpringBoot+VueBBS论坛系统设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝1W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f; 感兴趣的可以先收藏起来&#xff0c;还…

sku与spu的区别!!!

一个 SPU 可以有多个 SKU。

攻防演练之-成功的钓鱼邮件溯源

书接上文&#xff0c;《网络安全攻防演练风云》专栏之攻防演练之-网络安全产品大巡礼二&#xff0c;这里。 演练第一天并没有太大的波澜&#xff0c;白天的时间过得很快。夜色降临&#xff0c;攻防演练中心内的灯光依旧明亮。对于网络安全团队来说&#xff0c;夜晚和白天并没有…

教育培训知识付费在线课程小程序开发

教育培训知识付费在线课程小程序功能概述 核心功能 课程报名与缴费&#xff1a;支持线上报名、缴费&#xff0c;自定义课程时间、人数等。 砍价功能&#xff1a;用户通过分享邀请好友参与砍价&#xff0c;享受低价购买课程的优惠。 视频课程&#xff1a;支持倍速播放&#x…

Linux 基本指令3

date指令 date[选项][格式] %Y--年 %m--月 %d--日 %H--小时 %M--分 %S--秒 中间可用其他符号分割&#xff0c;不能使用空格。 -s 设置时间&#xff0c;会返回设置时间的信息并不是改变当前时间 设置全部时间年可用-或者&#xff1a;分割日期和时间用空格分隔&#xff…

Qt创建静态库及静态库使用

Qt创建静态库及静态库使用 1. 创建一个库文件 选择静态库 将需要打包的.h 和.cpp文件添加到程序中&#xff0c; 在编译器版本下的debug和release模式下分别编译&#xff08;右键项目&#xff0c;点击“qmake”,再点击“构建”&#xff09;后&#xff0c;在对应的的build目录下…

“探索机器学习的多面世界:从理论到应用与未来展望“

博客主页&#xff1a;誓则盟约系列专栏&#xff1a;机器学习 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 目录 一、机器学习基础理论 1.机器学习的定义与分类 监督学习 无监督学…

Python数据分析与机器学习在医疗诊断中的应用

文章目录 &#x1f4d1;引言一、数据收集与预处理1.1 数据收集1.2 数据预处理 二、特征选择与构建2.1 特征选择2.2 特征构建 三、模型选择与训练3.1 逻辑回归3.2 随机森林3.3 深度学习 四、模型评估与调优4.1 交叉验证4.2 超参数调优 五、模型部署与应用5.1 模型保存与加载5.2 …

Python:从头创建 Asyncio (2)

引言 现在&#xff0c;asyncio 已成为 Python 社区中的热门话题&#xff0c;并且名副其实——它提供了一种非常出色的处理 I/O 密集型程序的方法&#xff01;在我探索 asyncio 的过程中&#xff0c;我起初并不太明白它的工作原理。但随着深入学习&#xff0c;我意识到 asyncio …

Java 反射机制 -- Java 语言反射的概述、核心类与高级应用

大家好,我是栗筝i,这篇文章是我的 “栗筝i 的 Java 技术栈” 专栏的第 010 篇文章,在 “栗筝i 的 Java 技术栈” 这个专栏中我会持续为大家更新 Java 技术相关全套技术栈内容。专栏的主要目标是已经有一定 Java 开发经验,并希望进一步完善自己对整个 Java 技术体系来充实自…

GitLab教程(二):快速上手Git

文章目录 1.将远端代码克隆到本地2.修改本地代码并提交到远程仓库3.Git命令总结git clonegit statusgit addgit commitgit pushgit log 首先&#xff0c;我在Gitlab上创建了一个远程仓库&#xff0c;用于演示使用Gitlab进行版本管理的完整流程&#xff1a; 1.将远端代码克隆到本…