当前位置:首页 > 技术 > 正文内容

PanguHA双机热备系统部署指南

访客 技术 2026年10月7日 1

PanguHA双机热备软件获取

1.系统概述

PanguHA是一款专为Windows平台设计的双机热备集群解决方案,通过纯软件方式提供系统高可用性保障。该系统通常由两个节点组成:活动节点和备用节点。活动节点负责处理业务请求,而备用节点则作为备份,随时准备接管业务。当活动节点发生故障导致业务中断时,备用节点能够迅速检测并接替活动节点的工作,从而确保业务连续性或实现最小化中断。

2.运行环境

需安装.NET Framework 4.6.1或更高版本

3.软件部署

无需安装,直接解压后运行主程序即可。

4.功能详解

4.1 IP地址漂移

IP地址漂移功能主要用于实现Web应用、WebApi/Webservice、数据库等服务的故障转移。除了服务器的物理IP地址外,该软件允许配置一个虚拟IP地址,所有外部访问均通过此虚拟IP进行。配置完成后,虚拟IP绑定在主节点上;当主节点发生故障时,虚拟IP会自动漂移至备用节点继续提供服务。对于前端应用和数据库而言,这种切换是无感知的,因为它们仍然通过相同的虚拟IP进行访问。

主节点在以下情况下会触发IP漂移:

  1. 服务器宕机
  2. 网卡异常
  3. Web服务URL异常(需预先配置)
  4. Windows服务异常(需预先配置)

配置步骤:首先选择一台服务器作为主节点,打开软件并切换到【IP漂移】标签页,选择网络接口(确保该接口使用静态IP而非DHCP自动获取)。如有多个网卡,请选择需要配置虚拟IP的那个网段的网卡。

设置本机IP(例如192.168.56.101)和集群虚拟IP(例如192.168.56.103,注意路由器不应将该IP与MAC地址绑定)。虚拟IP必须与本机IP在同一网段,且未被其他设备使用。

添加对方服务器的IP地址。点击运行后,右下角显示"失败重试"可以忽略。等待一段时间后,左下角显示"主节点"即表示主节点配置成功,虚拟IP已绑定到当前节点。可通过ipconfig命令验证。对方节点显示红色是因为尚未运行热备软件。

心跳间隔和断联重试设置可调整主备切换速度,但不宜设置过小,否则可能因网络抖动导致频繁切换。【切换】按钮提供手动切换功能。【停止】按钮将停止心跳检测,不进行主备切换。

配置对方IP后,系统会检测对方热备软件的在线状态,异常时发送邮件提醒。防火墙需开放ICMP协议和默认端口9999。

如果发现端口已开放、软件已运行,但telnet不通,请检查防火墙是否阻止了HA应用。

对方IP状态颜色含义:

  • 绿色:正常连接
  • 红色:连接断开
  • 灰色:未监测

添加本机URL可监测Web服务状态。当检测到Web服务状态码非200(30秒内重试3次)时,如果是在主节点,则进行切换并邮件提醒;如果是在备节点,则仅发送邮件提醒(防止主节点服务异常且无可用的备节点)。

配置Windows服务后,主节点检测到服务异常会先尝试重启(30秒内重试3次),重启失败则进行故障转移。备节点服务重启失败则发送邮件提醒。

配置备用节点服务器时,需注意两台服务器必须能互相ping通,且设置的集群虚拟IP必须相同。两台服务器应在同一网段。

配置完成后,可进行故障转移测试:关闭主节点服务器,观察备用节点是否升级为主节点(即虚拟IP是否漂移过来)。测试成功后,访问虚拟IP将指向备用节点,实现故障转移。

Web应用配置:如使用IIS,需在两台服务器上部署相同应用,并将绑定IP均设置为虚拟IP。其他Web服务器(如Tomcat)同理。若绑定虚拟IP导致服务启动失败,可改为绑定0.0.0.0。

4.2 进程管理

进程管理功能基于IP漂移,用于配置可执行文件(.exe)或Windows服务的切换。配置的进程和服务仅在主节点上运行,备用节点上则会被关闭。

操作步骤:假设已完成IP漂移配置,现需控制WindowsFormsApp1.exe程序的切换。只需在A、B两台服务器上对该程序进行配置即可。配置的进程将在主节点上运行(即使手动关闭也会自动重启),在备节点上则会被关闭(即使意外启动也会自动终止)。当主节点A宕机后,备用节点B升级为主节点,会自动开启配置的进程,实现进程切换。Windows服务的配置方法相同。

4.3 文件同步

注意事项:数据库文件的同步请谨慎使用此功能

