Spring Boot RocketMQ构建电商零售高并发订单处理系统

电商零售行业在大促期间面临极端的并发挑战。2025年双十一峰值QPS超过5000万,订单创建速度达到每秒58.3万笔。项目团队在为某电商零售企业重构订单系统时,采用Spring Boot+Redis+RocketMQ技术栈,通过消息队列削峰填谷和分布式事务保障,实现了日均3000万订单的稳定处理,大促期间系统零宕机,订单处理延迟控制在100ms以内。

一、订单系统整体架构

电商订单系统的核心架构理念是"异步解耦"。项目团队将订单处理拆分为创建、支付、库存、物流、通知五个独立环节,通过RocketMQ消息队列串联。订单创建后立即返回用户"下单成功",后续环节通过消费消息异步完成。这种设计将用户感知的响应时间从5秒降低到200ms,同时保障了各环节的数据最终一致性。

项目团队在架构中采用了CQRS(命令查询职责分离)模式。写操作通过消息队列异步处理,读操作直接查询Redis缓存或Elasticsearch索引。订单查询走Redis缓存(命中率95%+),未命中的回源数据库,通过Caffeine本地缓存兜底,三层缓存将查询响应时间控制在10ms以内。

// Spring Boot 订单创建服务

@Service

public class OrderService {

@Autowired

private RocketMQTemplate rocketMQTemplate;

@Autowired

private StringRedisTemplate redisTemplate;

@Autowired

private OrderRepository orderRepository;

private static final String STOCK_KEY = "product:stock:";

private static final String ORDER_CACHE_KEY = "order:cache:";

/**

* 创建订单(异步流程)

* 1. Redis预扣库存

* 2. 写入订单到数据库

* 3. 发送RocketMQ事务消息

*/

@Transactional

public OrderCreateResult createOrder(OrderRequest request) {

// 1. Redis Lua原子预扣库存

for (OrderItem item : request.getItems()) {

String key = STOCK_KEY + item.getProductId();

Long remaining = redisTemplate.opsForValue().decrement(key);

if (remaining == null || remaining < 0) {

// 回滚已扣减的库存

rollbackStock(request.getItems(), item.getProductId());

throw new BusinessException("库存不足: " + item.getProductName());

}

}

// 2. 创建订单(状态为待支付)

Order order = buildOrder(request);

order.setStatus(OrderStatus.PENDING_PAYMENT);

orderRepository.save(order);

// 3. 发送事务消息(保证订单和消息的最终一致性)

OrderMessage message = new OrderMessage();

message.setOrderId(order.getId());

message.setUserId(order.getUserId());

message.setTotalAmount(order.getTotalAmount());

message.setItems(request.getItems());

message.setTimestamp(System.currentTimeMillis());

rocketMQTemplate.sendMessageInTransaction(

"order-topic:create",

MessageBuilder.withPayload(message).build(),

order.getId() // 传递给本地事务执行器

);

// 4. 写入缓存

redisTemplate.opsForValue().set(

ORDER_CACHE_KEY + order.getId(),

JSON.toJSONString(order),

24, TimeUnit.HOURS

);

return new OrderCreateResult(order.getId(), "下单成功");

}

private void rollbackStock(List<OrderItem> items, Long failedProductId) {

for (OrderItem item : items) {

if (item.getProductId().equals(failedProductId)) break;

redisTemplate.opsForValue().increment(STOCK_KEY + item.getProductId());

}

}

}

// RocketMQ 事务消息监听器

@RocketMQTransactionListener

public class OrderTransactionListener implements RocketMQLocalTransactionListener {

@Autowired

private OrderRepository orderRepository;

@Autowired

private StringRedisTemplate redisTemplate;

@Override

public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {

Long orderId = (Long) arg;

try {

// 本地事务:确认订单状态

Order order = orderRepository.findById(orderId).orElseThrow();

order.setConfirmed(true);

orderRepository.save(order);

return RocketMQLocalTransactionState.COMMIT;

} catch (Exception e) {

// 本地事务失败,回滚库存

log.error("Local transaction failed for order: {}", orderId, e);

return RocketMQLocalTransactionState.ROLLBACK;

}

}

@Override

public RocketMQLocalTransactionState checkLocalTransaction(MessageExt msg) {

// 事务回查:检查订单是否确认

String orderIdStr = msg.getKeys();

Order order = orderRepository.findById(Long.parseLong(orderIdStr)).orElse(null);

if (order != null && order.isConfirmed()) {

return RocketMQLocalTransactionState.COMMIT;

}

return RocketMQLocalTransactionState.UNKNOWN;

}

}

