编程开发笔记

Talk is cheap. Show me the code. 记录架构设计与后端的全栈实战经验。

架构复盘:如何优雅地使用 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_nameproxy_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;
    }
}