基于zookeeper实现服务节点HA主备自动切换

文章目录

  • 前言
  • 一、架构图和流程图
  • 二、流程说明
    • 1.服务启动初始化ZK、注册所有服务节点信息-MasterRegister
    • 2.创建、运行服务节点,并管理服务节点-LeaderSelectorZkClient。
    • 3.典型场景-调度服务单体执行-DigitalEmpTask
  • 总结
  • 参考


前言

Spring Boot 主备切换可以采用数据库的主从同步、Zookeeper选举、Redis Sentinel等技术实现高可用。

其中,数据库的主从同步可以通过配置数据库的主从复制来实现。在主节点出现故障时,从节点可以自动接管并成为新的主节点。这种方式实现简单,但需要手动配置主从复制。

Zookeeper选举可以利用Zookeeper的特性来实现,即在Zookeeper上创建一个临时节点作为选举的标志,节点创建成功的服务就是主节点,其他服务则是备节点。在主节点出现故障时,Zookeeper会重新选举一个新的主节点。这种方式实现相对较为复杂,但具有更好的灵活性和可扩展性。

Redis Sentinel是Redis提供的一种高可用性解决方案,可以自动完成主从切换,同时具有自动故障检测和恢复等功能。Redis Sentinel需要在多个节点上运行,并且可以配置多个从节点来实现数据备份和故障转移。当主节点故障时,Redis Sentinel会自动将其中一个从节点升级为新的主节点,保证服务的高可用性。


一、架构图和流程图

在这里插入图片描述
说明:
  主+备模式中有1个主服务节点、多个备服务节点,由主服务节点向外提供服务,备服务节点监听主机状态,一旦主服务节点宕机,备服务节点速接管主服务继续向外提供服务。
  通过Zookeeper(集群)服务注册/发现特性完成主备切换;

  • 1-工作服务器启动时,各服务节点在ZooKeeper的Servers节点下创建临时节点,并把基本信息写入临时节点,完成注册;
  • 2-各服务节点实时监听Servers节点的子节点列表,并尝试创建Master临时节点,谁创建成功谁就是Master,其他的服务节点就作为Slave
  • 3-所有的服务节点关注Master节点的删除事件,通过监听Master节点的删除事件来体现Master服务器是否宕机(创建临时节点的服务器一旦宕机,它所创建的临时节点即会自动删除)
  • 4-.一旦Master服务器宕机,其它服务节点开始新一轮的Master选举,计算新的Master服务器。

二、流程说明

1.服务启动初始化ZK、注册所有服务节点信息-MasterRegister

代码如下(示例):

