聊聊业务项目如何主动感知mysql是否存活

news2024/12/26 0:54:44

前言

先前写过一篇文章聊聊如何利用redis实现多级缓存同步,里面讲到业务部门因数据库宕机,有技术提出当数据库宕机,切换到redis,今天我们就来聊聊如何触发这个切换动作?

1、方案一:利用异常机制

伪代码如下:


首先这个方案是不可行的,因为每次请求,还是先走到数据库逻辑,然后等抛出异常,这个时间会挺长的,业务上是无法接受的

2、方案二:主动进行mysql探活

实现思路: 可以利用数据库连接池检测有效连接的思路

实现方案

1、方式一:利用druid连接池的ValidConnectionChecker进行扩展

核心逻辑如下

@Slf4j
public class MysqlConnectionCheck extends MySqlValidConnectionChecker {

    private ApplicationContext applicationContext;

    public MysqlConnectionCheck(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }


    @Override
    public boolean isValidConnection(Connection conn, String validateQuery, int validationQueryTimeout) throws Exception {
      return checkMySQLCommunications(conn,validateQuery,validationQueryTimeout);

    }

    private boolean checkMySQLCommunications (Connection conn, String validateQuery, int validationQueryTimeout) {
        boolean validConnection = false;
        try {
            validConnection = super.isValidConnection(conn, validateQuery, validationQueryTimeout);
        } catch (Exception e) {
        }
        if(validConnection){
            boolean b = MySQLCommunicationsHolder.isMySQLCommunicationsException.compareAndSet(true, false);
            if(b){
                CommunicationsHealthEvent event = CommunicationsHealthEvent.builder().conn(conn).build();
                applicationContext.publishEvent(event);
            }
        }


        return validConnection;
    }
}

在yml配置我们自定义的检测器

spring:
    datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driverClassName: ${DRIVER_CALSS_NAME:com.mysql.cj.jdbc.Driver}
        url: ${DATASOURCE_URL:jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai}
        username: ${DATASOURCE_USERNAME:root}
        password: ${DATASOURCE_PWD:123456}
        druid:
            # 指定连接的有效检查类
            valid-connection-checker-class-name: com.github.lybgeek.db.check.test.user.check.MysqlConnectionCheck 

这个方案也是不大行的,后面翻了一下druid源码。当数据库抛出不可恢复的异常时,比如网络抖动,异常断开,druid会触发exceptionSorter,抛弃连接。而CreateConnectionThread会检测是否需要创建连接,如果不需要,他就会进行等待。当连接不够时,会调用

com.alibaba.druid.pool.DruidAbstractDataSource#createPhysicalConnection()

​进行创建,同时isValidConnection也是在这个方法里面进行连接验证,但这边就有问题,就是当数据库宕机了,就创建不了连接,因此就进入异常流程,isValidConnection是没法执行的

2、方式二:参考druid的检测连接逻辑,额外编写定时器触发检测逻辑

核心代码块:

public class ValidConnectionCheckerAdapter implements ValidConnectionChecker {

    private DbCheckProperies dbCheckProperies;

    public ValidConnectionCheckerAdapter(DbCheckProperies dbCheckProperies) {
        this.dbCheckProperies = dbCheckProperies;
    }

    @Override
    public boolean isValidConnection(Connection conn, String query, int validationQueryTimeout) throws SQLException {
        boolean valid = checkConnection(conn, query, validationQueryTimeout);

        // unexcepted branch
        if (valid && isMysql()) {
            long lastPacketReceivedTimeMs = MySqlUtils.getLastPacketReceivedTimeMs(conn);
            if (lastPacketReceivedTimeMs > 0) {
                long mysqlIdleMillis = System.currentTimeMillis() - lastPacketReceivedTimeMs;
                if (lastPacketReceivedTimeMs > 0
                        && mysqlIdleMillis >= dbCheckProperies.getTimeBetweenEvictionRunsMillis()) {
                    return false;
                }
            }
        }
        return valid;

    }


    private boolean checkConnection(Connection conn, String query, int validationQueryTimeout) throws SQLException {
        if (query == null || query.length() == 0) {
            return true;
        }

        if(conn == null){
            return false;
        }

        Statement stmt = null;
        ResultSet rs = null;
        try {
            stmt = conn.createStatement();
            if (validationQueryTimeout > 0) {
                stmt.setQueryTimeout(validationQueryTimeout);
            }
            rs = stmt.executeQuery(query);
            return true;
        } finally {
            JdbcUtils.close(rs);
            JdbcUtils.close(stmt);
        }
    }



 @Override
    public void afterPropertiesSet() throws Exception {
        executorService.scheduleWithFixedDelay(new DbCheckTask(),0,dbCheckProperies.getTimeBetweenEvictionRunsMillis(), TimeUnit.MILLISECONDS);
    }
    