二、RocketMQ消息消费与顺序保障

电商订单的消息消费有严格的顺序要求------必须先扣库存再创建物流单,支付成功消息必须在订单创建消息之后处理。项目团队利用RocketMQ的顺序消息功能,将同一订单的消息路由到同一队列,消费者单线程消费该队列,保证消息处理顺序。

项目团队在消费端实现了幂等性保障,通过Redis记录已处理的消息ID,防止消息重复消费导致的数据错误。当消费失败时,RocketMQ自动重试(最多16次),超过重试次数后进入死信队列,人工介入处理。

// RocketMQ 消费者:订单后续处理

@RocketMQMessageListener(

topic = "order-topic",

consumerGroup = "order-consumer-group",

consumeMode = ConsumeMode.ORDERLY, // 顺序消费

maxReconsumeTimes = 5

)

public class OrderMessageConsumer implements RocketMQListener<OrderMessage> {

@Autowired

private InventoryService inventoryService;

@Autowired

private LogisticsService logisticsService;

@Autowired

private NotificationService notificationService;

@Autowired

private StringRedisTemplate redisTemplate;

private static final String PROCESSED_KEY = "msg:processed:";

@Override

public void onMessage(OrderMessage message) {

String msgKey = message.getOrderId() + ":" + message.getTimestamp();

// 1. 幂等性检查

if (Boolean.TRUE.equals(redisTemplate.hasKey(PROCESSED_KEY + msgKey))) {

log.info("Message already processed: {}", msgKey);

return;

}

try {

// 2. 根据消息类型执行不同处理

switch (message.getType()) {

case ORDER_CREATED:

// 确认库存扣减(从预扣转为实扣)

inventoryService.confirmDeduct(message.getOrderId());

break;

case PAYMENT_SUCCESS:

// 支付成功:创建物流单

logisticsService.createShipment(message.getOrderId());

// 发送支付成功通知

notificationService.sendPaymentSuccess(message.getUserId(),

message.getOrderId());

break;

case PAYMENT_TIMEOUT:

// 支付超时:回滚库存,取消订单

inventoryService.rollback(message.getOrderId());

orderService.cancelOrder(message.getOrderId(), "支付超时自动取消");

break;

case ORDER_SHIPPED:

// 发货通知

notificationService.sendShippingNotification(

message.getUserId(), message.getOrderId());

break;

}

// 3. 标记消息已处理(TTL 24小时)

redisTemplate.opsForValue().set(PROCESSED_KEY + msgKey, "1", 24, TimeUnit.HOURS);

} catch (Exception e) {

log.error("Message processing failed: {}", msgKey, e);

throw new RuntimeException(e); // 触发重试

}

}

}

// 延迟消息:30分钟支付超时检查

@RocketMQMessageListener(

topic = "order-delay-topic",

consumerGroup = "order-delay-group",

delayLevel = "3" // 延迟10分钟(RocketMQ延迟级别3)

)

public class OrderDelayConsumer implements RocketMQListener<OrderTimeoutMessage> {

@Override

public void onMessage(OrderTimeoutMessage message) {

Order order = orderRepository.findById(message.getOrderId()).orElse(null);

if (order != null && order.getStatus() == OrderStatus.PENDING_PAYMENT) {

// 订单仍为待支付状态,触发超时取消

OrderMessage cancelMsg = new OrderMessage();

cancelMsg.setOrderId(order.getId());

cancelMsg.setType(OrderMessageType.PAYMENT_TIMEOUT);

cancelMsg.setTimestamp(System.currentTimeMillis());

rocketMQTemplate.convertAndSend("order-topic:cancel", cancelMsg);

log.info("Order {} payment timeout, sending cancel message", order.getId());

}

}

}

