每日学习Java之一万个为什么

文章目录

Docker


一、基础命令

1. 启动/停止Docker服务
# 启动Docker服务(Linux)
sudo systemctl start docker

# 停止Docker服务
sudo systemctl stop docker

# 重启Docker服务
sudo systemctl restart docker

# 设置开机自启
sudo systemctl enable docker
2. 查看Docker信息
# 查看Docker版本
docker version

# 查看Docker系统详细信息(存储、网络、资源等)
docker info

二、镜像操作

1. 拉取镜像
# 从Docker Hub拉取镜像(默认latest标签)
docker pull 镜像名称[:标签]

# 示例:拉取最新版Nginx镜像
docker pull nginx:latest
2. 列出本地镜像
# 查看所有本地镜像
docker images

# 仅显示镜像ID(-q)
docker images -q

# 查看所有镜像(含历史层)
docker images -a
3. 删除镜像
# 删除指定镜像(需停止所有关联容器)
docker rmi 镜像ID/名称

# 强制删除镜像
docker rmi -f 镜像ID/名称

# 清理未使用的镜像
docker image prune -a
4. 构建镜像
# 使用Dockerfile构建镜像(当前目录)
docker build -t 镜像名称[:标签] .

# 示例:构建名为app:1.0的镜像
docker build -t app:1.0 .
5. 推送镜像
# 登录Docker Hub
docker login

# 推送镜像到仓库
docker push 镜像名称[:标签]

三、容器操作

1. 创建并启动容器
# 后台运行容器并映射端口
docker run -d -p 宿主机端口:容器端口 镜像名称[:标签]

# 示例:运行Nginx容器并映射8080→80端口
docker run -d -p 8080:80 nginx:latest

# 指定名称启动容器
docker run --name 自定义名称 镜像名称
2. 列出容器
# 查看运行中的容器
docker ps

# 查看所有容器(含已停止)
docker ps -a

# 过滤显示(如仅显示名称)
docker ps --format "table {{.Names}}\t{{.Status}}"
3. 管理容器生命周期
# 停止容器
docker stop 容器ID/名称

# 强制停止容器
docker stop -t 0 容器ID/名称

# 启动已停止的容器
docker start 容器ID/名称

# 删除容器
docker rm 容器ID/名称

# 强制删除运行中容器
docker rm -f 容器ID/名称
4. 进入容器
# 交互式进入运行中的容器(新终端)
docker exec -it 容器ID/名称 /bin/bash

# 以root权限进入(若容器支持)
docker exec -u root -it 容器ID/名称 /bin/sh
5. 查看容器日志
# 实时查看容器日志
docker logs -f 容器ID/名称

# 查看指定行数日志
docker logs --tail 100 容器ID/名称

四、网络操作

1. 网络管理
# 创建自定义网络
docker network create 网络名称

# 列出所有网络
docker network ls

# 删除网络
docker network rm 网络名称

# 将容器连接到网络
docker network connect 网络名称 容器ID/名称

# 将容器从网络断开
docker network disconnect 网络名称 容器ID/名称

五、卷(Volume)操作

1. 数据卷管理
# 创建命名卷
docker volume create 卷名称

# 列出所有卷
docker volume ls

# 删除卷
docker volume rm 卷名称

# 清理未使用的卷
docker volume prune

六、系统维护

1. 清理资源
# 清理所有无用资源(停止容器、未使用的镜像、卷、网络)
docker system prune -a

# 清理停止的容器
docker container prune

# 清理未使用的网络
docker network prune
2. 查看磁盘占用
# 查看Docker资源占用
docker system df

七、其他实用命令

1. 容器操作
# 查看容器实时资源使用(CPU/内存)
docker stats 容器ID/名称

# 导出容器为tar文件
docker export 容器ID > 容器备份.tar

# 导入tar文件为镜像
docker import 容器备份.tar 新镜像名称
2. 帮助与文档
# 查看Docker命令帮助
docker --help

# 查看特定命令帮助(如docker run)
docker run --help

八、进阶命令示例

1. 端口映射与挂载卷
docker run -d \
  -p 8080:80 \          # 端口映射
  -v /宿主机路径:/容器路径 \  # 挂载卷
  --name my_container \  # 自定义名称
  nginx:latest
2. 资源限制
# 限制容器内存和CPU
docker run -d \
  --memory="512m" \     # 最大512MB内存
  --cpus="2" \          # 最多使用2个CPU核心
  my_app

