加群联系作者vx:xiaoda0423
仓库地址:https://webvueblog.github.io/JavaPlusDoc/
https://1024bat.cn/
原问题 |
策略模式方案 |
---|---|
多个 if-else 处理不同事件类型 |
每种事件类型用一个独立的策略类处理 |
修改时容易误改其他逻辑 |
新增类型只需增加一个策略类 |
方法臃肿,难测试 |
单一职责原则,每个策略只管自己逻辑 |
swift
复制编辑
src/main/java/com/example/batteryswap/
├── BatterySwapApplication.java // 启动类
├── controller/
│ └── SwapEventController.java // 模拟外部请求入口
├── strategy/
│ ├── BatterySwapStrategy.java
│ ├── BatterySwapStrategyFactory.java
│ ├── LabelSwapStrategy.java
│ ├── DoorGetStrategy.java
│ └── ErrMsgSwapStrategy.java
├── service/
│ └── DummyBusinessService.java // 模拟服务类
├── model/
│ ├── CabinetsBizEvent.java
│ └── BExchSvcOrder.java
└── dto/
└── RestRet.java // 响应封装类
职责分离: 拆分常量类,按照业务维度归类。
避免魔法值: 可通过 enum
替代字符串常量,提高可读性与类型安全。
方法优化: 替换多重 if-else
为更清晰的策略/映射方式。
命名统一规范: 常量命名建议全部大写,并用 _
分隔词语。
高并发性能优化:
使用 computeIfAbsent
替代手动判断延迟队列是否存在,提高线程安全性。
避免重复调用 initDelayQueue
。
代码结构优化:
提取公共日志方法,减少重复。
分类注释清晰,增强可维护性。
可扩展性提升:
使用泛型支持更强类型检查。
提前暴露接口方法,提高使用灵活性。
线程安全与可用性优化:
delayedQueueMap
使用 ConcurrentHashMap
保证线程安全。
使用统一异常处理方式,方便故障排查。
优化点 |
描述 |
---|---|
缓存队列对象 | 避免重复初始化 |
双重检查锁 | 防止多线程下并发初始化,提高线程安全性和效率。 |
统一日志格式 | 清晰易懂,便于排查问题。 |
异步投递接口 | 提升吞吐能力,适用于高并发场景。 |
sendIfPresent 逻辑优化 |
移除已存在元素后重新添加,确保更新逻辑准确。 |
高扩展性结构 | 支持多个队列名称,便于扩展不同业务队列。 |
使用 ConcurrentHashMap
和 computeIfAbsent
,避免并发初始化导致队列重复创建。
使用 JsonJacksonCodec
,确保延迟消息的序列化在 Redis 分布式环境中兼容性强,支持多服务跨语言。
提供 sendAsync
和 sendAsyncIfAbsent
,适合高并发不阻塞场景。
支持动态创建多个队列,适合大规模系统中多个业务模块隔离使用。
后续可扩展为队列优先级、回调通知、队列消费监听等模块。
Redisson 内部会处理 Cluster 路由逻辑,避免手动分片。
/**
* 延迟消息生产者,基于 Redisson 实现高可用、高并发的分布式延迟队列
*/
@Component
public class DelayMessageProducer {
private static final Logger log = LoggerFactory.getLogger(DelayMessageProducer.class);
@Autowired
private RedissonClient redissonClient;
// 本地缓存已创建的延迟队列,提升性能,避免重复创建
private final Map> delayedQueueMap = new ConcurrentHashMap<>(4);
/**
* 初始化延迟队列,使用本地缓存减少 Redisson 重复创建开销
*/
private RDelayedQueue
springboot,springcloud启动过程
Spring Boot 的启动过程从 @SpringBootApplication
注解的类的 main
方法开始。该方法调用了 SpringApplication.run()
来启动整个 Spring Boot 应用。@SpringBootApplication
注解是一个组合注解,它包含了:
@Configuration
:表明该类是配置类。
@EnableAutoConfiguration
:启用 Spring Boot 的自动配置。
@ComponentScan
:启动组件扫描,扫描该类所在包及其子包中的 Bean。
SpringApplication.run()
是 Spring Boot 启动过程中的关键方法。它会完成以下任务:
创建并配置 SpringApplication
对象。
启动嵌入式的 Web 服务器(如 Tomcat、Jetty)。
初始化 Spring 容器(ApplicationContext)。
执行一些其他的初始化操作,比如命令行参数解析、环境配置等。
在执行 SpringApplication.run()
时,SpringApplication
会被初始化,它会做以下几件事:
设置 ApplicationContext
(默认是 AnnotationConfigApplicationContext
)。
设置 Banner
(Banner 是 Spring Boot 启动时显示的 ASCII 字符图标)。
设置 CommandLineRunner
和 ApplicationRunner
接口的 Bean,它们会在应用启动后执行。
Spring Boot 启动过程大致如下:
执行 main
方法,调用 SpringApplication.run()
启动应用。
初始化 SpringApplication
,加载配置。
创建并初始化 ApplicationContext
,扫描并注册 Bean。
自动配置系统根据依赖自动配置应用。
发布应用启动事件。
执行 CommandLineRunner
和 ApplicationRunner
中的代码(如果定义)。
启动完成,应用开始运行。
Spring Cloud 是基于 Spring Boot 构建的分布式系统的开发框架,旨在简化微服务架构的构建。它提供了很多用于微服务架构的解决方案,如服务发现、负载均衡、配置管理、消息驱动等。
Spring Cloud 的启动过程依赖于 Spring Boot 启动过程,但它有自己的一些额外步骤,主要用于处理服务注册与发现、配置管理、服务通信等。下面是 Spring Cloud 启动的主要过程:
与 Spring Boot 类似,Spring Cloud 的应用通常也从一个 main
方法开始。在这个方法中,调用 SpringApplication.run()
来启动应用。@SpringCloudApplication
注解是 Spring Cloud 的启动注解,它是一个组合注解,包含了:
@SpringBootApplication
:包含了 Spring Boot 启动的所有功能。
@EnableDiscoveryClient
或 @EnableEurekaClient
:启用服务发现客户端,这让服务能够注册到 Eureka 或其他服务注册中心。
@EnableCircuitBreaker
:启用熔断器机制,防止调用失败时影响其他服务。
Spring Cloud 是基于 Spring Boot 构建的分布式系统的开发框架,旨在简化微服务架构的构建。它提供了很多用于微服务架构的解决方案,如服务发现、负载均衡、配置管理、消息驱动等。
Spring Cloud 的启动过程依赖于 Spring Boot 启动过程,但它有自己的一些额外步骤,主要用于处理服务注册与发现、配置管理、服务通信等。下面是 Spring Cloud 启动的主要过程:
与 Spring Boot 类似,Spring Cloud 的应用通常也从一个 main
方法开始。在这个方法中,调用 SpringApplication.run()
来启动应用。@SpringCloudApplication
注解是 Spring Cloud 的启动注解,它是一个组合注解,包含了:
@SpringBootApplication
:包含了 Spring Boot 启动的所有功能。
@EnableDiscoveryClient
或 @EnableEurekaClient
:启用服务发现客户端,这让服务能够注册到 Eureka 或其他服务注册中心。
@EnableCircuitBreaker
:启用熔断器机制,防止调用失败时影响其他服务。
SpringApplication.run()
是 Spring Boot 启动的关键方法,也同样适用于 Spring Cloud,它会执行以下步骤:
创建并配置 SpringApplication
对象。
启动应用的上下文(ApplicationContext
)。
加载并初始化 Spring Boot 自动配置。
初始化服务注册中心,开始与服务发现交互。
在 Spring Cloud 中,如果应用涉及服务注册与发现(如 Eureka 或 Consul),这一步会连接到注册中心,进行服务注册。
如果应用是一个服务消费者或提供者,并且启用了服务发现机制(如 Eureka),则 Spring Cloud 会:
启动服务发现客户端(如 EurekaClient
),并自动注册到服务发现平台。
每个服务都会将其实例信息(如 IP 地址、端口等)注册到服务注册中心(如 Eureka)。
服务消费者在启动时会自动从服务注册中心获取服务列表,使用负载均衡进行调用。
Spring Cloud 提供了配置中心(如 Spring Cloud Config Server),允许将应用的配置从集中式的配置服务器中获取。Spring Cloud 在启动时会尝试连接配置服务器,并加载应用的配置。
如果启用了 Spring Cloud Config,启动时会从配置服务器获取配置信息(如数据库连接信息、微服务配置等),这些信息会被自动加载到 Spring 环境中。
Spring Cloud 还集成了 Netflix 的一些工具,如 Hystrix(熔断器)和 Ribbon(负载均衡),帮助处理服务调用的可靠性和负载均衡。启动时,Spring Cloud 会根据配置启动这些组件,确保服务调用的容错性。
Hystrix:通过 @EnableCircuitBreaker
注解启用熔断器,防止调用失败时影响其他服务。
Ribbon:启用客户端负载均衡,服务消费者会根据 Ribbon 自动选择一台合适的服务提供者。
Spring Cloud 启动过程的主要步骤如下:
执行 main
方法,调用 SpringApplication.run()
启动应用。
初始化 Spring Boot 的核心功能,加载配置。
启动服务注册与发现,服务注册到注册中心。
加载和初始化 Spring Cloud 相关功能(如配置中心、熔断器、负载均衡等)。
执行 CommandLineRunner
和 ApplicationRunner
中的代码(如果定义)。
启动完成,应用开始接受请求。
提高并发性能:
使用异步处理Kafka消息发送,避免在主线程中阻塞,提升性能。
可以通过增加并发线程池来处理多个消息,提高吞吐量。
提高高可用性:
如果Kafka发送失败,可以进行重试或者备用处理,以确保消息不丢失。
对于消息处理,可以考虑使用队列处理和死信队列的机制,避免消息丢失和延时。
优化代码逻辑:
代码中没有处理异常情况,我们可以增加异常处理和日志记录。
可以使用缓存机制减少频繁调用的数据库操作。
异步消息处理:
在 handle()
方法中,使用 ExecutorService
提交异步任务来处理 Kafka 消息的发送。这样可以避免消息处理阻塞主线程,提高系统的并发处理能力。
日志记录和异常处理:
增加了异常捕获和日志记录,确保在发生异常时能够捕获并记录,方便后续排查问题。
使用 Kafka 的 addCallback
方法来捕获消息发送成功与失败的回调,分别进行相应的日志记录。
ExecutorService 线程池:
通过 ExecutorService
使用线程池来处理高并发任务。线程池的大小可以根据实际业务需求进行调整(这里使用了一个固定大小的线程池,大小为10)。线程池能够有效地管理并发任务,避免因线程创建过多而消耗过多的资源。
提高可扩展性和高可用性:
通过异步操作和线程池,可以根据业务需要调整并发度,并发量增加时,只需要扩展线程池即可。
Kafka 发送失败时,增加了失败回调,可以根据需要做重试机制,或者将失败消息放入死信队列进行后续处理。
消息格式化:
使用 GsonUtils.getJsonFromObject(event)
将 OrderOverdueEvent
对象转化为 JSON 字符串。虽然这里没有修改,但可以根据需要优化 JSON 序列化方式。
代码:
@Component
public class OrderOverdueMessageHandler extends AbstractDelayMessageHandler {
private static final Logger log = LoggerFactory.getLogger(OrderOverdueMessageHandler.class);
@Autowired
@Qualifier("bizKafkaTemplate")
KafkaTemplate bizKafkaTemplate;
@Autowired
private ExecutorService executorService; // 用于处理异步消息发送
/**
* 订单逾期消息处理,异步发送到 Kafka
*
* @param message 订单ID
*/
@Override
public void handle(String message) {
// 异步执行发送Kafka的操作,避免阻塞主线程
executorService.submit(() -> {
try {
log.info("接收到订单逾期延时消息,订单Id:{}", message);
OrderOverdueEvent event = OrderOverdueEvent.builder()
.source(OrderOverdueSourceEnum.DELAY_QUEUE.getCode())
.orderId(message)
.build();
// 将事件发送到 Kafka
bizKafkaTemplate.send(KafkaConstant.ORDER_OVERDUE_TOPIC, GsonUtils.getJsonFromObject(event))
.addCallback(
result -> log.info("订单逾期消息成功发送到 Kafka, 订单Id:{}", message),
ex -> log.error("订单逾期消息发送到 Kafka 失败,订单Id:{}", message, ex)
);
} catch (Exception e) {
log.error("处理订单逾期消息时发生异常,订单Id:{}", message, e);
}
});
}
/**
* 获取队列名称
*
* @return 队列名称
*/
@Override
public String queueName() {
return RedissonConstant.ORDER_OVERDUE_DELAY_QUEUE;
}
/**
* 定义ExecutorService用于并发执行消息发送操作
*
* @return ExecutorService
*/
@Bean
public ExecutorService executorService() {
return Executors.newFixedThreadPool(10); // 创建固定大小的线程池,根据业务需求调整线程数
}
}
KafkaTemplate 注入:使用 @Autowired
注解注入了一个 KafkaTemplate
,并且通过 @Qualifier
指定了具体的 Kafka 模板(bizKafkaTemplate
)。
**消息处理方法 handle
**:
handle
方法接收一个 String
类型的消息(即订单 ID)。
创建一个 OrderOverdueEvent
对象,并将订单 ID 和事件来源(OrderOverdueSourceEnum.DELAY_QUEUE.getCode()
)等信息封装到事件中。
调用 bizKafkaTemplate.send
方法将事件对象转化为 JSON 字符串后,发送到 Kafka 中,使用的主题是 KafkaConstant.ORDER_OVERDUE_TOPIC
。
队列名称:
queueName()
方法返回了延时队列的名称,RedissonConstant.ORDER_OVERDUE_DELAY_QUEUE
,这个名称通常对应于 Redis 中的延时队列。
该类用于订单逾期的消息处理,特别是在延时消息处理的场景下。假设某个订单逾期了,并且该逾期事件被放入 Redis 的延时队列中,当延时到达时,这个类会从队列中取出订单 ID,然后生成一个事件并通过 Kafka 发送到相关系统,进行进一步的处理。
在 application.yml
或 application.properties
文件中配置 Kafka 和 Redis(或者使用默认配置):
Kafka 配置:
spring:
kafka:
producer:
bootstrap-servers: localhost:9092
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.apache.kafka.common.serialization.StringSerializer
Redis 配置(如果没有自动配置):
spring:
redis:
host: localhost
port: 6379
OrderOverdueMessageHandler
在你的服务或控制器中调用 OrderOverdueMessageHandler
,可以模拟一个订单逾期消息的处理:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class OrderOverdueService {
@Autowired
private OrderOverdueMessageHandler orderOverdueMessageHandler;
public void handleOrderOverdue(String orderId) {
// 调用 handler 处理订单逾期消息
orderOverdueMessageHandler.handle(orderId);
}
}
定时任务中定期检查订单状态,当发现订单逾期时,调用上面的服务方法处理逾期:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class OrderOverdueTask {
@Autowired
private OrderOverdueService orderOverdueService;
@Scheduled(fixedRate = 5000) // 每 5 秒检查一次
public void checkOrderOverdue() {
// 假设从数据库或其他地方获取到逾期订单的 ID
String orderId = "123456"; // 模拟订单 ID
orderOverdueService.handleOrderOverdue(orderId);
}
}
实际应用中应该有一个 Kafka 消费者来接收这些消息并进行处理。以下是一个简单的 Kafka 消费者示例:
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Service;
@Service
public class OrderOverdueConsumer {
@KafkaListener(topics = "order-overdue-topic", groupId = "order-overdue-group")
public void consume(String message) {
// 处理订单逾期事件
System.out.println("消费到订单逾期消息: " + message);
// 解析消息并做后续处理
}
}
你可以在 Kafka 的消费者端接收到订单逾期的消息,消息内容是一个 JSON 字符串,包含订单 ID 和来源信息。例如:
{
"source": "DELAY_QUEUE",
"orderId": "123456"
}
类别 |
优化内容 |
---|---|
✅ 可读性 |
使用 |
✅ 性能 |
避免重复调用、日志统一处理 |
✅ 安全性 |
finally 中解锁加 |
✅ 可维护 |
各种业务分支更清晰易扩展,核心流程注释明确 |