架构复盘:如何优雅地使用 Redis 实现接口的防刷与限流?
分类:后端架构 | 标签:Redis, Spring Boot, 分布式
在开发注册模块时,为了防止恶意脚本疯狂调用邮件发送接口,我重构了系统的安全防护层。传统的单机 JVM 锁在多实例部署下完全失效,因此我引入了基于 Redis 的分布式防刷方案。
核心逻辑是采用 StringRedisTemplate,以标准化的用户输入(如邮箱或手机号)作为 Key。利用 Redis 的 SETNX 特性和过期时间指令,确保同一标识在指定时间窗口内的请求具有绝对排他性。该操作必须保证原子性,防止并发条件下的误判。
// 核心防刷控制代码片段
String lockKey = "auth:sms:lock:" + normalizedInput;
// 利用 Redis 的原子指令 setIfAbsent,同时设置 60 秒的过期时间
Boolean isLocked = stringRedisTemplate.opsForValue()
.setIfAbsent(lockKey, "locked", 60, TimeUnit.SECONDS);
if (Boolean.FALSE.equals(isLocked)) {
return Result.fail(429, "验证码发送太频繁,请1分钟后再试");
}
// 执行发信业务逻辑...
这套方案极其轻量,不仅规避了数据库层面的死锁风险,还在 Nginx 网关层之后筑起了第二道坚固的业务防火墙。同时,锁的释放完全依赖 TTL 过期,避免了服务宕机导致的死锁问题。
运维实战:微服务容器化部署实践与 Nacos 配置规范
分类:云原生部署 | 标签:Docker, Nacos, 部署规范
在近期的后端微服务架构环境迁移中,为了保证多环境(开发、测试、生产)的一致性,我将所有核心服务进行了全面容器化。容器化的核心前提是实现代码与配置的彻底分离。
在编写业务代码和构建镜像时,务必遵循配置绝对外置原则。禁止在代码内部硬编码任何数据库密码、中间件地址或环境特征内容。所有的动态配置统一由 Nacos 配置中心下发,环境标识通过 Docker 环境变量动态注入。
踩坑记录:曾经遇到过将测试环境的 Redis 密码直接写死在 application.yml 并打入 Jar 包的惨痛教训。这导致生产环境容器启动时直接连接测试库失败。因此,环境变量注入是实现“一处构建,多处运行”的唯一规范。
# 使用轻量级基础镜像
FROM openjdk:17-jdk-slim
WORKDIR /app
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
COPY target/service-app.jar /app/app.jar
# 声明环境变量,禁止在镜像内写死具体环境配置
ENV SPRING_PROFILES_ACTIVE=prod
ENV NACOS_SERVER_ADDR=127.0.0.1:8848
EXPOSE 8080
# 启动命令动态接收环境变量
ENTRYPOINT ["java", "-Dspring.profiles.active=${SPRING_PROFILES_ACTIVE}", "-Dspring.cloud.nacos.discovery.server-addr=${NACOS_SERVER_ADDR}", "-jar", "/app/app.jar"]
高并发处理:RabbitMQ 异步解耦实战
分类:消息队列 | 标签:RabbitMQ, 高并发, 异步处理
在复杂的业务链条中(例如用户注册后需要发放新人优惠券、初始化积分账户、发送欢迎邮件),同步阻塞处理会导致接口响应时间过长,甚至耗尽 Tomcat 线程池。我引入了 RabbitMQ 对核心链路与非核心链路进行了解耦。
为了符合代码开发规范,队列名称、交换机名称以及 RoutingKey 必须避免魔法值,全部通过 @Value 注解从配置中心动态读取。
@Configuration
public class RabbitConfig {
// 严禁硬编码,从 Nacos 配置中心动态读取
@Value("${mq.exchange.user-event}")
private String userEventExchange;
@Value("${mq.queue.welcome-email}")
private String emailQueue;
@Bean
public DirectExchange userExchange() {
return new DirectExchange(userEventExchange);
}
@Bean
public Queue emailQueue() {
return new Queue(emailQueue, true);
}
}
通过这种方式,主业务接口只需发送一条消息至 MQ 即可快速返回给前端,消费者服务在后台平滑地消费消息,极大提升了系统的吞吐量。
性能调优:线上慢查询排查与隐式类型转换优化
分类:数据库 | 标签:MySQL, 性能优化, 索引失效
近期系统监控捕获到大量耗时超过 2 秒的查询告警。通过拿到具体的慢 SQL 语句并在数据库中执行 EXPLAIN 分析执行计划,我发现该查询并未走预期的索引,而是触发了全表扫描(type 显示为 ALL)。
经过对比表结构和入参,查明原因是隐式类型转换。数据库表中的单号字段定义为 VARCHAR 类型,但 Mybatis 传入的查询参数被错误地定义为了 Long 类型。MySQL 在处理时会自动将字符串转换为数字进行比较,这会直接导致该字段上的 B+ 树索引彻底失效。
解决方式非常简单明确:将 Java 实体类中的对应字段类型修正为 String,保持与数据库字段类型一致。重新发布后,查询时间瞬间回落至 10 毫秒以内。
备忘录:从零部署 Spring Boot 项目到 Linux 服务器
分类:运维基础 | 标签:项目部署, Linux, Java
这是纯后端的单体项目部署标准流程。不要将服务器 IP、账号和路径硬编码在任何自动化脚本中,请通过环境变量传入。
第一步:在本地开发环境中,使用 Maven 将你的项目打包成可执行的 Jar 文件。
mvn clean package -DskipTests
第二步:使用 SCP 命令或任意 FTP 工具,将生成的 Jar 包上传至服务器的指定目录。
scp target/my-app.jar user@${SERVER_IP}:/opt/app/
第三步:登录服务器,进入对应目录。使用 nohup 命令后台启动 Java 进程,并将标准输出与错误输出重定向到日志文件中。
cd /opt/app/
nohup java -jar my-app.jar > app.log 2>&1 &
备忘录:Nginx 基础安装与反向代理配置
分类:运维基础 | 标签:Nginx, 反向代理, Web服务器
后端服务启动后,通常不会直接暴露 8080 端口给外部,我们需要用 Nginx 做一层反向代理。
第一步:在 Linux 服务器上通过包管理器安装 Nginx(以 Ubuntu 为例)。
sudo apt update
sudo apt install nginx -y
第二步:编辑 Nginx 配置文件。务必将 server_name 和 proxy_pass 的值根据实际部署环境进行配置,严禁在自动化模板里写死业务域名。
# 编辑配置文件,例如:vim /etc/nginx/conf.d/my-app.conf
server {
listen 80;
server_name ${YOUR_DOMAIN_NAME};
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
第三步:检查 Nginx 配置语法是否正确,随后重载 Nginx 使配置生效。
sudo nginx -t
sudo nginx -s reload
备忘录:免费 SSL 证书申请与 HTTPS 配置
分类:网络安全 | 标签:SSL, HTTPS, Certbot
网站上线必须配置 HTTPS。使用 Certbot 申请 Let's Encrypt 免费证书是最高效的方式。
第一步:安装 Certbot 以及对应的 Nginx 插件。
sudo apt install certbot python3-certbot-nginx -y
第二步:执行自动申请与配置命令。程序会要求你输入邮箱并同意协议,随后它会自动寻找 Nginx 配置并帮你完成 SSL 设置。
sudo certbot --nginx -d ${YOUR_DOMAIN_NAME}
第三步:如果你需要在统一网关服务器上手动配置证书(不依赖 certbot 自动修改),请在 Nginx 配置文件中开启 443 端口,并挂载标准的证书路径。注意保持路径为系统通用路径变量映射,避免路径硬编码带来的迁移问题。
server {
listen 443 ssl;
server_name ${YOUR_DOMAIN_NAME};
# 证书路径通常由环境统一分配,不建议写死在代码库中
ssl_certificate /etc/letsencrypt/live/${YOUR_DOMAIN_NAME}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${YOUR_DOMAIN_NAME}/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
}
}