九、常见问题命令

1. 修复Docker进程占用过高
# 查看占用资源最多的容器
docker stats
2. 查找未命名的容器
docker ps -a | grep "容器ID前缀"

附录:快速参考表

操作类型命令
基础服务systemctl start/stop/restart docker, docker info
镜像操作docker pull, docker images, docker rmi, docker build, docker push
容器操作docker run, docker ps, docker stop/start, docker exec
网络管理docker network create, docker network ls, docker network rm
清理资源docker system prune, docker image prune, docker volume prune

Gateway

技术对比

路由转发配置

路由属性 RouterDefinition

网关实现登录校验(JWT)

问题:怎么实现?

自定义过滤器:编写登录校验的Pre前置处理逻辑。校验时机:网关将请求转发到微服务之前。校验结果后的用户信息放置Http请求头中。

问题:如何自定义过滤器?

实现GlobalFilter接口,重写filter方法,并注册到Spring中。

问题:校验逻辑?

1.获取请求中的请求头中的 登录凭证

2.校验凭证

3.return chain.filter(exchange) 并保证我们的过滤器在NettyRoutingFilter之前执行。

问题:NettyRoutingFilter的实现是什么样的?
实现了globalfilter接口,以及Ordered接口。
Ordered接口需要返回一个优先级,值越低优先级越高,范围是int范围。

问题:微服务之间的用户信息如何存储?

分析:基于OpenFegin发起,可以也可以放在请求头里。

问题:请求头放置什么类型的数据?

自定义GatewayFilter

问题:为什么用GatewayFilter?怎么实现

更灵活。区别于globalfilter,需要实现一个抽象网关过滤器工厂。过滤器工厂可以读取配置并帮助你创建过滤器对象。无参过滤器具体实现是定义匿名内部类的方法。自定义gatewayfilter 方法名必须是
前缀+GatewayFilterFactory,前缀未来就是yml中的 filter: -前缀

网关处理请求的流程(责任链)

1.路由映射器:默认HandlerMapping 路由处理,将匹配的路由存储上下文

2.请求处理器:默认FilteringWebHandler,加载多个过滤器(Pre前置处理),放入集合中形成过滤器链,然后依次执行这些过滤器。

3.路由过滤器:NettyRoutingFilter,将请求转发到微服务(URI属性)并将响应结果存入上下文(Post后置处理)。

网关过滤器

  • GatewayFilter :针对特定路由 / yml中的过滤器属性或者配置到default-filter中,为所有未设置过滤器的路由生效。
  • GlobalFilter:全局过滤器,声明生效。

两种过滤器的接口方法签名完全一致:

Mon<Void> filter(ServerWebExchange exchage,GatewayFilterChain chain);

参数:

  • exchange:网关上下文
  • chain : 可以调用下一个过滤器

返回值:

  • Mono :定义回调函数(异步非阻塞编程)

使用场景:几乎不需要写Post

Sentinel


配置信息

1. 熔断降级配置

(1) 基础熔断配置(Java代码)
// 定义需保护的服务方法,绑定降级逻辑
@SentinelResource(
  value = "orderService", 
  fallback = "orderFallback", 
  blockHandler = "orderBlockHandler"
)
public OrderDTO getOrder(String orderId) {
  // 业务逻辑(如调用下游服务)
}

// 通用异常降级方法(fallback)
public OrderDTO orderFallback(String orderId, Throwable e) {
  return new OrderDTO("DEFAULT_ORDER"); // 返回默认值或错误信息
}

// 熔断触发时的专用处理(blockHandler)
public OrderDTO orderBlockHandler(
  String orderId, 
  BlockException ex
) {
  return new OrderDTO("SERVICE_UNAVAILABLE"); // 服务不可用时的逻辑
}

说明

  • fallback:捕获业务异常(如空指针、数据库异常)。
  • blockHandler:捕获 Sentinel 规则触发的限流/降级(如QPS超过阈值)。
  • 双保险设计:两者结合可覆盖异常和流量控制场景(如知识库[1]中金融平台案例)。
(2) 动态规则配置(Nacos集成)
# application.yml(规则持久化配置)
spring:
  cloud:
    sentinel:
      datasource:
        ds1:
          nacos:
            server-addr: localhost:8848
            dataId: ${spring.application.name}-sentinel-rules
            groupId: DEFAULT_GROUP
            rule-type: flow