package com.merak.hyper.automation.zk;
import com.merak.hyper.automation.util.ZkHelper;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/*** @author harry* @version 1.0* @ClassName: ZkMasterRegister* @description: zk主备MasterRegister:启动时初始化ZK、注册所有服务节点信息*/
@Component
@Order(1)
public class ZkMasterRegister implements CommandLineRunner {public static final Logger log = LoggerFactory.getLogger(ZkMasterRegister.class);@Value("${zk_master.status}")private String status;@Value("${zk_master.serviceurl}")private String serviceurl;@Overridepublic void run(String... args) {if( ZkHelper.getInstance().zookeeperOpen(status) ) {String[] workServerArr = ZkHelper.getInstance().workServerInfo();if (!StringUtils.isBlank(workServerArr[0]) && !StringUtils.isBlank(serviceurl)) {LeaderSelectorZkClient.getInstance().initZk(serviceurl, workServerArr[0], workServerArr[1], workServerArr[2], ZkHelper.getInstance().zkMasterPath());log.info("程序启动,初始化ZK、注册服务节点等信息!");} else {log.warn("参数未配置zookeeper服务器的地址[sys.zookeeper.serviceurl],请检查!");//ip 和 name}}else{log.warn("当前调度服务为单节点服务,未配置zookeeper服务器");}}
}

2.创建、运行服务节点,并管理服务节点-LeaderSelectorZkClient。

工作服务器节点的基本信息
每个分布式服务节点基本信息包括:serviceIp、servicePort和name, 确保分布式服务节点的唯一性。

package com.merak.hyper.automation.zk;
import java.io.Serializable;/*** 工作服务器节点的基本信息*/
public class RunningData implements Serializable {private static final long serialVersionUID = 4260577459043203630L;private String serviceIp;private String servicePort;private String name;public String getServiceIp() {return serviceIp;}public void setServiceIp(String serviceIp) {this.serviceIp = serviceIp;}public String getServicePort() {return servicePort;}public void setServicePort(String servicePort) {this.servicePort = servicePort;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "RunningData{" +"serviceIp='" + serviceIp + '\'' +", servicePort='" + servicePort + '\'' +", name='" + name + '\'' +'}';}
}

创建、运行服务节点,并管理服务节点
代码如下(示例):

/*** @description: ZOOKEEPER_SERVER连接和服务节点管理* @param: [zookeeper_server, session_connection_timeout, serviceIp, serviceName] * @return: void*/public  void initZk(String zookeeper_server,String serviceIp, String serviceName, String servicePort,String zkMasterName) {try {log.info("创建服务器节点["+serviceIp+","+serviceName+"]开始!");//创建zkClientclient = ZkConnect.getInstance().connectZkSever(zookeeper_server);//创建serverDatarunningData = new RunningData();runningData.setServiceIp(serviceIp);runningData.setName(serviceName);runningData.setServicePort(servicePort);//创建服务workServer = new WorkServer(runningData,zkMasterName);workServer.setZkClient(client);workServer.start();log.info("创建服务器节点["+serviceIp+","+serviceName+"]结束!");} catch (Exception e) {log.error("zookeeper_server init error,msg=" + e.getMessage());} finally {log.info("zookeeper_server finally ...");}}

服务节点启动、订阅Master节点删除事件、争抢Master权利成为master节点
代码片断如下:

    //初始化工作服务器WorkServer信息public WorkServer(RunningData rd, String zkMasterName) {this.serverData = rd; // 记录服务器基本信息this.MASTER_PATH = zkMasterName;this.dataListener = new IZkDataListener() {public void handleDataDeleted(String dataPath) {//master切换时需要重置 调度云托管任务表 schedule_status = initzkResetScheduleStatus.switchResetScheduleStatus();log.info(dataPath + "路径已经删除,开始新一轮Master抢占");if (masterData != null && masterData.getName().equals(serverData.getName())&& masterData.getServiceIp().equals(serverData.getServiceIp())&& masterData.getServicePort().equals(serverData.getServicePort())) {takeMaster();//自己就是上一轮的Master服务器,则直接抢} else {//否则延迟5秒后再抢。应对网络抖动给上一轮的Master服务器优先抢占master的权利,避免不必要的数据迁移开销delayExecutor.schedule(new Runnable() {public void run() {log.info("服务器开始抢占Master权利");takeMaster();}}, delayTime, TimeUnit.SECONDS);}}public void handleDataChange(String dataPath, Object data) {log.info("IZkDataListener - handleDataChange,dataPath=" + dataPath + ",data=" + data.toString());}};}.....// 1 启动服务器public void start() throws Exception {if (running) {throw new Exception("server has startup...");}running = true;// 2 订阅Master节点删除事件zkClient.subscribeDataChanges(MASTER_PATH, dataListener);// 3 争抢Master权利takeMaster();}.....// 争抢Masterprivate void takeMaster() {if (!running)return;try {if (!zkClient.exists(MASTER_PATH)) {// 尝试创建Master临时节点zkClient.create(MASTER_PATH, serverData, CreateMode.EPHEMERAL);masterData = serverData;log.info("服务器节点[" + serverData.getServiceIp() + "," + serverData.getName() + "," + serverData.getServicePort() + "]争抢Master成功,成为master[isMaster]!");} else {// 已被其他服务器创建了,读取Master节点信息RunningData runningData = zkClient.readData(MASTER_PATH, true);log.info("master已被服务器节点[" + runningData.getServiceIp() + "," + runningData.getName() + "," + runningData.getServicePort() + "]占有,当前节点["+ serverData.getServiceIp() + "," + serverData.getName() + "," + serverData.getServicePort() + "]只能读取master节点信息!");if (runningData == null) {takeMaster(); // 没读到或读取瞬间Master节点宕机可争抢} else {masterData = runningData;}}} catch (ZkNodeExistsException e) {log.error("当前节点" + serverData.getServiceIp() + "," + serverData.getName() + "," + serverData.getServicePort() + "]创建Master临时节点异常,msg=" + e.getMessage());} catch (Exception e) {log.error("当前节点" + serverData.getServiceIp() + "," + serverData.getName() + "," + serverData.getServicePort() + "]争抢Master异常,msg=" + e.getMessage());}}

3.典型场景-调度服务单体执行-DigitalEmpTask

需求:某个时刻只允许Master节点执行调度服务,其它Slave从节点处于闲置、不执行状态。

package com.merak.hyper.automation.quartz.task;
import com.merak.hyper.automation.util.DateUtils;
import com.merak.hyper.automation.util.ZkHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/*** @author harry* @version 1.0* @ClassName: BizOrderTask* @description: 任务队列服务调度*/
@Component
public class DigitalEmpTask {public static final Logger log = LoggerFactory.getLogger(DigitalEmpTask.class);@Value("${zk_master.status}")private String status;@Scheduled(cron = "0/30 * * * * ?")protected void digitalEmpTaskScheduler() {//1.判断是否开启zookeeper分布式调度模式if( ZkHelper.getInstance().zookeeperOpen(status) ) {//2.判断当前工作服务节点为Master节点if( ZkHelper.getInstance().checkMaster() ) {executeCloudTask();}}else{//1.未开启zookeeper分布式调度模式,为单节点部署executeCloudTask();}}public void executeCloudTask(){log.info("任务开始执行,时间:" + DateUtils.dateTimeNow(DateUtils.YYYY_MM_DD_HH_MM_SS));try {} catch (Exception e) {log.error("调度失败,原因:" + e.getMessage());}}}

总结

1.线上1主2从已运行半年,可达到HA业务需求、自动切换能力
2.前端采取Nginx负载、分流,配置多个工作服务节点

参考

浅析如何基于ZooKeeper实现高可用架构
源代码下载

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

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

相关文章

中央处理单元(CPU)学习笔记

一、引言 中央处理单元(CPU)是计算机的核心部件,负责执行程序指令和控制整个计算机系统的运行。本篇学习笔记将介绍CPU的功能和组成,帮助读者深入了解CPU的工作原理和重要性。 二、CPU的功能 程序控制:CPU通过执行指…

一文读懂「多模态大模型」

​ 学习资源 5-多模态大模型一统NLP和CV 1.多模态大模型的基本原理 2.常见的多模态大模型 https://www.bilibili.com/video/BV1NN41177Zp?p5&vd_sourcef27f081fc77389ca006fcebf41bede2d 3.多模态大模型如_哔哩哔哩_bilibili 强强联手!科大讯飞和中科院终于把【…

模型 ECRS分析原则

系列文章 主要是 分享 思维模型,涉及各个领域,重在提升认知。效能优化。 1 ECRS分析原则的应用 1.1 ECRS分析原则在企业管理中的应用-麦当劳的服务流程优化 取消(Eliminate):麦当劳作为全球知名的快餐连锁企业&#…

一起来了解综合能源服务认证

首先,综合能源服务认证是有国家政策支持的, 《能源生产和消费革命战略(2016-2030)》中指出:1、能源生产端要以绿色低碳为方向,推动能源集中式和分布式开发并举,大幅提高新能源和可再生能源比重&#xff1b…

C# 进行图像处理的几种方法(Bitmap,BitmapData,IntPtr)

在C#中,进行图像处理时主要会使用到 System.Drawing 命名空间中的几个关键类,其中Bitmap、BitmapData和IntPtr是进行高效像素操作的重要工具。以下是如何利用这些类进行图像处理的方法概述: Bitmap 类: System.Drawing.Bitmap 是一个封装了位…

Redis黑马点评业务总结(含mac m1pro | windows11 wsl2 ubuntu环境配置 持续更新中~)

redis黑马点评项目分析业务学习笔记 含项目配置教学mac m1pro windows mac M1pro环境配置windows11 wsl2 ubuntu 环境配置一.短信登录1. 1发送验证码1.2短信登录注册1.3登录校验拦截器补缺Cookie Session Token1.4基于redistoken认证实现短信登陆1.5完善token认证的刷新机制 二…

STM32F103C8T6(HAL库函数 - 内部Flash操作)

简介 STM32F103C8T6 内部Flash 为 64KB,本次将对他多余空间进行读写。 介绍 数据手册下载 STM32F103x8/STM32F103xB 数据手册 包含Flash Memory Page分布 STM32F设备命名 设备容量类型 中容量类型 内部空间介绍 64 KBytes大小Flash Memory 从 0x0800 0000 ~…

【BIOS实战】

文章目录 网络通信编程基本常识Java原生网络编程-BIO 网络通信编程基本常识 什么是Socket? Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口,一般由操作系统提供。 在设计模式中,Socket其实就是一个门面模式&…

Leetcod面试经典150题刷题记录 —— 二叉搜索树篇

Leetcod面试经典150题刷题记录-系列Leetcod面试经典150题刷题记录——数组 / 字符串篇Leetcod面试经典150题刷题记录 —— 双指针篇Leetcod面试经典150题刷题记录 —— 矩阵篇Leetcod面试经典150题刷题记录 —— 滑动窗口篇Leetcod面试经典150题刷题记录 —— 哈希表篇Leetcod面…

vue3 封裝一个常用固定按钮组件(添加、上传、下载、删除)

效果图 这个组件只有四个按钮&#xff0c;添加&#xff0c;上传、下载、删除&#xff0c;其中删除按钮的颜色默认是灰色&#xff0c;当表格有数据选中时再变成红色 实现 组件代码 <script lang"ts" setup> import { Icon } from /components/Icon/index im…

【Gin实战教程】快速入门

Gin是一个轻量级的Web框架&#xff0c;使用Go语言开发。它具有高性能、易用性和灵活性的特点&#xff0c;是构建可扩展的Web应用程序的理想选择。 首先&#xff0c;Gin是一个高性能的框架。它基于Go语言的原生HTTP包进行开发&#xff0c;利用了Go语言的并发特性和协程模型&…

spark-sql字段血缘实现

spark-sql字段血缘实现 背景 Apache Spark是一个开源的大数据处理框架&#xff0c;它提供了一种高效、易于使用的方式来处理大规模数据集。在Spark中&#xff0c;数据是通过DataFrame和Dataset的形式进行操作的&#xff0c;这些数据结构包含了一系列的字段&#xff08;也称为…

在ASP.NET MVC中使用JQuery提供的弹出窗口(模态窗口)

在ASP.NET MVC中使用JQuery提供的弹出窗口&#xff08;模态窗口&#xff09; 原理 使用<div>图层灵活显示在浏览器的任何位置。默认情况下指定<div>不可见 引用 样式表 在JQuery的官方网站可以下载对应的css样式表。打开官网的样例页。 找到样式表引用路径 …

flex弹性盒子常用的布局属性详解

想必大家在开发中经常会用到flex布局。而且还会经常用到 justify-content 属性实现分栏等等 接下来给大家分别讲一下 justify-content 的属性值。 以下是我敲的效果图大家可以清晰看出区别 space-between 属性值可以就是说两端对齐 space-evenly 属性值是每个盒子之间的…

Requests库的接口测试实现

Requests库是在接口测试中被广泛运用的库&#xff0c;包括模拟请求的下发&#xff0c;请求相关配置和响应结果的获取&#xff0c;核心主体都是通过request库完成。在接口测试中使用非常频繁。 一、Requests库环境搭建 接口测试的核心从模拟请求开始。在Python中&#xff0c;通…

15个等轴视图设计的电动车汽车无人机等PR剪辑素材视频制作元素

包含15个等轴视图、等距视角电动车、汽车、无人机、沙漏、飞机等PR剪辑素材视频制作元素mogrt动画模板。 特征&#xff1a; 等距设计&#xff1b; 可以更改颜色&#xff1b; 分辨率&#xff1a;全高清&#xff08;19201080&#xff09;&#xff1b; 持续时间&#xff1a;15秒&a…

IDEA+Git——项目分支管理

IDEAGit——项目分支管理 1. 前言2. 基础知识点2.1. 分支区分2.2. Git 代码提交规范2.3. 四个工作区域2.4. 文件的四种状态2.5. 常用命令2.6 注重点 3. IDEA分支管理 1. 前言 在Git中&#xff0c;分支是项目的不同版本&#xff0c;当开始开发一个新项目时&#xff0c;主分支通常…

白话编程-概述

前言: 编程作为一种纯理性的学科,时刻都要明白自己到底在干什么.程序方面的书很多,却难以有那种把复杂问题讲清楚,让人很容易理解的.笔者想建立一种程序方面简单的思维,便于理解和记忆. 目录 一.概述 二.面向过程和面向对象 一.概述 编程到底是在干什么? 1)编程大多数情况…

使用命令行方式搭建uni-app + Vue3 + Typescript + Pinia + Vite + Tailwind CSS + uv-ui开发脚手架

使用命令行方式搭建uni-app Vue3 Typescript Pinia Vite Tailwind CSS uv-ui开发脚手架 项目代码以上传至码云&#xff0c;项目地址&#xff1a;https://gitee.com/breezefaith/uniapp-vue3-ts-scaffold 文章目录 使用命令行方式搭建uni-app Vue3 Typescript Pinia V…

深入理解C/C++指针:从基本操作到复杂表达式

目录 代码展示&#xff1a; 示例1&#xff1a;指向数组结束位置之后的地址 示例2&#xff1a;结构体大小对指针运算的影响 示例3&#xff1a;访问数组元素的不同方式 示例4&#xff1a;逗号表达式在数组初始化中的应用 示例5&#xff1a;计算多维数组元素间的地址差值 示…