Spring Boot 实现自定义审计功能
背景
在现代应用系统中,事件审计是一个至关重要的功能。通过记录用户的操作行为,我们可以追踪问题、分析用户行为,甚至在出现安全问题时提供关键证据。由于目前没有较好的事件审计框架,笔者决定实现一套可扩展的事件审计组件,要求对业务低侵入性,可以轻松获取前后变更的内容。
目标
提供自定义注解给业务侧,实现开箱即用的事件审计存储功能。
实现
审计
我们定义了 @EventAuditor 注解,关键字段包括 operator(操作人)、content(操作内容模板)、bizScenario(事件发生的场景)等。
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667@Documented@Inherited@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface EventAudi ...
Spring 扩展 Bean 动态注册和销毁
背景
Spring Boot Starters 的自动装配机制给我们提供了非常丰富的扩展点,但有时候需要动态开启或者关闭某些组件,只能修改配置,重启服务才能生效。例如,我们接入了 Arthas 工具,有时候需要关闭,有时候需要开启,但又不能随意重启服务。
目标
监听 Spring Boot Starters 组件的配置变化,动态注册销毁 Bean。
实现
以 Arthas Spring Boot Starter 组件为例。我们发现 Spring Cloud 组件提供了 EnvironmentChangeEvent 作为配置变更事件,传入 ApplicationListener 监听器可以实现配置变化的监听,代码片段如下:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051@RequiredArgsConstructor@Slf4jpublic class ArthasEnvironmentChangeListener implements Applic ...
Mybatis 拦截器解析原始 SQL 语句
背景
MyBatis Plus 通过配置文件中设置 log-impl 属性来指定日志实现,以打印 SQL 语句。
1234567mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpllogging: level: org.ylzl.eden.demo.mapper: DEBUG
打印出来的 SQL 内容如下。
12345==> Preparing: SELECT id,login,email,activated,locked,lang_key,activation_key,reset_key,reset_date,created_by,created_date,last_modified_by,last_modified_date FROM demo_user WHERE id=?==> Parameters: 1(Long)<== Columns: ID, LOGIN, EMAIL, ACTIVATED, LOCKED, LANG_KEY ...
Mybatis 自动填充创建时间和更新时间字段
背景
Spring JPA 提供了 @CreatedDate、@LastModifiedDate 注解,用于自动赋值实体类的创建时间和更新时间。但我们的团队主要使用 MyBatis-Plus 作为 ORM 框架,需要提供同类的机制支持。
目标
为 MyBatis-Plus 提供自动填充功能。
实现
MyBatis-Plus 也提供了自动填充功能,通过实现 com.baomidou.mybatisplus.core.handlers.MetaObjectHandler 接口来实现。笔者定了 AutofillMetaObjectHandler 类实现,如下。
123456789101112131415161718192021222324252627282930@RequiredArgsConstructorpublic class AutofillMetaObjectHandler implements MetaObjectHandler { private final String createdDateFieldName; private final String lastM ...
Sentinel 接入 Prometheus 监控
背景
由于 Sentinel Dashboard 开源版本没有实现监控数据的持久化,只能查看 5 分钟的内存数据。在项目初期,我们没有足够的精力对 Sentinel 进行改造,因此,将 Sentinel 的监控数据集成到 Prometheus,并导入到 Grafana 可视化管理。
目标
扩展 Sentinel,将监控数据作为 Spring Boot Actuator 暴露到 Prometheus,并展示到 Grafana 管理。
实现
从官方的 PR 可以找到,Sentinel 提供了 MetricExtension 扩展点,允许您使用 SPI 扩展。
源码如下。
1234567891011121314151617181920package com.alibaba.csp.sentinel.metric.extension;import com.alibaba.csp.sentinel.slots.block.BlockException;public interface MetricExtension { void addPass(String resource, ...
Spring Boot 实现可扩展的分布式锁
背景
在 Java 中分布式锁的实现框架主要包括基于 数据库、Redis 和 Zookeeper 的实现方式。使用 Redis 实现的组件可以选择 Jedis API 或者 Redisson API,使用 Zookeeper 实现的组件可以选择 Zookeeper API 或者 Curator API。笔者在项目中看到不少这种混用 API 的情况,维护性较差。
目标
封装通用接口,屏蔽 API 细节,基于 Spring Boot 自动装配管理。
实现
首先,定义分布式锁接口。
123456789101112131415161718192021222324252627282930313233public interface DistributedLock { /** * 锁类型 * * @return 锁类型 */ String lockType(); /** * 加锁(阻塞) * * @param key 锁对象 */ boolean lock(String key); /** * 加锁(阻塞直到超时) * * @param key 锁对象 ...
Spring Boot 实现可扩展的分布式ID生成器
背景
在复杂的分布式系统中,往往需要对大量的数据和消息进行唯一标识。美团技术团队的文章中介绍了 Leaf 分布式ID生成系统的两种方案:Leaf-snowflake 方案和 Leaf-segment 方案。
Leaf-snowflake 方案沿用 Twitter 开源的雪花算法,在原有的基础上使用 Zookeeper 持久顺序节点进行优化,如下图。
Leaf-segment 方案基于数据库实现,每次获取一批递增号段的值,用完之后再去数据库获取新的号段,可以生成趋势递增的 ID,同时 ID 号是可计算的,因此不适用于订单 ID 生成场景(容易被竞争对手算出一天的订单量),相关原理如下图。
由于 Leaf 分布式ID生成系统设计为独立部署,由各系统接入使用,但稍微维护不当,容易搞垮整个系统。笔者认为,可以将 Leaf 改造为插件化组件,托管到 Spring Boot Starter 自动装配。除此之外,业界开源的组件也有滴滴 TinyId、百度 UidGenerator 等,最好是设计为通用的 ID 生成器,底层实现任意切换,对业务无感知。
目标
封装通用接口,屏蔽 API 细节,基于 ...
Spring 自定义 @RestControllerAdvice 返回结果
背景
Spring 提供了 @RestControllerAdvice 用来实现 HTTP 协议的全局异常处理。在异常信息的处理上通常只返回特定的 Response 对象,如下。
123456789101112131415161718192021@Slf4j@RestControllerAdvicepublic class RestExceptionResolver { @ExceptionHandler(Exception.class) public ResponseEntity<?> processException(Exception ex) { BodyBuilder builder; Response response; ResponseStatus responseStatus = AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class); if (responseStatus != null) { builder = ResponseEn ...
Spring 扩展 @AutoConfiguration 开关
背景
Spring Boot Starters 提供了很多常用的组件,有些组件例如 Kafka 和 RocketMQ,引入到工程后会自动装配,它会检查您的配置,跟外部的组件创建一些连接,这个时候,我们很难控制 Kafka 关闭,RocketMQ 开启。
目标
给 Spring Boot Starters 的组件提供一个开关,控制自动装配。
实现
Spring Boot AutoCofiguration 提供了 AutoConfigurationImportFilter 过滤器,用来控制 @Configuration 配置类是否生效。查看 AutoConfigurationImportSelector 的源码,它会去调用 AutoConfigurationImportFilter 过滤器,传入所有的 @Configuration 配置类,通过 filter 方法筛选掉不需要的配置类,返回最新的配置类集合。
12345678910111213141516171819202122232425262728293031323334353637383940414243public class Au ...
使用 binlog2sql 工具在线恢复数据
背景
生产数据库执行 SQL 脚本,一般会经过正规的审批流程才能运行。但有些情况是例外的,业务部门在提出一些删除数据的需求后打算撤回,或者在运营后台不小心删除了一些数据,然后找到 DBA 团队协助,希望能恢复数据。
经调研,binlog2sql 是大众点评开源的一款用于解析 MySQL binlog 的工具,根据不同选项,可以得到原始SQL、回滚SQL、去除主键的INSERT SQL 等,适用于数据快速回滚(闪回)和主从切换后新 Master 丢数据的修复工作。
目标
验证 binlog2sql 工具是否可以快速恢复数据。
步骤
准备工作
安装 binlog2sql 工具。
123456> git clone https://github.com/danfengcao/binlog2sql.git && cd binlog2sql# > yum install python3-pip# > whereis pip# > pip3.6 install -r requirements.txt> pip install -r requiremen ...