// Nacos中配置的流控规则示例
[
  {
    "resource": "orderService",
    "grade": 1,          // QPS模式
    "count": 1000,       // 阈值1000 QPS
    "strategy": 0,       // 直接拒绝
    "controlBehavior": 0 // 快速失败
  },
  {
    "resource": "paymentService",
    "grade": 2,          // 异常比例模式
    "count": 0.5,        // 异常率超过50%触发降级
    "strategy": 0
  }
]
(3) 热点参数限流(秒杀场景)
// 方法定义(需标注@SentinelResource)
@SentinelResource(value = "hotProduct", blockHandler = "hotProductBlock")
public Product getHotProduct(@RequestParam("productId") String productId) {
  // 业务逻辑(如查询热门商品)
}

// 初始化热点参数规则(白名单配置)
public void initHotRules() {
  ParamFlowRule rule = new ParamFlowRule("hotProduct")
    .setParamIdx(0)          // 参数索引(从0开始)
    .setCount(100)           // 单参数阈值100 QPS
    .setGrade(RuleConstant.FLOW_GRADE_QPS)
    .setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
  
  // 添加例外项(如VIP用户不触发限流)
  rule.getParamFlowItemList()
    .add(new ParamFlowItem()
      .setSpecificItem("VIP_1001") // 指定参数值
      .setObjectType(ParamFlowItem setObjectTypeAnd));
  
  ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
}

2. 流控模式与效果

模式说明效果
直接单纯基于资源名的限流。快速失败、Warm Up(预热)、排队等待(需配置超时时间)。
关联根据关联资源的流量控制非核心操作(如读写分离场景)。例如:当核心写操作流量过高时,自动限流非核心读操作。
链路根据调用链路区分限流对象(如内部调用与外部调用)。例如:外部请求达到阈值时触发限流,内部调用不受影响。
(1) Warm Up(预热)配置
# 配置示例(application.yml)
spring.cloud.sentinel.flow.coldFactor=3  # 冷启动系数,默认3

逻辑

  • 初始QPS = 阈值 / coldFactor(如阈值12 → 起始QPS=4)。
  • 逐步提升至阈值(如4秒内从4 QPS线性增长到12 QPS)。
(2) 排队等待
# 示例配置(需结合规则JSON)
controlBehavior: 2  # 排队等待模式
maxQueueingTimeMs: 2000  # 等待超时时间(毫秒)

效果

  • 达到阈值后,请求进入队列等待,超时未处理则拒绝。

微服务集成

1. Spring Cloud集成步骤

(1) 依赖添加
<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
(2) Feign与Sentinel整合
# application.yml启用Feign与Sentinel的集成
feign:
  sentinel:
    enabled: true
(3) 全局异常处理
@SentinelResource(
  fallback = "globalFallback",
  blockHandler = "globalBlockHandler"
)
@RestControllerAdvice
public class GlobalExceptionHandler {
  public OrderDTO globalFallback(
    Throwable e, 
    BlockException ex
  ) {
    // 全局降级逻辑
    return new OrderDTO("GLOBAL_ERROR");
  }
}

2. 最佳实践

  • 服务雪崩防护:对核心服务配置熔断(如知识库[1]中金融平台提升37%成功率)。
  • 监控与告警:通过Sentinel Dashboard实时监控指标(QPS、异常率、熔断状态)。
  • 动态规则管理:结合Nacos或Apollo实现规则热更新,无需重启服务。

Seata(分布式事务)

在这里插入图片描述

在这里插入图片描述

XA模式

1. XA模式原理

  • 角色

    • TM(事务管理器):协调全局事务(如Seata的TC)。
    • RM(资源管理器):管理资源(如数据库)。
    • AP(应用):发起事务请求。
  • 流程

    1. 准备阶段:AP向TM提交事务,TM通知各RM准备(锁资源)。
    2. 提交阶段:TM收集RM状态,若全部成功则提交,否则回滚。
  • 特点

    • 强一致性:依赖数据库对XA协议的支持(如MySQL 8+)。
    • 性能开销大:需两阶段提交,资源锁定时间长。

2. Seata XA模式配置

# seata.config
transaction.service.group.xa.DataSource.XADataSourceType = mysql
transaction.service.group.xa.DataSource.user = root
transaction.service.group.xa.DataSource.password = root
// 业务代码示例(需@GlobalTransactional注解)
@GlobalTransactional
public void saveOrder(OrderSaveParam param) {
  // 跨服务操作(如扣减库存、生成订单)
  // 异常时自动回滚
}