文件同步功能基于IP漂移,该软件定位为文件服务器,因此需配合虚拟IP使用。配置前需先设置共享文件夹,并确保两台服务器能互相访问对方的共享文件夹路径。

在服务器A上配置A→B的同步设置,在服务器B上配置B→A的同步设置。虽然两台服务器都配置了同步路径并启动,但同步任务仅在主节点上运行。当主节点A宕机,备用节点B升级为主节点后,会开启同步任务。文件同步是实时的,右上角的镜像周期是为了防止备用节点意外修改或删除文件,确保备用节点的文件始终与主节点保持一致,并非指同步有5分钟延迟。

4.4 MySQL主从同步

注意事项:部署前请确保两个节点的结构和数据完全一致

视频教程请参考:

MySQL主从同步视频教程

该软件可快速部署MySQL双主复制,结合IP漂移功能,可构建MySQL高可用架构。操作步骤:切换到【MySQL主从】标签页,按照提示在A、B两台服务器上进行配置,然后重启MySQL服务。

在服务器A输入相关信息配置A主B从,点击部署按钮,右下角显示部署成功。在服务器B上输入相关信息配置B主A从,点击部署按钮,显示成功提示。

至此,MySQL双主复制搭建完成。此外,需确保mysql.user表内有%账号,以便虚拟IP可以访问MySQL。结合IP漂移功能,可实现服务器级别宕机的双机切换——当主节点A服务器宕机后,备用节点B升级为主节点,虚拟IP指向B节点,继续提供MySQL服务。

若在下方填写本机MySQL服务名称并开启健康检测,可实现数据库应用级别的宕机切换——当主节点A的MySQL服务故障时,备用节点B可检测到并转移虚拟IP,升级为主节点继续提供服务。同时,若备用节点的MySQL服务故障,则会发送邮件提醒。

4.5 MySQL/SQLServer/PostgreSQL数据同步

基本条件:

  1. 表必须有主键(复合主键也可)
  2. 任务初始化完成后,源端会自动添加C_PanguSyncSourceTimestamp时间戳字段,目标端会自动添加C_PanguSyncTargetTimestamp时间戳字段。这两个字段应由数据库自行维护,业务系统中不要主动写入或更新。

视频教程请参考:

MySQL/SQLServer/PostgreSQL数据同步视频教程

视频要点说明:

假设初始状态X为主节点,Y为备用节点。首先在主备节点准备数据库,表结构需完全一致(数据不需要一致)。然后在备用节点Y使用【清空备用节点数据脚本】或TRUNCATE语句(不要使用DELETE)清空数据。在Y节点切换到【数据同步】标签页,添加任务,选择库而非表,批量配置Y→X方向的任务并启动。遇到主键问题时,使用【批量主键创建脚本】或手动创建主键,待所有任务启动成功。然后在X主节点上切换到【数据同步】标签页,添加任务,选择库而非表,批量配置X→Y方向的任务并启动。遇到主键问题时,使用【批量主键创建脚本】或手动创建主键,待所有任务启动成功。此时会进行X→Y方向的初始数据全量同步,在全量同步阶段请在Y备用节点的【IP漂移】界面暂停,避免意外切换。全量同步完成后会进行增量同步,此时再在Y备用节点的【IP漂移】界面运行。

SQLServer清空备用节点数据脚本【参考视频】:

-- 替换为实际数据库名称
Use B;
DECLARE @DatabaseName NVARCHAR(128) = N'B';
 
-- 验证数据库是否存在
IF NOT EXISTS (SELECT 1 FROM sys.databases WHERE name = @DatabaseName)
BEGIN
    RAISERROR(N'错误:数据库 "%s" 不存在!', 16, 1, @DatabaseName);
    RETURN;
END;
 
-- 声明变量
DECLARE @SchemaName    NVARCHAR(128),
        @TableName     NVARCHAR(128),
        @FullTableName  NVARCHAR(255),
        @SqlStatement  NVARCHAR(MAX);
 
-- 创建游标获取所有用户表
DECLARE tableCursor CURSOR LOCAL FAST_FORWARD FOR
SELECT 
    s.name AS SchemaName,
    t.name AS TableName
FROM 
    sys.databases db
INNER JOIN 
    sys.tables t ON db.name = @DatabaseName
INNER JOIN 
    sys.schemas s ON t.schema_id = s.schema_id
WHERE 
    db.name = @DatabaseName 
    AND t.type = 'U';  -- 只获取用户表
 
-- 开始处理
OPEN tableCursor;
 
FETCH NEXT FROM tableCursor INTO @SchemaName, @TableName;
 