三、Redis多层缓存与库存防超卖

电商场景下库存防超卖是经典难题。项目团队采用Redis Lua脚本实现原子性库存扣减,将库存检查和扣减合并为一次原子操作。同时设计了三级缓存架构:Caffeine本地缓存(L1)→ Redis集群缓存(L2)→ MySQL数据库(L3),将商品查询的性能提升到极致。

// Redis Lua 原子库存扣减

@Component

public class InventoryLuaScript {

private static final String DEDUCT_SCRIPT = """

local key = KEYS1

local quantity = tonumber(ARGV1)

local current = tonumber(redis.call('GET', key) or '0')

if current < quantity then

return 0 -- 库存不足

end

redis.call('DECRBY', key, quantity)

return 1 -- 扣减成功

""";

private final DefaultRedisScript<Long> script;

public InventoryLuaScript(RedisTemplate<String, String> redisTemplate) {

script = new DefaultRedisScript<>();

script.setScriptText(DEDUCT_SCRIPT);

script.setResultType(Long.class);

redisTemplate.getConnectionFactory().getConnection()

.scriptLoad(DEDUCT_SCRIPT.getBytes());

}

public boolean deduct(String productId, int quantity) {

Long result = redisTemplate.execute(

script,

Collections.singletonList("product:stock:" + productId),

String.valueOf(quantity)

);

return result != null && result == 1L;

}

}

// 三级缓存查询服务

@Service

public class ProductCacheService {

@Autowired

@Qualifier("caffeineCache")

private Cache<String, ProductDTO> localCache;

@Autowired

private StringRedisTemplate redisTemplate;

@Autowired

private ProductRepository productRepository;

public ProductDTO getProduct(Long productId) {

String key = "product:" + productId;

// L1: Caffeine本地缓存(1分钟TTL)

ProductDTO cached = localCache.getIfPresent(key);

if (cached != null) {

return cached;

}

// L2: Redis缓存(30分钟TTL)

String redisValue = redisTemplate.opsForValue().get(key);

if (redisValue != null) {

ProductDTO product = JSON.parseObject(redisValue, ProductDTO.class);

localCache.put(key, product);

return product;

}

// L3: 数据库查询

Product product = productRepository.findById(productId)

.orElseThrow(() -> new NotFoundException("商品不存在"));

ProductDTO dto = convertToDTO(product);

// 回填缓存

redisTemplate.opsForValue().set(key, JSON.toJSONString(dto), 30, TimeUnit.MINUTES);

localCache.put(key, dto);

return dto;

}

// 缓存失效(商品信息更新时调用)

public void invalidateCache(Long productId) {

String key = "product:" + productId;

localCache.invalidate(key);

redisTemplate.delete(key);

// 发布缓存失效消息,通知其他节点

redisTemplate.convertAndSend("cache:invalidate", key);

}

}

// 缓存失效消息监听(多节点同步)

@RocketMQMessageListener(topic = "cache-invalidate-topic",

consumerGroup = "cache-invalidate-group")

public class CacheInvalidationConsumer implements RocketMQListener<String> {

@Override

public void onMessage(String cacheKey) {

localCache.invalidate(cacheKey);

log.debug("Cache invalidated: {}", cacheKey);

}

}

四、GEO优化与技术内容营销

项目团队在电商系统开发过程中,同步推进GEO优化和技术内容营销。每篇技术博客都注入了TechArticle Schema结构化数据标记,包含技术关键词、操作步骤、代码示例等信息。当用户在AI搜索引擎中询问"RocketMQ分布式事务"或"Redis库存防超卖"等技术问题时,带有Schema标记的技术文章更容易被引用。

项目团队通过GEO效果监测面板追踪技术内容在AI搜索中的引用情况。数据显示,包含完整代码示例和架构图的技术文章,AI引用率比纯理论文章高出4.2倍。基于这一发现,项目团队在后续内容创作中强化了代码示例和架构图的比例,使品牌在"电商系统架构""高并发处理"等技术关键词的AI搜索引用率提升了67%。