AT模式

1. AT模式原理

  • 自动补偿机制:通过异步提交快照对比实现最终一致性。
  • 流程
    1. 预写阶段:记录业务数据的前后快照。
    2. 提交阶段:直接提交业务数据,异步对比快照完成最终提交或回滚。
  • 特点
    • 无侵入性:无需数据库支持XA协议。
    • 性能较高:避免两阶段提交的锁竞争问题。

2. Seata AT模式配置

# seata.config
transaction.service.group.at.DataSource.driverClass = com.mysql.cj.jdbc.Driver
transaction.service.group.at.DataSource.url = jdbc:mysql://localhost:3306/test?useSSL=false
// 业务代码与XA模式相同,只需切换模式
@GlobalTransactional
public void saveOrder(OrderSaveParam param) {
  // 同样实现跨服务操作
}

XA模式 vs AT模式对比

维度XA模式AT模式
一致性强一致性(两阶段提交)最终一致性(异步补偿)
性能较低(锁资源时间长)较高(无锁设计)
数据库依赖需支持XA协议(如MySQL 8+)无需XA协议,支持主流数据库
适用场景高一致性要求的金融场景高并发、低延迟的电商、互联网场景

总结

  • Sentinel:用于流量控制、熔断降级,保障单服务稳定性。
  • Seata XA/AT模式:解决分布式事务问题,确保跨服务数据一致性。
  • 组合方案:在微服务架构中,Sentinel可与Seata结合,既控制流量又保证事务。

如需进一步优化配置或具体场景实现,可参考知识库中的代码示例和最佳实践!

RabbitMQ

高性能异步通信组件

同步通讯:打视频,打电话(实时阻塞)

异步通讯:WX聊天(非阻塞)
在这里插入图片描述

同步和异步

同步调用:需要等待调用的结果 (大概是核心业务)

异步调用:无需等待调用结果 / 回复 (大概是边缘业务)

问题:异步调用为什么需要消息代理?
消息的接收方可能在忙。需要驿站

MQ技术选型

在这里插入图片描述

数据隔离

知识总结

  • 虚拟主机(Virtual Host):逻辑隔离不同业务的数据,每个虚拟主机独立管理队列、交换机和权限。
  • 用户权限:通过用户与虚拟主机的绑定实现权限隔离,用户只能操作关联虚拟主机内的资源。

注意

  • 默认虚拟主机/ 是默认虚拟主机,需谨慎使用。
  • 权限配置:需显式授予用户对虚拟主机的 configurewriteread 权限。

使用场景

  • 多项目/团队共享同一 RabbitMQ 集群时,避免数据互相干扰。
  • 敏感业务需独立资源管理(如金融、日志系统)。

常见八股文

  • Q:如何实现 RabbitMQ 的数据隔离?
    A:通过创建独立虚拟主机,并为不同用户分配对应虚拟主机的权限。
  • Q:虚拟主机与用户权限的关系?
    A:用户需绑定到虚拟主机,且需显式授予操作权限(如 set_permissions)。

Spring AMQP

知识总结

  • 核心组件RabbitTemplate(发送消息)、@RabbitListener(消费消息)、MessageConverter(消息转换)。
  • 自动声明:通过配置自动创建队列、交换机及绑定关系。

细节

  • 消息序列化:默认使用 JdkSerializationConverter,生产环境推荐 Jackson2JsonMessageConverter,两者不兼容。
  • 事务与确认:需显式配置 publisher-confirmspublisher-returns,并配置相应的回调函数。

使用场景

  • Spring Boot 项目中快速集成 RabbitMQ,实现消息的发送与消费。

常见八股文

  • Q:如何配置 JSON 消息转换器?
    A:通过 @Bean 注册 Jackson2JsonMessageConverter
  • Q:Spring AMQP 如何实现消息确认?
    A:配置 PublisherCallbackChannel 并实现 CorrelationData

Work 模式(工作队列)

知识总结

  • 特性:一个生产者、多个消费者,消息默认按轮询分发,确保消息被消费一次。
  • 负载均衡:消费者按能力拉取消息(基于 prefetch-count 配置)。

细节

  • 公平调度:需设置合理 prefetch-count(默认 256),避免快慢消费者不均。
  • 消息持久化:需同时设置队列持久化(durable)和消息持久化(persistent)。

使用场景

  • 高并发任务处理(如订单创建、日志收集)。