WHILE @@FETCH_STATUS = 0
BEGIN
    BEGIN TRY
        -- 构造完整表名和SQL语句
        SET @FullTableName = QUOTENAME(@SchemaName) + '.' + QUOTENAME(@TableName);
        SET @SqlStatement = N'USE ' + QUOTENAME(@DatabaseName) + N'; 
        TRUNCATE TABLE ' + @FullTableName;
 
        -- 执行截断操作
        EXEC sp_executesql @SqlStatement;
        PRINT N'成功截断表: ' + @FullTableName;
    END TRY
    BEGIN CATCH
        PRINT N'错误截断表: ' + @FullTableName + N' | 错误信息: ' + ERROR_MESSAGE();
    END CATCH
 
    FETCH NEXT FROM tableCursor INTO @SchemaName, @TableName;
END;
 
-- 清理资源
CLOSE tableCursor;
DEALLOCATE tableCursor;
 
PRINT N'操作完成!请检查输出信息确认结果。';

PostgreSQL清空备用节点数据脚本【参考视频】:

DO $$
DECLARE
    table_name TEXT;
BEGIN
    -- 获取所有用户表
    FOR table_name IN 
        SELECT tablename 
        FROM pg_tables 
        WHERE schemaname = 'public'
         
    LOOP
        -- 清空表并重置序列
        EXECUTE 'TRUNCATE TABLE ' || quote_ident(table_name) || ' RESTART IDENTITY CASCADE';
        RAISE NOTICE '已清空表: %', table_name;
    END LOOP;
END 
$$;

MySQL清空备用节点数据脚本【参考视频】:

SET SESSION group_concat_max_len = 10000000; 
-- 生成 TRUNCATE 语句
SELECT GROUP_CONCAT(
    CONCAT('TRUNCATE TABLE `', table_name, '`')
    SEPARATOR ';'
) INTO @truncate_sql
FROM information_schema.tables 
WHERE table_schema = DATABASE() 
AND table_type = 'BASE TABLE';
 
-- 显示将要执行的语句
SELECT @truncate_sql AS 将要执行的语句;

1.查询出结果后,复制语句

-- 禁用外键检查
SET FOREIGN_KEY_CHECKS = 0;
2.刚才查询出来的语句
SET FOREIGN_KEY_CHECKS = 1;

【SQLServer批量主键创建脚本】(遍历某个库所有表,1.如果有主键则跳过。2.如果有自增列却没有主键,则将自增列设置为主键。3.如果没有自增列也没有主键,则自动创建名为_PanguId_类型为Bigint的自增主键):

DECLARE @DatabaseName NVARCHAR(128) = N'A' -- 替换为你的数据库名称

USE [A] -- 这里也需要替换为实际的数据库名称

DECLARE @TableSchema NVARCHAR(128)
DECLARE @TableName NVARCHAR(128)
DECLARE @ColumnName NVARCHAR(128)
DECLARE @Sql NVARCHAR(MAX)
DECLARE @PKName NVARCHAR(128)

-- 创建游标遍历所有用户表
DECLARE table_cursor CURSOR FOR
SELECT 
    TABLE_SCHEMA, 
    TABLE_NAME 
FROM INFORMATION_SCHEMA.TABLES 
WHERE TABLE_TYPE = 'BASE TABLE'
  AND TABLE_CATALOG = @DatabaseName

OPEN table_cursor
FETCH NEXT FROM table_cursor INTO @TableSchema, @TableName

