基于binlog实现一些业务(Binlog4j)
前言
今天要跟大家分享的是监控数据变化,实现自己的业务的另一个思路,基于数据库的binglog。我这里是用的Binlog4j实现,希望看总结的,直接看最后。
一、Binlog4j是什么?
Binlog4j是轻量级 Mysql Binlog 客户端, 提供宕机续读, 高可用集群等特性等等。具体看一看它的官网,目前已经出到1.9.0版本,项目加入了Dromara 社区。
二、使用步骤
先说下我这里用上这个的原因:
- 多个数据增删改触发重新计算
- 不想在每个业务操作方法后调用重新计算
- 不想aop切面处理,实现重新计算
- 不想用springboot的事件、监听实现重新计算
- 骨子里想创新,接受新事物、新思路
所以我选择了它,下面我简单介绍下使用。
官网其实有demo,我这里是与springboot集成。
1.引入库
<!-- binlog支持 --><dependency><groupId>com.gitee.Jmysy</groupId><artifactId>binlog4j-spring-boot-starter</artifactId><version>1.9.0</version></dependency>
mysql的依赖、redis的依赖自行补充。
2.配置文件
spring: binlog4j:database: 要监听的数据库(一个实例上有多个库)redis-config: #redis配置host: ipport: 端口password: 密码client-configs:master:username: 数据库用户password: 密码host: 数据库ipport: 端口serverId: 1990
配置说明
- timeOffset 时间偏移量, 单位:毫秒
- serverId 编号
- redisConfig Redis 配置信息, 详见 RedisConfig
- inaugural 首次启动, 如果为 true 在启动时不再读取 Redis 记录
- persistence 是否启用持久化, 默认为 false
- strict 严格模式, 默认为 true
- mode 模式, 详见: BinlogClientMode
- username 数据库账户
- password 数据库密码
- host 数据库所在服务器 IP 地址
- port 数据库占用端口, 默认 3306
- hikariConfig 数据库连接池配置
3.统一监听处理
MyBinlogEventHandler
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.date.StopWatch;
import cn.hutool.json.JSONUtil;
import com.gitee.Jmysy.binlog4j.core.BinlogEvent;
import com.gitee.Jmysy.binlog4j.core.IBinlogEventHandler;
import com.gitee.Jmysy.binlog4j.springboot.starter.annotation.BinlogSubscriber;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import site.morn.rest.RestBuilders;
import site.morn.rest.RestMessage;import javax.annotation.Resource;/*** binlog事件处理器* 连接数据的用户需要有binlog读权限** @author zwmac*/
@Slf4j
@BinlogSubscriber(clientName = "master")
public class MyBinlogEventHandler implements IBinlogEventHandler {@Value("${spring.binlog4j.database:linkappdb}")public String monitorDatabase;@Resourceprivate ProgressWarnService progressWarnService;@Overridepublic void onInsert(BinlogEvent binlogEvent) {//log.info("数据库:" + binlogEvent.getDatabase());//log.info("数据表:" + binlogEvent.getTable());//log.info("插入数据:" + binlogEvent.getData());//需要重新计算场景//1、插入设置 app_progress_warn_config//2、新增实际进度详情 app_progress_real_detail//3、新增进度计划任务(子节点)app_progress_infoRestMessage restMessage = RestBuilders.successMessage();CalProgressWarnVo calVo = new CalProgressWarnVo();calVo.setType(1);String tableName = binlogEvent.getTable();if ("app_progress_warn_config".equals(tableName)) {StopWatch sw = new StopWatch();sw.start();calVo.setDataType(1);ProgressWarnConfig progressWarnConfig = BeanUtil.mapToBean(JSONUtil.parseObj(binlogEvent.getData()), ProgressWarnConfig.class, true, CopyOptions.create().ignoreNullValue());calVo.setNewData(progressWarnConfig);restMessage = progressWarnService.calProgressWarn(calVo);sw.stop();log.info("新增[进度预警配置]数据触发binlog事件执行结果:{}-耗时:{}ms", restMessage.getCode(), sw.getTotalTimeMillis());}if ("app_progress_real_detail".equals(tableName)) {StopWatch sw = new StopWatch();sw.start();calVo.setDataType(2);ProgressRealDetail realDetail = BeanUtil.mapToBean(JSONUtil.parseObj(binlogEvent.getData()), ProgressRealDetail.class, true, CopyOptions.create().ignoreNullValue());calVo.setNewData(realDetail);restMessage = progressWarnService.calProgressWarn(calVo);sw.stop();log.info("新增[实际进度详情]数据触发binlog事件执行结果:{}-耗时{}ms", restMessage.getCode(), sw.getTotalTimeMillis());}if ("app_progress_info".equals(tableName)) {StopWatch sw = new StopWatch();sw.start();Object progressInfoObj = binlogEvent.getData();ProgressInfo progressInfo = BeanUtil.mapToBean(JSONUtil.parseObj(binlogEvent.getData()), ProgressInfo.class, true, CopyOptions.create().ignoreNullValue());if (progressInfo.getType() == 2) {//计划任务才需要重新计算calVo.setDataType(3);calVo.setNewData(progressInfo);restMessage = progressWarnService.calProgressWarn(calVo);}sw.stop();log.info("新增[进度任务]数据触发binlog事件执行结果:{}-耗时{}ms", restMessage.getCode(), sw.getTotalTimeMillis());}}@Overridepublic void onUpdate(BinlogEvent binlogEvent) {//log.info("数据库:" + binlogEvent.getDatabase());//log.info("数据表:" + binlogEvent.getTable());//log.info("原数据:" + binlogEvent.getOriginalData());//log.info("新数据:" + binlogEvent.getData());//需要重新计算场景//1、设置表变更 app_progress_warn_config//2、进度详情记录变更 app_progress_real_detail//3、进度计划任务变更(计划开始时间、计划结束时间、工期)app_progress_infoRestMessage restMessage = null;CalProgressWarnVo calVo = new CalProgressWarnVo();calVo.setType(2);String tableName = binlogEvent.getTable();if ("app_progress_warn_config".equals(tableName)) {StopWatch sw = new StopWatch();sw.start();calVo.setDataType(1);ProgressWarnConfig oldConfig = BeanUtil.mapToBean(JSONUtil.parseObj(binlogEvent.getOriginalData()), ProgressWarnConfig.class, true, CopyOptions.create().ignoreNullValue());ProgressWarnConfig newConfig = BeanUtil.mapToBean(JSONUtil.parseObj(binlogEvent.getData()), ProgressWarnConfig.class, true, CopyOptions.create().ignoreNullValue());calVo.setNewData(newConfig);calVo.setOldData(oldConfig);restMessage = progressWarnService.calProgressWarn(calVo);sw.stop();log.info("修改[进度预警配置]数据触发binlog事件执行结果:{}-耗时{}ms", restMessage.getCode(), sw.getTotalTimeMillis());}if ("app_progress_real_detail".equals(tableName)) {StopWatch sw = new StopWatch();sw.start();calVo.setDataType(2);ProgressRealDetail oldDetail = BeanUtil.mapToBean(JSONUtil.parseObj(binlogEvent.getOriginalData()), ProgressRealDetail.class, true, CopyOptions.create().ignoreNullValue());ProgressRealDetail newDetail = BeanUtil.mapToBean(JSONUtil.parseObj(binlogEvent.getData()), ProgressRealDetail.class, true, CopyOptions.create().ignoreNullValue());calVo.setNewData(newDetail);calVo.setOldData(oldDetail);restMessage = progressWarnService.calProgressWarn(calVo);sw.stop();log.info("修改[进度详情]数据触发binlog事件执行结果:{}-耗时{}ms", restMessage.getCode(), sw.getTotalTimeMillis());}if ("app_progress_info".equals(tableName)) {StopWatch sw = new StopWatch();sw.start();calVo.setDataType(3);ProgressInfo oldInfo = BeanUtil.mapToBean(JSONUtil.parseObj(binlogEvent.getOriginalData()), ProgressInfo.class, true, CopyOptions.create().ignoreNullValue());ProgressInfo newInfo = BeanUtil.mapToBean(JSONUtil.parseObj(binlogEvent.getData()), ProgressInfo.class, true, CopyOptions.create().ignoreNullValue());calVo.setNewData(newInfo);calVo.setOldData(oldInfo);restMessage = progressWarnService.calProgressWarn(calVo);sw.stop();log.info("修改[进度计划任务]数据触发binlog事件执行结果:{}-耗时{}ms", restMessage.getCode(), sw.getTotalTimeMillis());}}@Overridepublic void onDelete(BinlogEvent binlogEvent) {//log.info("数据库:" + binlogEvent.getDatabase());//log.info("数据表:" + binlogEvent.getTable());//log.info("删除数据:" + binlogEvent.getData());//需要重新计算场景//1、删除进度详情记录 app_progress_real_detail//2、删除进度任务(子节点)app_progress_infoRestMessage restMessage = null;CalProgressWarnVo calVo = new CalProgressWarnVo();calVo.setType(3);String tableName = binlogEvent.getTable();if ("app_progress_real_detail".equals(tableName)) {StopWatch sw = new StopWatch();sw.start();calVo.setDataType(2);ProgressRealDetail oldDetail = BeanUtil.mapToBean(JSONUtil.parseObj(binlogEvent.getOriginalData()), ProgressRealDetail.class, true, CopyOptions.create().ignoreNullValue());calVo.setOldData(oldDetail);restMessage = progressWarnService.calProgressWarn(calVo);sw.stop();log.info("删除[进度详情]数据触发binlog事件执行结果:{}-耗时{}ms", restMessage.getCode(), sw.getTotalTimeMillis());}if ("app_progress_info".equals(tableName)) {StopWatch sw = new StopWatch();sw.start();calVo.setDataType(3);ProgressInfo oldInfo = BeanUtil.mapToBean(JSONUtil.parseObj(binlogEvent.getOriginalData()), ProgressInfo.class, true, CopyOptions.create().ignoreNullValue());calVo.setOldData(oldInfo);restMessage = progressWarnService.calProgressWarn(calVo);sw.stop();log.info("删除[进度计划任务]数据触发binlog事件执行结果:{}-耗时{}ms", restMessage.getCode(), sw.getTotalTimeMillis());}}@Overridepublic boolean isHandle(String database, String table) {//log.info("database:{},table:{}", database, table);//只监控aep数据库if (monitorDatabase.equals(database)) {return true;}return false;}}
CalProgressWarnVo
import lombok.Data;/*** 计算进度预警参数Vo** @author zwmac*/
@Data
public class CalProgressWarnVo {/*** 类型:1新增,2变更,3删除*/private Integer type;/*** 数据类型:1进度预警配置,2进度详情,3进度计划任务*/private Integer dataType;/*** 旧数据*/private Object oldData;/*** 新数据*/private Object newData;}
从入参可以看出3类数据增删改都触发重新计算,这里的oldData、newData可以直接用于修改的时候传参,不用在查一次数据库。
progressWarnService.calProgressWarn(calVo);就是重新计算的具体实现了,这就涉及到业务了,各位自己实现。
总结
- 配置的账号要有binlog的读权限
- 项目在跑,直接用其他工具操作数据库,也可以触发(这就是监听binlog的美妙)
- 其他项目操作本项目的表,也可以监听到(原理同上)
- 统一入口,不用有aop、事件、业务调用那么多入口需要考虑
好了,就写到这里,希望可以帮到大家,拥抱新事物,uping!
相关文章:
基于binlog实现一些业务(Binlog4j)
前言 今天要跟大家分享的是监控数据变化,实现自己的业务的另一个思路,基于数据库的binglog。我这里是用的Binlog4j实现,希望看总结的,直接看最后。 一、Binlog4j是什么? Binlog4j是轻量级 Mysql Binlog 客户端, 提供宕…...
python实现rpc的几种方式(SimpleXMLRPCServer 自带的、第三方ZeroRPC)、连接linux远程开发分布式锁、分布式id
1 python实现rpc的几种方式 1.1 SimpleXMLRPCServer 自带的 1.2 第三方ZeroRPC 2 连接linux远程开发 3 分布式锁 4 分布式id 1 python实现rpc的几种方式 # 远程过程调用-1 借助于rabbitmq,可以跨语言-2 SimpleXMLRPCServer 自带的-3 ZeroRPC-4 GRPC:跨语言的 htt…...
ARM麒麟V10 auditctl启动失败处理
问题: 业务服务器需要启用审计服务,但是启动审计服务失败,查看状态提示audit0。 修改配置文件/boot/efi/EFI/kylin/grub.cfg 删除audit0,或者设置audit1。 重启服务器后验证状态。 auditctl -D echo "-w /data -p rwxa"…...
day67
今日回内容 视图层 响应对象 cbv和fbv 上传文件 模板层 视图层 一、响应对象 响应对象的本质都是 HttpResponse HttpResponse:字符串 render: 将一个模板页面中的模板语法进行渲染,最终渲染成一个html页面作为响应体。 redirect:重定向 …...
04:2440---内存控制器
目录 一:介绍 1:引入 2:概念 3:通信 A:片选信号 B:片选信号的地址空间范围 4:地址线 A:不同位数的接法 B:访问原理 C:访问地址 5:时序 1:NOR FLASH A:2440NOR FLASH时序 B:原理/时序图 C:寄存器 6:SDARM A:访问方式 B:原理图 C:BWSCON D:BANKCON…...
【深度学习】CNN中pooling层的作用
1、pooling是在卷积网络(CNN)中一般在卷积层(conv)之后使用的特征提取层,使用pooling技术将卷积层后得到的小邻域内的特征点整合得到新的特征。一方面防止无用参数增加时间复杂度,一方面增加了特征的整合度…...
基于H1ve一分钟搭好CTF靶场
写在前面 ◉ ‿ ◉ 上一篇文章给大家详细介绍了基于H1ve搭建CTF靶场,以及过程中可能遇到的报错及解决方法,那么这篇文章,我总结了一下,将不会遇到报错的方法给到大家,但是前提是你的服务器最好是一个全新的哦~~~ 我…...
网络篇---第五篇
系列文章目录 文章目录 系列文章目录前言一、如何实现跨域?二、TCP 为什么要三次握手,两次不行吗?为什么?三、说一下 TCP 粘包是怎么产生的?怎么解决粘包问题的?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站…...
Git——Git应用入门
将会介绍以下知识: 搭建Git环境和创建Git版本库(init、clone)。文件添加、状态检查、创建注释和查看历史记录。与其他Git版本库交互(pull、push)。解决合并冲突。创建分支列表、列表切换和合并。创建标签。 1、版本控…...
【SpringBoot】Redisson 分布式锁注解和 @Transactional 注解一起使用问题
一、前言 平时使用切面去加分布式锁,是先开启事务还是先尝试获得锁?这两者有啥区别? 业务中怎么控制切面的顺序?切面的顺序对事务的影响怎么避免? 下面程序分析: OverrideTransactionalpublic ReceiveH5…...
Druid数据库连接池框架
1.Druid概述 Druid 是一个开源的数据库连接池框架,用于管理和优化数据库连接的使用。它提供了高效的、可扩展的连接池管理,可以用于 Java 应用程序连接到关系型数据库。 之前有了解过 C3P0 数据库连接池,所谓数据库连接池就是重复利用连接数据…...
Python项目打包
Python项目如何打包? 本指南总结了Python项目打包的最佳实践,主要涉及代码的打包和分发,以及环境和依赖的管理。 0. 一般项目清单 源代码(可使用git托管)数据包(可使用DVC托管)Docker环境镜像…...
ASUS(华硕) B760M-AYW WIFI D4_解决wifi不能使用
1、最近新购买了一套 diy电脑主机,选用的是 ASUS B760M-AYW WIFI D4电脑主板 win10 系统,到货后 发现右下角电脑图标处及网络适配器中 没有wifi选项 首先 在官网和旗舰店客服处,确认了 该主板 有集成wifi模块,鲨鱼鳍天线未安装…...
Postgresql数据库运维统计信息
如果需要使用以下运维信息,需要如下几步 修改postgresql.conf文件 #shared_preload_libraries # (change requires restart)shared_preload_libraries pg_stat_statements重启数据库创建扩展 CREATE EXTENSION IF NOT EXISTS pg_stat_statements;1. 统计信息…...
Python3基础
导包 在 python 用 import 或者 from...import 来导入相应的模块。 将整个模块(somemodule)导入,格式为: import somemodule 从某个模块中导入某个函数,格式为: from somemodule import somefunction 从某个模块中导入多个函数,格式为&#…...
【性能测试】服务器常用的性能指标总结,一文概全...
目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 压测过程中&#…...
Vue学习笔记-Vuex基本使用
基本使用 初始化数据、配置actions、mutations,操作文件/store/index.js //index.js文件用于创建Vuex中最为核心的store对象 import Vue from vue import Vuex from vuex Vue.use(Vuex) //actions对象用于响应组件中的动作,专门负责业务逻辑 const actions {//函数…...
vue3中的customRef创建一个自定义的 ref对象
customRef 创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制 小案例: 自定义 ref 实现 debounce <template><div style"font-size: 14px;"><input v-model"text" placeholder"搜索关键字"/><…...
动态规划学习——子序列问题
目录 编辑 一,最长定差子序列 1.题目 2,题目接口 3,解题思路及其代码 一,最长定差子序列 1.题目 给你一个整数数组 arr 和一个整数 difference,请你找出并返回 arr 中最长等差子序列的长度,该子序列…...
使用 COPY 加速 PostgreSQL 批量插入
文章目录 1.copy命令介紹2.copy vs insert的优势3.测量性能4.结论 1.copy命令介紹 PostgreSQL 中的命令COPY是执行批量插入和数据迁移的强大工具。它允许快速有效地将大量数据插入表中。 COPY命令为批量插入和数据迁移提供了更简单且更具成本效益的解决方案。 可以避免使用诸…...
C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...
Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...
uniapp手机号一键登录保姆级教程(包含前端和后端)
目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...
【C++进阶篇】智能指针
C内存管理终极指南:智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...
day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...
Leetcode33( 搜索旋转排序数组)
题目表述 整数数组 nums 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...