常见八股文

  • Q:Work 模式如何实现公平分发?
    A:通过设置 prefetch-count 为 1,确保消费者处理完当前消息后才拉取新消息。
  • Q:Work 模式与发布订阅的区别?
    A:Work 模式消息被消费一次,发布订阅消息被所有消费者接收(FanOut交换机)。

MQ 消息转换器

知识总结

  • 作用:将 Java 对象与字节流相互转换,支持 JSON、XML、序列化等格式。
  • 核心类MessageConverter(如 Jackson2JsonMessageConverter)。

细节

  • 一致性:生产者与消费者需使用相同的转换器。
  • 自定义转换:可通过继承 AbstractJavaTypeMapper 处理复杂对象映射。
  • Jackson序列化比JDK ObjectOutputStream的转换更小,而且可读性更高。

使用场景

  • 发送复杂对象(如 Java Bean)时避免二进制序列化。

常见八股文

  • Q:如何自定义消息转换器?
    A:实现 MessageConverter 接口,覆盖 toMessagefromMessage 方法。
  • Q:为什么 JSON 转换器比默认序列化更好?
    A:JSON 更易读、体积小,且支持跨语言解析。

发布订阅模式(Pub/Sub)

知识总结

  • 特性:消息被广播到所有绑定的队列,每个消费者独立消费一份副本。
  • 交换机类型fanout(无路由键)、topicdirectheaders

