每日学习Java之一万个为什么
文章目录
- Docker
- **一、基础命令**
- **1. 启动/停止Docker服务**
- **2. 查看Docker信息**
- **二、镜像操作**
- **1. 拉取镜像**
- **2. 列出本地镜像**
- **3. 删除镜像**
- **4. 构建镜像**
- **5. 推送镜像**
- **三、容器操作**
- **1. 创建并启动容器**
- **2. 列出容器**
- **3. 管理容器生命周期**
- **4. 进入容器**
- **5. 查看容器日志**
- **四、网络操作**
- **1. 网络管理**
- **五、卷(Volume)操作**
- **1. 数据卷管理**
- **六、系统维护**
- **1. 清理资源**
- **2. 查看磁盘占用**
- **七、其他实用命令**
- **1. 容器操作**
- **2. 帮助与文档**
- **八、进阶命令示例**
- **1. 端口映射与挂载卷**
- **2. 资源限制**
- **九、常见问题命令**
- **1. 修复Docker进程占用过高**
- **2. 查找未命名的容器**
- **附录:快速参考表**
- Gateway
- 技术对比
- 路由转发配置
- 路由属性 RouterDefinition
- 网关实现登录校验(JWT)
- 自定义GatewayFilter
- 网关处理请求的流程(责任链)
- 网关过滤器
- Sentinel
- 配置信息
- 1. **熔断降级配置**
- (1) 基础熔断配置(Java代码)
- (2) 动态规则配置(Nacos集成)
- (3) 热点参数限流(秒杀场景)
- 2. **流控模式与效果**
- (1) **Warm Up(预热)配置**
- (2) **排队等待**
- 微服务集成
- 1. **Spring Cloud集成步骤**
- (1) 依赖添加
- (2) Feign与Sentinel整合
- (3) 全局异常处理
- 2. **最佳实践**
- Seata(分布式事务)
- XA模式
- 1. **XA模式原理**
- 2. **Seata XA模式配置**
- AT模式
- 1. **AT模式原理**
- 2. **Seata AT模式配置**
- **XA模式 vs AT模式对比**
- 总结
- RabbitMQ
- 同步和异步
- MQ技术选型
- 数据隔离
- 知识总结
- 注意
- 使用场景
- 常见八股文
- Spring AMQP
- 知识总结
- 细节
- 使用场景
- 常见八股文
- Work 模式(工作队列)
- 知识总结
- 细节
- 使用场景
- 常见八股文
- MQ 消息转换器
- 知识总结
- 细节
- 使用场景
- 常见八股文
- 发布订阅模式(Pub/Sub)
- 知识总结
- 细节
- 使用场景
- 常见八股文
- 消息堆积问题处理
- 知识总结
- 细节
- 使用场景
- 常见八股文
- 发送者重连
- 知识总结
- 细节
- 使用场景
- 常见八股文
- 发送者确认
- 知识总结
- 细节
- 使用场景
- 常见八股文
- MQ 持久化
- 知识总结
- 细节
- 使用场景
- 常见八股文
- LazyQueue(惰性队列)
- 知识总结
- 细节
- 使用场景
- 常见八股文
- 消费者确认
- 知识总结
- 细节
- 使用场景
- 常见八股文
- 失败重试
- 知识总结
- 细节
- 使用场景
- 常见八股文
- 业务幂等
- 知识总结
- 细节
- 使用场景
- 常见八股文
- 延迟消息
- 知识总结
- 细节
- 使用场景
- 常见八股文
- AOP代理失效的情况
- **一、内部方法调用(this调用)**
- **现象**
- **原因**
- **解决方法**
- **示例**
- **二、静态方法调用**
- **现象**
- **原因**
- **解决方法**
- **示例**
- **三、final方法或类**
- **现象**
- **原因**
- **解决方法**
- **示例**
- **四、私有方法(private)**
- **现象**
- **原因**
- **解决方法**
- **示例**
- **五、未正确配置AOP**
- **现象**
- **原因**
- **解决方法**
- **示例**
- **六、目标对象未由Spring管理**
- **现象**
- **原因**
- **解决方法**
- **示例**
- **七、异步方法(@Async)**
- **现象**
- **原因**
- **解决方法**
- **示例**
- **八、切点表达式错误**
- **现象**
- **原因**
- **解决方法**
- **示例**
- **九、循环依赖**
- **现象**
- **原因**
- **解决方法**
- **示例**
- **十、事务与AOP的冲突**
- **现象**
- **原因**
- **解决方法**
- **排查步骤总结**
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:8848dataId: ${spring.application.name}-sentinel-rulesgroupId: DEFAULT_GROUPrule-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(应用):发起事务请求。
-
流程:
- 准备阶段:AP向TM提交事务,TM通知各RM准备(锁资源)。
- 提交阶段: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模式原理
- 自动补偿机制:通过异步提交和快照对比实现最终一致性。
- 流程:
- 预写阶段:记录业务数据的前后快照。
- 提交阶段:直接提交业务数据,异步对比快照完成最终提交或回滚。
- 特点:
- 无侵入性:无需数据库支持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):逻辑隔离不同业务的数据,每个虚拟主机独立管理队列、交换机和权限。
- 用户权限:通过用户与虚拟主机的绑定实现权限隔离,用户只能操作关联虚拟主机内的资源。
注意
- 默认虚拟主机:
/
是默认虚拟主机,需谨慎使用。 - 权限配置:需显式授予用户对虚拟主机的
configure
、write
、read
权限。
使用场景
- 多项目/团队共享同一 RabbitMQ 集群时,避免数据互相干扰。
- 敏感业务需独立资源管理(如金融、日志系统)。
常见八股文
- Q:如何实现 RabbitMQ 的数据隔离?
A:通过创建独立虚拟主机,并为不同用户分配对应虚拟主机的权限。 - Q:虚拟主机与用户权限的关系?
A:用户需绑定到虚拟主机,且需显式授予操作权限(如set_permissions
)。
Spring AMQP
知识总结
- 核心组件:
RabbitTemplate
(发送消息)、@RabbitListener
(消费消息)、MessageConverter
(消息转换)。 - 自动声明:通过配置自动创建队列、交换机及绑定关系。
细节
- 消息序列化:默认使用
JdkSerializationConverter
,生产环境推荐Jackson2JsonMessageConverter
,两者不兼容。 - 事务与确认:需显式配置
publisher-confirms
或publisher-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
接口,覆盖toMessage
和fromMessage
方法。 - Q:为什么 JSON 转换器比默认序列化更好?
A:JSON 更易读、体积小,且支持跨语言解析。
发布订阅模式(Pub/Sub)
知识总结
- 特性:消息被广播到所有绑定的队列,每个消费者独立消费一份副本。
- 交换机类型:
fanout
(无路由键)、topic
、direct
、headers
。
细节
- 临时队列:消费者可声明
exclusive
队列,断开后自动删除。 - 路由规则:
topic
支持通配符(*
匹配一个词,#
匹配多个词)。
使用场景
- 实时通知(如系统告警、消息广播)。
常见八股文
- Q:如何实现精准匹配的 Pub/Sub?
A:使用direct
交换机,通过路由键精确匹配队列。 - Q:fanout 与 topic 的区别?
A:fanout 不区分路由键,消息广播给所有绑定队列;topic 支持通配符路由。
消息堆积问题处理
知识总结
- 原因:生产速度 > 消费速度,或消费者故障。
- 解决方案:扩容消费者、优化消费逻辑、调整预取数、使用优先级队列。
细节
- 预取数(prefetch-count):过高可能导致内存溢出,过低影响吞吐。
- 死信队列:设置
max-length
或ttl
,将未及时消费的消息转存。
使用场景
- 高峰流量(如秒杀、促销活动)时的流量削峰。
常见八股文
- Q:如何快速缓解消息堆积?
A:扩容消费者、优化消费逻辑、调整预取数、启用死信队列。 - Q:消息堆积时如何保证消息不丢失?
A:确保消息持久化、消费者确认模式为manual/auto
。
发送者重连
知识总结
- 机制:RabbitMQ 客户端通过
Retry
机制自动重连。 - 配置参数:
connection-factory
的recovery-interval
、automatic-recovery-enabled
细节
- 重连间隔:默认 5 秒,需根据业务调整。
- 未确认消息:重连后需手动重发未确认的消息(如使用
PublisherCallbackChannel
)。
使用场景
- 网络波动或服务重启时保持生产者稳定性。
常见八股文
- Q:如何配置 RabbitMQ 的重连策略?
A:通过CachingConnectionFactory
设置setRetry
和setRecoveryInterval
。 - 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
。
细节
- 未确认消息:消费者崩溃时消息重新入队(需幂等处理)。
- 批量确认:通过
basicAck
的multiple
参数优化性能。
使用场景
- 需要确保消息被正确处理后再确认(如事务型操作)。
常见八股文
- Q:手动确认 vs 自动确认?
A:手动确认避免消息丢失,但需处理幂等;自动确认简单但可能丢失消息。 - Q:如何实现批量确认?
A:设置multiple = true
并定期调用basicAck
。
失败重试
知识总结
- 死信队列(DLX):将失败消息路由到指定队列,由备用消费者处理。
- 重试机制:通过
RetryTemplate
或Spring Retry
实现本地重试。
细节
- 死信条件:需设置
x-dead-letter-exchange
和max-delivery
。 - 重试间隔:指数退避避免雪崩,需与业务逻辑匹配。
使用场景
- 需要重试的失败消息(如网络波动、临时服务不可用)。
常见八股文
- Q:如何配置死信队列?
A:设置队列参数x-dead-letter-exchange
和x-dead-letter-routing-key
。 - Q:本地重试与死信队列的区别?
A:本地重试在消费者端重试,死信队列将消息转移至其他队列处理。
业务幂等
知识总结
- 核心思想:同一消息多次处理结果一致。
- 实现方式:数据库唯一索引、Redis 记录消息 ID、版本号校验。
细节
- 分布式幂等:需全局唯一 ID(如
UUID
)和分布式锁。 - 幂等与性能:如 Redis 记录需考虑缓存穿透和过期时间。
使用场景
- 避免重复扣款、重复发券等业务场景。
常见八股文
- Q:如何实现 RabbitMQ 消息的幂等性?
A:通过消息 ID 记录(如 Redis)或数据库唯一约束。 - Q:幂等与事务的区别?
A:幂等关注结果一致性,事务关注操作的原子性。
延迟消息
知识总结
- 实现方式:
- 插件方式:
rabbitmq_delayed_message_exchange
(需配置x-delay
)。 - 死信方式:通过 TTL + DLX 实现。
- 插件方式:
- 插件配置:声明延迟交换机
x-delayed-message
。
细节
- 插件依赖:需安装
rabbitmq_delayed_message_exchange
插件。 - TTL 方案:需多个队列支持不同延迟时间。
使用场景
- 定时任务(如优惠券过期通知、订单超时取消)。
常见八股文
- Q:RabbitMQ 如何实现延迟消息?
A:使用延迟插件或 TTL + DLX 方案。 - Q:插件方案与 TTL 方案的优缺点?
A:插件方案灵活(单消息延迟),TTL 需预设队列但性能更高。
AOP代理失效的情况
一、内部方法调用(this调用)
现象
- 目标类中一个方法调用本类的另一个方法时,AOP不生效。
原因
- AOP通过代理对象拦截方法调用,但
this
引用直接指向原始对象,绕过了代理。
解决方法
- 通过代理对象调用:从Spring容器中获取代理对象。
- 启用CGLIB代理:配置
proxyTargetClass=true
。
示例
@Service
public class MyService {@Autowiredprivate 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 {@Loggablepublic final void finalMethod() { // 失效(final方法)// ...}
}
四、私有方法(private)
现象
- 私有方法无法被切面拦截。
原因
- 代理对象无法访问私有方法。
解决方法
- 将方法改为
public
、protected
或包级可见。
示例
@Service
public class MyService {@Loggableprivate void privateMethod() { // 失效(私有方法)// ...}
}
五、未正确配置AOP
现象
- 切面类未被识别或切点表达式错误。
原因
- 缺少
@Aspect
注解或未启用AOP支持。
解决方法
- 添加
@Aspect
到切面类。 - 确保主配置类有
@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管理的对象没有被代理。
解决方法
- 通过
@Autowired
或ApplicationContext
获取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@Loggablepublic void asyncMethod() { // 需要特殊处理// ...}
}
八、切点表达式错误
现象
- 切点未匹配到目标方法。
原因
- 表达式语法错误或范围不匹配。
解决方法
- 检查
execution
表达式是否正确。
示例
// ❌ 错误表达式:未匹配到包路径
@After("execution(* com.example.service..*(..))")// ✅ 正确表达式:匹配所有service包的方法
@After("execution(* com.example.service.*.*(..))")
九、循环依赖
现象
- 循环依赖可能导致AOP代理未正确生成。
原因
- Spring在解决循环依赖时可能返回未增强的早期Bean引用。
解决方法
- 重构代码消除循环依赖,或使用
@Lazy
延迟注入。
示例
@Service
public class ServiceA {@Autowiredprivate ServiceB serviceB; // 可能导致循环依赖
}@Service
public class ServiceB {@Autowiredprivate ServiceA serviceA;
}
十、事务与AOP的冲突
现象
- 事务注解
@Transactional
与AOP同时失效。
原因
- 事务本身依赖AOP代理,若AOP失效则事务也失效。
解决方法
- 确保事务方法在代理对象上调用(如Service层而非内部调用)。
排查步骤总结
- 检查配置:确认切面类有
@Aspect
,主类有@EnableAspectJAutoProxy
。 - 验证Bean管理:确保目标对象是Spring管理的Bean。
- 检查方法可见性:非
public
方法或final
方法无法被拦截。 - 内部调用问题:通过代理对象调用方法,避免
this
直接调用。 - 切点表达式:确保表达式语法正确且匹配目标方法。
- 异步与事务:处理
@Async
或@Transactional
的特殊场景。