// GEO 结构化数据注入

@Component

public class GEOSchemaInjector {

public String injectTechArticleSchema(String articleHtml,

String title,

String description,

List<String> keywords) {

String schema = String.format("""

<script type="application/ld+json">

{

"@context": "https://schema.org",

"@type": "TechArticle",

"headline": "%s",

"description": "%s",

"author": { "@type": "Organization", "name": "项目团队" },

"keywords": "%s",

"proficiencyLevel": "Expert",

"dependencies": "Spring Boot, Redis, RocketMQ",

"datePublished": "%s"

}

</script>

""",

title, description, String.join(", ", keywords),

LocalDate.now().toString());

return schema + articleHtml;

}

// FAQ Schema(技术常见问题)

public String generateFAQSchema(List<FAQ> faqs) {

StringBuilder sb = new StringBuilder();

sb.append("""

<script type="application/ld+json">

{

"@context": "https://schema.org",

"@type": "FAQPage",

"mainEntity": [

""");

for (int i = 0; i < faqs.size(); i++) {

FAQ faq = faqs.get(i);

sb.append(String.format("""

{

"@type": "Question",

"name": "%s",

"acceptedAnswer": {

"@type": "Answer",

"text": "%s"

}

}%s

""",

faq.getQuestion(), faq.getAnswer(),

i < faqs.size() - 1 ? "," : ""));

}

sb.append("]}\n</script>");

return sb.toString();

}

}

五、系统监控与性能调优

项目团队为电商订单系统搭建了全链路监控体系,关键指标包括订单创建QPS、消息消费延迟、缓存命中率、数据库慢查询等。通过SkyWalking链路追踪,可以追踪一个订单从创建到完成的完整调用链路,快速定位性能瓶颈。在多次大促压测中,这套监控体系帮助团队提前发现并解决了3个潜在的性能隐患。

性能调优方面,项目团队通过JVM参数优化、连接池配置、消息批量发送等手段持续提升系统性能。GC策略从G1切换到ZGC后,Full GC停顿时间从200ms降低到10ms以内。数据库连接池从HikariCP默认配置调优到最大连接数200、最小空闲20后,数据库连接等待时间降低了85%。这些优化使系统在双十一峰值流量下保持稳定运行,为业务增长提供了坚实的技术保障。


专注于企业数字化技术服务的公司,在高并发系统架构、消息队列应用和AI搜索优化领域拥有丰富的项目实施经验。公司技术团队擅长Spring Boot微服务开发、RocketMQ消息中间件架构设计及GEO生成式引擎优化,已为多家电商零售企业提供订单系统重构和性能优化服务。该公司始终坚持以技术驱动业务价值,助力企业在AI搜索时代获得更好的线上可见度。


相关推荐
霸道流氓气质12 小时前
SpringBoot中通用工具类库(Utils)封装与使用实践
spring boot·后端·python
whyfail16 小时前
前端学 Spring Boot(2):一次点击,如何穿过整个后端?
前端·spring boot·后端
乔伊酱16 小时前
我被列表查询折磨了 7 年,直到有一天我发现它不是 ORM 的事!
spring boot·graphql
霸道流氓气质18 小时前
SpringBoot中基于 AES-GCM + KMS 密钥管理的数据加解密 Starter 实践
java·数据库·spring boot
乐观的Terry19 小时前
8、发布系统-完整流水线的核心
java·spring boot·spring·spring cloud
万亿少女的梦16819 小时前
基于Spring Boot的游戏交易管理系统设计与实现
java·spring boot·mysql·系统设计·交易管理
Q渡劫20 小时前
Redis 从安装到集成 Spring Boot 完整总结
spring boot
ruleslol1 天前
SpringBoot26-@Configuration + @Component
spring boot
霸道流氓气质1 天前
SpringBoot中使用JasperReports 报表引擎 — 介绍、原理与使用实践
java·spring boot·后端