细节

  • 临时队列:消费者可声明 exclusive 队列,断开后自动删除。
  • 路由规则topic 支持通配符(* 匹配一个词,# 匹配多个词)。

使用场景

  • 实时通知(如系统告警、消息广播)。

常见八股文

  • Q:如何实现精准匹配的 Pub/Sub?
    A:使用 direct 交换机,通过路由键精确匹配队列。
  • Q:fanout 与 topic 的区别?
    A:fanout 不区分路由键,消息广播给所有绑定队列;topic 支持通配符路由。

消息堆积问题处理

知识总结

  • 原因:生产速度 > 消费速度,或消费者故障。
  • 解决方案:扩容消费者、优化消费逻辑、调整预取数、使用优先级队列。

细节

  • 预取数(prefetch-count):过高可能导致内存溢出,过低影响吞吐。
  • 死信队列:设置 max-lengthttl,将未及时消费的消息转存。

使用场景

  • 高峰流量(如秒杀、促销活动)时的流量削峰。

常见八股文

  • Q:如何快速缓解消息堆积?
    A:扩容消费者、优化消费逻辑、调整预取数、启用死信队列。
  • Q:消息堆积时如何保证消息不丢失?
    A:确保消息持久化、消费者确认模式为 manual/auto

发送者重连

知识总结

  • 机制:RabbitMQ 客户端通过 Retry 机制自动重连。
  • 配置参数connection-factoryrecovery-intervalautomatic-recovery-enabled

细节

  • 重连间隔:默认 5 秒,需根据业务调整。
  • 未确认消息:重连后需手动重发未确认的消息(如使用 PublisherCallbackChannel)。

使用场景

  • 网络波动或服务重启时保持生产者稳定性。

常见八股文

  • Q:如何配置 RabbitMQ 的重连策略?
    A:通过 CachingConnectionFactory 设置 setRetrysetRecoveryInterval
  • Q:重连时如何避免消息重复?
    A:结合消息 ID 和数据库幂等处理。

发送者确认

知识总结

  • Confirm 确认:确认消息到达交换机。
  • Return 确认:确认消息未被路由到队列(需设置 mandatory)。

细节

  • 异步确认:通过 ConfirmCallback 处理大规模消息。
  • 消息丢失场景:需结合持久化和持久化确认。

使用场景

  • 需要保证消息到达交换机的场景(如订单通知)。

常见八股文

  • Q:Confirm 与 Return 的区别?
    A:Confirm 确认消息到达交换机,Return 确认消息未被路由到队列。
  • Q:如何实现消息的可靠投递?
    A:结合 Confirm、Return 和持久化,配合数据库记录状态。

MQ 持久化

知识总结

  • 队列持久化:设置 durable = true
  • 消息持久化:设置 messageProperties.setDeliveryMode(PERSISTENT)
  • 磁盘写入:需配合 镜像队列集群 避免单点故障。

细节

  • 持久化 ≠ 安全:需结合主从复制或集群保证高可用。
  • 性能影响:持久化会降低吞吐量,需权衡可靠性与性能。

使用场景

  • 重要业务消息(如支付、订单)需持久化。

常见八股文

  • Q:如何实现消息的持久化?
    A:队列设置 durable,消息设置 persistent,并启用磁盘写入。
  • Q:持久化消息丢失的可能原因?
    A:未配置持久化、磁盘故障、未启用主从复制。

LazyQueue(惰性队列)

知识总结

  • 特性:消息先存储在磁盘,延迟加载到内存,适合冷数据存储。
  • 配置:队列参数 x-queue-mode = lazy
  • 版本:3.12 版本后队列默认为惰性队列无法修改

细节

  • 吞吐量下降:磁盘读取速度低于内存,适合低频消费场景。
  • 内存占用:显著降低,适合存储大量不活跃消息。
  • 并发量:能够处理的并发量是上升的。

使用场景

  • 日志归档、历史数据存储。

常见八股文

  • Q:LazyQueue 与普通队列的区别?
    A:LazyQueue 消息存储磁盘,减少内存占用,但吞吐量低。
  • Q:何时使用 LazyQueue?
    A:消息需长期存储且消费频率低时。

消费者确认

知识总结

  • 自动确认autoAck = true,消费立即确认。
  • 手动确认autoAck = false,需显式调用 basicAck

细节

  • 未确认消息:消费者崩溃时消息重新入队(需幂等处理)。
  • 批量确认:通过 basicAckmultiple 参数优化性能。

使用场景

  • 需要确保消息被正确处理后再确认(如事务型操作)。

常见八股文

  • Q:手动确认 vs 自动确认?
    A:手动确认避免消息丢失,但需处理幂等;自动确认简单但可能丢失消息。
  • Q:如何实现批量确认?
    A:设置 multiple = true 并定期调用 basicAck

失败重试

知识总结

  • 死信队列(DLX):将失败消息路由到指定队列,由备用消费者处理。
  • 重试机制:通过 RetryTemplateSpring Retry 实现本地重试。

细节

  • 死信条件:需设置 x-dead-letter-exchangemax-delivery
  • 重试间隔:指数退避避免雪崩,需与业务逻辑匹配。

使用场景

  • 需要重试的失败消息(如网络波动、临时服务不可用)。

常见八股文

  • Q:如何配置死信队列?
    A:设置队列参数 x-dead-letter-exchangex-dead-letter-routing-key
  • Q:本地重试与死信队列的区别?
    A:本地重试在消费者端重试,死信队列将消息转移至其他队列处理。

业务幂等

知识总结

  • 核心思想:同一消息多次处理结果一致。
  • 实现方式:数据库唯一索引、Redis 记录消息 ID、版本号校验。

细节

  • 分布式幂等:需全局唯一 ID(如 UUID)和分布式锁。
  • 幂等与性能:如 Redis 记录需考虑缓存穿透和过期时间。

使用场景

  • 避免重复扣款、重复发券等业务场景。

常见八股文

  • Q:如何实现 RabbitMQ 消息的幂等性?
    A:通过消息 ID 记录(如 Redis)或数据库唯一约束。
  • Q:幂等与事务的区别?
    A:幂等关注结果一致性,事务关注操作的原子性。

延迟消息

知识总结

  • 实现方式
    1. 插件方式rabbitmq_delayed_message_exchange(需配置 x-delay)。
    2. 死信方式:通过 TTL + DLX 实现。
  • 插件配置:声明延迟交换机 x-delayed-message

细节

  • 插件依赖:需安装 rabbitmq_delayed_message_exchange 插件。
  • TTL 方案:需多个队列支持不同延迟时间。

使用场景

  • 定时任务(如优惠券过期通知、订单超时取消)。

常见八股文

  • Q:RabbitMQ 如何实现延迟消息?
    A:使用延迟插件或 TTL + DLX 方案。
  • Q:插件方案与 TTL 方案的优缺点?
    A:插件方案灵活(单消息延迟),TTL 需预设队列但性能更高。

AOP代理失效的情况


一、内部方法调用(this调用)

现象
  • 目标类中一个方法调用本类的另一个方法时,AOP不生效。
原因
  • AOP通过代理对象拦截方法调用,但this引用直接指向原始对象,绕过了代理。
解决方法
  1. 通过代理对象调用:从Spring容器中获取代理对象。
  2. 启用CGLIB代理:配置proxyTargetClass=true
示例
@Service
public class MyService {
    @Autowired
    private MyService proxyService; // 通过Spring注入代理对象
    
    public void methodA() {
        methodB(); // 失效(直接调用this的方法)
        proxyService.methodB(); // 生效(通过代理对象调用)
    }

    @Loggable // 自定义切面注解
    public void methodB() {}
}
// 配置CGLIB代理
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppConfig {}

二、静态方法调用

现象
  • 切面无法拦截静态方法。
原因
  • 静态方法属于类而非实例,代理对象无法覆盖静态方法。
解决方法
  • 将静态方法改为实例方法,并确保通过代理对象调用。
示例
public class MyService {
    public static void staticMethod() { // 无法被AOP拦截
        // ...
    }
    
    public void instanceMethod() { // 可被拦截
        // ...
    }
}

三、final方法或类

现象
  • 标有final的关键字的方法或类无法被AOP拦截。
原因
  • CGLIB无法生成子类覆盖final方法。
解决方法
  • 移除final修饰符。
示例
@Service
public class MyService {
    @Loggable
    public final void finalMethod() { // 失效(final方法)
        // ...
    }
}

四、私有方法(private)

现象
  • 私有方法无法被切面拦截。
原因
  • 代理对象无法访问私有方法。
解决方法
  • 将方法改为publicprotected或包级可见。
示例
@Service
public class MyService {
    @Loggable
    private void privateMethod() { // 失效(私有方法)
        // ...
    }
}

五、未正确配置AOP

现象
  • 切面类未被识别或切点表达式错误。
原因
  • 缺少@Aspect注解或未启用AOP支持。
解决方法
  1. 添加@Aspect到切面类。
  2. 确保主配置类有@EnableAspectJAutoProxy
示例
// ✅ 正确配置
@Aspect
@Component
public class LoggingAspect {
    @After("execution(* com.example..*(..))")
    public void log() {
        System.out.println("Logged!");
    }
}

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {}

六、目标对象未由Spring管理

现象
  • 手动new的对象无法触发AOP。
原因
  • 非Spring管理的对象没有被代理。
解决方法
  • 通过@AutowiredApplicationContext获取Bean。
示例
// ❌ 错误:手动new对象
MyService service = new MyService();
service.method(); // 无AOP

// ✅ 正确:通过Spring容器获取
@Autowired
private MyService service;
service.method(); // 有AOP

七、异步方法(@Async)

现象
  • 标有@Async的方法可能无法触发AOP。
原因
  • 异步方法由独立线程执行,代理逻辑可能未正确传递。
解决方法
  • 确保切面支持异步方法,或使用@EnableAsync并配置代理。
示例
@Service
public class MyService {
    @Async
    @Loggable
    public void asyncMethod() { // 需要特殊处理
        // ...
    }
}

八、切点表达式错误

现象
  • 切点未匹配到目标方法。
原因
  • 表达式语法错误或范围不匹配。
解决方法
  • 检查execution表达式是否正确。
示例
// ❌ 错误表达式:未匹配到包路径
@After("execution(* com.example.service..*(..))")

// ✅ 正确表达式:匹配所有service包的方法
@After("execution(* com.example.service.*.*(..))")

九、循环依赖

现象
  • 循环依赖可能导致AOP代理未正确生成。
原因
  • Spring在解决循环依赖时可能返回未增强的早期Bean引用。
解决方法
  • 重构代码消除循环依赖,或使用@Lazy延迟注入。
示例
@Service
public class ServiceA {
    @Autowired
    private ServiceB serviceB; // 可能导致循环依赖
}

@Service
public class ServiceB {
    @Autowired
    private ServiceA serviceA;
}

十、事务与AOP的冲突

现象
  • 事务注解@Transactional与AOP同时失效。
原因
  • 事务本身依赖AOP代理,若AOP失效则事务也失效。
解决方法
  • 确保事务方法在代理对象上调用(如Service层而非内部调用)。

排查步骤总结

  1. 检查配置:确认切面类有@Aspect,主类有@EnableAspectJAutoProxy
  2. 验证Bean管理:确保目标对象是Spring管理的Bean。
  3. 检查方法可见性:非public方法或final方法无法被拦截。
  4. 内部调用问题:通过代理对象调用方法,避免this直接调用。
  5. 切点表达式:确保表达式语法正确且匹配目标方法。
  6. 异步与事务:处理@Async@Transactional的特殊场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

~Yogi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值