WHILE @@FETCH_STATUS = 0
BEGIN
    -- 检查表是否有主键
    IF NOT EXISTS (
        SELECT 1 
        FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
        JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu 
            ON tc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME
        WHERE tc.CONSTRAINT_TYPE = 'PRIMARY KEY'
          AND tc.TABLE_SCHEMA = @TableSchema
          AND tc.TABLE_NAME = @TableName
          AND tc.TABLE_CATALOG = @DatabaseName
    )
    BEGIN
        -- 检查表是否有自增列
        IF EXISTS (
            SELECT 1 
            FROM INFORMATION_SCHEMA.COLUMNS 
            WHERE TABLE_SCHEMA = @TableSchema 
              AND TABLE_NAME = @TableName 
              AND TABLE_CATALOG = @DatabaseName
              AND COLUMNPROPERTY(
                  OBJECT_ID(QUOTENAME(@TableSchema) + '.' + QUOTENAME(@TableName)), 
                  COLUMN_NAME, 
                  'IsIdentity'
              ) = 1
        )
        BEGIN
            -- 获取自增列名
            SELECT @ColumnName = COLUMN_NAME
            FROM INFORMATION_SCHEMA.COLUMNS 
            WHERE TABLE_SCHEMA = @TableSchema 
              AND TABLE_NAME = @TableName 
              AND TABLE_CATALOG = @DatabaseName
              AND COLUMNPROPERTY(
                  OBJECT_ID(QUOTENAME(@TableSchema) + '.' + QUOTENAME(@TableName)), 
                  COLUMN_NAME, 
                  'IsIdentity'
              ) = 1
            
            -- 设置自增列为主键
            SET @PKName = 'PK_' + @TableName
            SET @Sql = N'ALTER TABLE ' + QUOTENAME(@TableSchema) + '.' + QUOTENAME(@TableName) + 
                      N' ADD CONSTRAINT ' + QUOTENAME(@PKName) + 
                      N' PRIMARY KEY (' + QUOTENAME(@ColumnName) + N')'
            
            PRINT N'为表' + @TableSchema + '.' + @TableName + N' 设置自增列为主键: ' + @Sql
            EXEC sp_executesql @Sql
        END
        ELSE
        BEGIN
            -- 创建_PanguId_自增主键
            SET @PKName = 'PK_' + @TableName
            SET @Sql = N'
            IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS 
                          WHERE TABLE_SCHEMA = @Schema 
                            AND TABLE_NAME = @Table 
                            AND COLUMN_NAME = ''_PanguId_'')
            BEGIN
                ALTER TABLE ' + QUOTENAME(@TableSchema) + '.' + QUOTENAME(@TableName) + 
                N' ADD _PanguId_ bigint IDENTITY(1,1) NOT NULL
            END
            
            ALTER TABLE ' + QUOTENAME(@TableSchema) + '.' + QUOTENAME(@TableName) + 
            N' ADD CONSTRAINT ' + QUOTENAME(@PKName) + 
            N' PRIMARY KEY (_PanguId_)'
            
            PRINT N'为表' + @TableSchema + '.' + @TableName + N' 创建_PanguId_自增主键'
            
            EXEC sp_executesql @Sql, 
                N'@Schema NVARCHAR(128), @Table NVARCHAR(128)', 
                @Schema = @TableSchema, @Table = @TableName
        END
    END
    ELSE
    BEGIN
        PRINT N'表' + @TableSchema + '.' + @TableName + N' 已有主键,跳过处理'
    END
    
    FETCH NEXT FROM table_cursor INTO @TableSchema, @TableName
END

CLOSE table_cursor
DEALLOCATE table_cursor

【MySQL批量主键创建脚本】自动创建主键脚本(遍历某个库所有表,1.如果有主键则跳过。2.如果有自增列却没有主键,则将自增列设置为主键。3.如果没有自增列也没有主键,则自动创建Bigint类型名称为_PanguId_的自增主键):

下面脚本中的CALL AddMissingPrimaryKeys('A'); A替换为自己的数据库

DELIMITER $$
 
DROP PROCEDURE IF EXISTS AddMissingPrimaryKeys$$
 
