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

文章目录

  • 主从架构介绍
  • 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,一经查实,立即删除!

相关文章

斯坦福的新工具,生物计算,操作系统与AI融合之路

一支烟花官网&#xff1a; https://agifun.love 智源社区 斯坦福让“GPU高速运转”的新工具火了&#xff0c;比FlashAttention2更快 西风 发自 凹非寺量子位 | 公众号 QbitAIAI算力资源越发紧张的当下&#xff0c;斯坦福新研究将GPU运行效率再提升一波——内核只有100行代码…

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绘图的人来…

淘宝/1688获得店铺的所有商品(商品列表)

通过以下步骤&#xff0c;可以获得淘宝或1688店铺的所有商品。请注意&#xff0c;具体步骤可能会因为平台的更新而有所改变&#xff0c;可以根据实际情况进行操作。 更多API调用展示以及获取Key和secret请移步 返回数据格式&#xff1a; {"user": null,"ite…

python 简单demo

import pyttsx3def read_poem_with_voice(filename):"""使用女声从文本文件中读取古诗词并朗读:param file_path: 文本文件的路径"""engine pyttsx3.init() # 初始化语音引擎# 设置语速&#xff08;范围通常是50-200&#xff09;engine.setPro…

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

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

c++_0基础_讲解4 变量定义

变量 C 中的变量是存储数据值的容器&#xff0c;这些值可以在程序执行过程中被修改和使用。在 C 中&#xff0c;变量必须先声明后使用&#xff0c;声明变量也可以称之为定义变量&#xff0c;它告诉编译器在何处以及如何去分配存储空间。接下来我将对 C 中的变量定义进行详细的介…

常见的 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;我们可以轻松地实现这一…

事务中存在多线程,怎么处理?

在 Spring 框架中&#xff0c;Transactional 注解作为一种声明式事务管理的关键机制&#xff0c;其背后的工作原理远比简单的 AOP&#xff08;面向切面编程&#xff09;和 ThreadLocal 存储更为细腻。该注解的实现核心在于 Spring 的 TransactionInterceptor&#xff08;事务拦…

图片转Base64

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

项目中常量的定义方式

方式一 在常量个数少的时候&#xff0c;通常情况下使用这种方式。 public class MqConstants {public static final String EXCHANGE_1 "exchange1";public static final String EXCHANGE_2 "exchange2";public static final String EXCHANGE_3 "…

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…

全面对比DAMO ModelScope OCR与Paddle OCR技术

本文深入比较了DAMO ModelScope OCR与Paddle OCR在功能、性能、应用场景及技术细节上的差异&#xff0c;旨在为读者提供全面的OCR技术选型参考。 文章目录 引言OCR技术的重要性概述DAMO ModelScope和Paddle OCRDAMO ModelScope OCRPaddle OCR 技术背景与概述自底向上的文字检测…

Linux 基本指令3

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