    private class DbCheckTask implements Runnable{

        @Override
        public void run() {
            SQLException sqlException = null;
            Connection conn = dbConnManger.getConn();
            try {
                boolean validConnection = validConnectionChecker.isValidConnection(conn, dbCheckProperies.getValidationQuery(), dbCheckProperies.getValidationQueryTimeout());
                if(validConnection){
                    boolean b = MySQLCommunicationsHolder.isMySQLCommunicationsException.compareAndSet(true, false);
                    if(b){
                        CommunicationsHealthEvent event = CommunicationsHealthEvent.builder().conn(conn).build();
                        applicationContext.publishEvent(event);
                    }
                }else{
                   sqlException = new SQLException("connection is invalid","10040");
                }
            } catch (SQLException e) {
                 log.error("{}",e);
                 sqlException = e;
                 dbConnManger.closeConnection();
                 conn = null;
            }

            if(sqlException != null){
                MySQLCommunicationsHolder.isMySQLCommunicationsException.compareAndSet(false, true);
                CommunicationsUnHealthEvent event = CommunicationsUnHealthEvent.builder().sqlException(sqlException).build();
                applicationContext.publishEvent(event);
            }



        }
    } 

总结

其实mysql的探活实现方式有很多种,本文的实现检测逻辑是直接套用druid的检测连接逻辑,之前对druid的使用,基本上就是停留在配置上,没过多关注。

为了写这篇文章,特地翻了一下druid的源码,主要是因为之前以为扩展ValidConnectionChecker就行了,后面发现行不通。就看了一下源码,发现druid的设计思路挺好的,有些实现思想是我们在日常开发中,可以借鉴使用的。还有druid里面有些跟数据库相关的util,也是可以直接拿过来用的。

demo链接

https://github.com/lyb-geek/springboot-learning/tree/master/springboot-db-check

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

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

相关文章

大三寒假人生第一次面试失败

2022/12/28,今天是人生第一次面试。坐了2个小时的地铁去面试结果却很惨。一开始进门就笔试,当看到笔试题时发现很多基础,平时耳熟能详的词汇却怎么样也回答不出来。做了一个多小时,当面试官把题改了以后一句笔试没过。说真的在出门…

RocketMQ消息队列的下载、配置、启动、测试

目录 下载 环境变量的配置 新建一个变量 配置path 新建变量 启动 命名服务器 启动broker 测试是否启动成功 下载 地址:RocketMQ 官方网站 | RocketMQ 切换到中文模式很容易看的 下载那一列就行了 安装很容易的。 环境变量的配置 新建一个变量 就是你的bin文…

No.181# 点直播简要架构梳理走查

引言直播带货、潮流电商、短视频不断融合,本文走查下音视频直播的简要架构和角色。选择UDP,注重传输实时性,在线教育、音视频会议等。选择TCP,注重画面质量、是否卡顿等,娱乐直播、直播带货等。本文主要内容有&#xf…

RPA:帮助企业完成财务数字化转型

为什么要做财务的数字化转型 a. 传统企业财务的现状 “重复性强、耗时耗力、效率低下”是目前大家对传统企业财务的固有印象。很多企业的财务部门仍然采用传统的手工操作模式,财务流程繁琐分散,且财务部门缺乏获取、处理数据的工具。绝大部分的人力都投…

2022LOL微博杯模糊问题,1080p高清看微博杯the shy比赛直播

2022LOL微博杯的直播模糊,看着不爽 观看方法 1.打开下面在线播放m3u8文件的地址 http://www.m3u8.zone/ 如图 2.输入播放地址 微博杯的播放地址: (1月三号的地址 如果失效往下看解决方法) https://plwb01.live.weibo.com/ali…

前端数据结构与算法

前端数据结构与算法 文章宝典 链表 可以快速删除和插入节点,只用修改节点的引用 实例 队列 实例 栈 实例 树 并且左节点的值和后续节点的值都要小于等于该节点的值 图 根据图的节点之间的边是否有方向,可以分为有向图和无向图。 在有向图…

数字调制系列:如何理解IQ ?

最近在筹划写一系列关于数字IQ 调制的短文,以帮助初学者能够更好地理解和掌握。虽然IQ 调制技术已经非常广泛地应用于各种无线通信应用中,但是究其细节,仍有很多人存在疑惑,尤其对于初学者。作者从事测试工作多年,对IQ…

强化学习的Sarsa与Q-Learning的Cliff-Walking对比实验

强化学习的Sarsa与Q-Learning的Cliff-Walking对比实验Cliff-Walking问题的描述Sarsa和Q-Learning算法对比代码分享需要改进的地方引用和写在最后Cliff-Walking问题的描述 悬崖行走:从S走到G,其中灰色部分是悬崖不可到达,求可行方案 建模中&am…

(Java)【深基9.例4】求第 k 小的数

【深基9.例4】求第 k 小的数 一、题目描述 输入 nnn&#xff08;1≤n<50000001 \le n < 50000001≤n<5000000 且 nnn 为奇数&#xff09;个数字 aia_iai​&#xff08;1≤ai<1091 \le a_i < {10}^91≤ai​<109&#xff09;&#xff0c;输出这些数字的第 kk…

元旦礼第三弹!玻色量子荣登2022年中国创新力量50榜单

​2022年12月&#xff0c;国内最大的创新者社区极客公园重磅发布了全新的「中国创新力量 50 榜单&#xff08;InnoForce 50&#xff09;」——在过去一年为泛计算机科学领域及其交叉领域带来创新和突破的中国公司/机构。玻色量子凭借在光量子计算领域突出的核心竞争力&#xff…

配电网前推后带法求电力系统潮流(PythonMatlab实现)

目录 1 概述 2 数学模型 3 节点分层前推回代潮流计算及步骤 3.1 计算方法 3.2 计算步骤 4 算例及数据 5 Matlab&Python代码实现 1 概述 配电网通常是单电源全网连接、开环运行&#xff0c;即呈树状。针对配电系统分析&#xff0c;其根本就是进行潮流计算。潮流计算的…

通过反射机制访问java对象的属性 给属性赋值 读取属性的值

package com.javase.reflect;import java.lang.reflect.Field;/*** 通过反射机制&#xff0c;访问java对象的属性&#xff0c;给属性赋值&#xff0c;读取属性的值&#xff08;重点&#xff1a;五颗星*****&#xff09;* 本例中使用反射机制编写代码&#xff0c;看起来比不使用…

Hi3861鸿蒙物联网项目实战:智能温度计

华清远见FS-Hi3861开发套件&#xff0c;支持HarmonyOS 3.0系统。开发板主控Hi3861芯片内置WiFi功能&#xff0c;开发板板载资源丰富&#xff0c;包括传感器、执行器、NFC、显示屏等&#xff0c;同时还配套丰富的拓展模块。开发板配套丰富的学习资料&#xff0c;包括全套开发教程…

art-template模板引擎

1、模板引擎的基本概念 1.1、渲染UI结构时遇到的问题 var rows [] $.each(res.data, function (i, item) { // 循环拼接字符串 rows.push(<li class"list-group-item"> item.content <span class"badge cmt-date">评论时间&#xff1a; item…

C++ 使用Socket实现主机间的UDP/TCP通信

前言 完整代码放到github上了&#xff1a;cppSocketDemo 服务器端的代码做了跨平台&#xff08;POSIX和WINDOWS&#xff09;&#xff0c;基于POSIX平台&#xff08;Linux、Mac OS X、PlayStation等&#xff09;使用sys/socket.h库&#xff0c;windows平台使用winsock2.h库。 客…

STM32配置LED模块化

文章目录前言一、LED的模块化二、GPIO初始化详细解析三、LED代码封装总结前言 本篇文章将带大家深入了解GPIO的配置&#xff0c;并带大家实现LED模块化编程。 一、LED的模块化 什么叫模块化编程&#xff1f;我的理解就是每一个模块都分别写成对应的.c和.h文件&#xff0c;有…

S32K144—从0到1一个MBD模型的诞生

一个MBD模型的诞生&#xff0c;分为以下几步&#xff1a; 1、连接好硬件S32K144 EVB 2、选择一个合适的工作空间&#xff0c;新建一个simulink模型&#xff0c;保存 3、在模型中拖入模块&#xff1a; MBD_S32K1xx_Config_Information Digital_Input_ISR Periodic_Interrupt…

C++ 设计模式

设计模式序创建型模式工厂方法模式抽象工厂模式单例模式建造者模式&#xff08;生成器模式&#xff09;原型模式结构型模式适配器模式装饰器代理模式外观模式桥接模式组合模式&#xff08;部分--整体模式&#xff09;享元模式行为型模式策略模式模板模式观察者模式迭代器模式责…

对抗js前端加密的万能方法

1、前言 现在越来越多的网站采用全报文加密&#xff0c;测试的时候需要逆向提取加密算法以及密钥&#xff0c;过程十分繁琐和复杂。本文提供一种更为简单快捷的方法来解决此问题。 原理大致如下&#xff1a;使用浏览器的Override Hook加密前的数据&#xff0c;配置代理地址发…

[Linux]Linux编译器-gcc/g++

&#x1f941;作者&#xff1a; 华丞臧. &#x1f4d5;​​​​专栏&#xff1a;【LINUX】 各位读者老爷如果觉得博主写的不错&#xff0c;请诸位多多支持(点赞收藏关注)。如果有错误的地方&#xff0c;欢迎在评论区指出。 推荐一款刷题网站 &#x1f449; LeetCode刷题网站 文…