CREATE PROCEDURE AddMissingPrimaryKeys(IN target_db VARCHAR(64))
BEGIN
    DECLARE done INT DEFAULT FALSE;
    DECLARE tbl_name VARCHAR(64);
    DECLARE auto_col_name VARCHAR(64) DEFAULT NULL;
    DECLARE has_pk TINYINT DEFAULT 0;
    DECLARE has_pangu_col TINYINT DEFAULT 0;
 
    DECLARE cur CURSOR FOR
        SELECT TABLE_NAME
        FROM information_schema.TABLES
        WHERE TABLE_SCHEMA = target_db
          AND TABLE_TYPE = 'BASE TABLE';
 
    -- 主循环的 NOT FOUND handler
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
 
    OPEN cur;
 
    read_loop: LOOP
        FETCH cur INTO tbl_name;
        IF done THEN
            LEAVE read_loop;
        END IF;
 
        -- 重置状态
        SET auto_col_name = NULL;
        SET has_pk = 0;
        SET has_pangu_col = 0;
 
        -- 检查是否有主键
        SELECT COUNT(*) INTO has_pk
        FROM information_schema.KEY_COLUMN_USAGE
        WHERE TABLE_SCHEMA = target_db
          AND TABLE_NAME = tbl_name
          AND CONSTRAINT_NAME = 'PRIMARY';
 
        IF has_pk > 0 THEN
            ITERATE read_loop;
        END IF;
 
        -- 👇 安全获取自增列:使用嵌套块隔离 NOT FOUND 👇
        BEGIN
            DECLARE no_auto_inc CONDITION FOR SQLSTATE '02000';
            DECLARE CONTINUE HANDLER FOR NOT FOUND BEGIN END; -- 吃掉 NOT FOUND
 
            SELECT COLUMN_NAME INTO auto_col_name
            FROM information_schema.COLUMNS
            WHERE TABLE_SCHEMA = target_db
              AND TABLE_NAME = tbl_name
              AND EXTRA = 'auto_increment'
            LIMIT 1;
        END;
 
        IF auto_col_name IS NOT NULL THEN
            SET @sql = CONCAT(
                'ALTER TABLE `', target_db, '`.`', tbl_name, '` ',
                'ADD PRIMARY KEY (`', auto_col_name, '`);'
            );
            PREPARE stmt FROM @sql;
            EXECUTE stmt;
            DEALLOCATE PREPARE stmt;
            ITERATE read_loop;
        END IF;
 
        -- 👇 安全检查 _PanguId_ 列是否存在(COUNT 不会触发 NOT FOUND,可不用嵌套)👇
        SELECT COUNT(*) INTO has_pangu_col
        FROM information_schema.COLUMNS
        WHERE TABLE_SCHEMA = target_db
          AND TABLE_NAME = tbl_name
          AND COLUMN_NAME = '_PanguId_';
 
        IF has_pangu_col > 0 THEN
            SET @sql = CONCAT(
                'ALTER TABLE `', target_db, '`.`', tbl_name, '` ',
                'MODIFY COLUMN `_PanguId_` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, ',
                'ADD PRIMARY KEY (`_PanguId_`);'
            );
            PREPARE stmt FROM @sql;
            EXECUTE stmt;
            DEALLOCATE PREPARE stmt;
            ITERATE read_loop;
        END IF;
 
        -- 新建 _PanguId_
        SET @sql = CONCAT(
            'ALTER TABLE `', target_db, '`.`', tbl_name, '` ',
            'ADD COLUMN `_PanguId_` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT FIRST, ',
            'ADD PRIMARY KEY (`_PanguId_`);'
        );
        PREPARE stmt FROM @sql;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
 
    END LOOP;
 
    CLOSE cur;
END$$
 
DELIMITER ;
 
-- 使用示例
CALL AddMissingPrimaryKeys('A');
DROP PROCEDURE IF EXISTS AddMissingPrimaryKeys;

4.6 邮件通知

如需邮件通知功能,请在【邮件通知】标签页进行相关配置。默认配置了163的SMTP服务器,也可设置其他邮箱服务器。发件人邮箱必须与SMTP服务器类型匹配,收件人可以是其他邮箱。授权码需登录相应邮箱设置。配置完成后,可先点击【邮件测试】确认邮件发送正常,然后点击【保存配置】。

以下情况会触发邮件通知:

  • VIP切换时
  • 添加对方IP后,主节点监测到对方IP连接异常时
  • 添加本机Web服务URL后,备节点检测到Web服务异常时
  • 添加本机Windows服务后,备节点检测到Windows服务异常时
  • 开启MySQL健康检测后,备节点断联或同步异常时
  • SQLServer数据同步异常停止任务时

邮件格式如下图所示。

5.开机自启动

  • 在【IP漂移】点击运行成功后,会保存相关信息,下次打开软件将自动运行
  • 在【进程管理】配置后,下次打开将自动运行
  • 在【文件同步】配置后,下次打开将自动运行
  • 在【MySQL主从配置】开启健康检测成功后,会保存MySQL相关设置,下次打开软件将自动开启健康检测
  • 在【数据同步】添加任务后,下次打开将自动运行
  • 将软件快捷方式放入启动文件夹(Win+R输入shell:startup)
  • 或通过组策略设置(Win+R输入gpedit.msc,依次展开计算机配置→Windows设置→安全设置→本地策略→安全选项→用户账户控制:以管理员批准模式运行所有管理员,设置为已禁用)

6.清除配置

删除Config文件夹即可清除所有配置。

返回列表

上一篇:Java中对象的浅拷贝与深拷贝实现及差异

没有最新的文章了...

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

linux screen 用法详情 (nohup 的替代方案)

一、screen 是什么?能干嘛?screen 是一个终端复用器,可以:在一个 SSH 会话中开多个“虚拟终端”SSH 断线后,程序仍然在后台运行随时重新连接到原来的会话特别适合:nohup 的替代方案跑脚本 / 爬虫 / 训练模型运维、远程开发二、安装 screen# CentOS / Rocky / Almayum install -y screen# Debian / Ubuntuapt i...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。