Building a High‑Performance Seckill System with SpringBoot, RabbitMQ and Redis (Full Code)
This article walks through the design and implementation of a complete seckill (flash‑sale) system built on SpringBoot, MyBatis, MySQL, RabbitMQ and Redis, covering double MD5 password hashing, distributed sessions, unified exception handling, caching strategies, memory flags, pre‑decrement inventory, asynchronous order processing, oversell prevention, and rate limiting, with code snippets and UI screenshots.
Project Overview
The reference implementation is a seckill (flash‑sale) system built with SpringBoot. Core functions include login, product list, product detail, seckill purchase, and order detail. Backend stack: SpringBoot, MyBatis, MySQL, RabbitMQ, Redis. Frontend stack: HTML, jQuery, Thymeleaf.
Implementation Details
1. Double MD5 Password Encryption
First MD5 protects the password during network transmission; a second MD5 protects the password if the database is compromised.
2. Distributed Session Management
After successful authentication a UUID token is generated. The token is stored in Redis as the key with the user object as the value, and the same token is written to a cookie. Subsequent requests read the token from the cookie and retrieve the user object from Redis.
3. Unified Exception Handling
All exceptions are intercepted by a custom interceptor and processed uniformly using @ControllerAdvice together with @ExceptionHandler(value=Exception.class).
4. Page Cache + Object Cache
Page cache: rendered HTML pages are cached in Redis; later requests for the same page are served directly from Redis, reducing server load.
Object cache: hot objects such as user, product and order are cached in Redis. At application startup the seckill products are pre‑warmed into Redis.
5. Page Staticization
The front‑end uses AJAX to fetch data asynchronously. After the first load both the page markup and the data are cached in the browser, so subsequent visits load instantly from the client cache.
6. Memory Flag + Redis Pre‑Decrement + RabbitMQ Asynchronous Processing
private Map<Long, Boolean> localOverMap = new HashMap<Long, Boolean>();
// memory flag, reduces Redis access
boolean over = localOverMap.get(goodsId);
if (over) {
return Result.error(CodeMsg.MIAO_SHA_OVER);
}
// Redis pre‑decrement
long stock = redisService.decr(GoodsKey.getSeckillGoodsStock, "" + goodsId);
if (stock < 0) {
localOverMap.put(goodsId, true);
return Result.error(CodeMsg.MIAO_SHA_OVER);
}
// check if user already ordered
SeckillOrder order = orderService.getOrderByUserIdGoodsId(user.getId(), goodsId);
if (order != null) {
return Result.error(CodeMsg.REPEATE_MIAOSHA);
}
// push message to queue
SeckillMessage sm = new SeckillMessage();
sm.setUser(user);
sm.setGoodsId(goodsId);
sender.sendSeckillMessage(sm);Workflow:
When a seckill request arrives, the local map is consulted first. If the product is already marked sold out, the request returns CodeMsg.MIAO_SHA_OVER.
If not sold out, the Redis counter is decremented. A negative result sets the local flag to true and returns the same error.
The service checks whether the user has already placed an order; if so, CodeMsg.REPEATE_MIAOSHA is returned.
If the checks pass, a SeckillMessage containing the user and product ID is sent to RabbitMQ for asynchronous order processing.
Product and inventory data are pre‑loaded into Redis at startup via InitializingBean.afterPropertiesSet. RabbitMQ processes the messages, while the client polls an order‑result endpoint until success or failure.
7. Oversell Prevention
(1) SQL update that succeeds only when stock > 0:
update seckill_goods set stock_count = stock_count - 1
where goods_id = #{goodsId} and stock_count > 0(2) Unique index on (user_id, goods_id) prevents a single user from ordering the same product twice.
(3) Optimistic lock: a version column is added to the product table; each update increments the version and includes the current version in the WHERE clause. If a version conflict occurs, the update is retried a limited number of times.
8. Interface Rate Limiting
Requests are limited by recording the number of accesses per user within a time window. A custom @AccessLimit annotation is applied to controller methods.
@AccessLimit(seconds=5, maxCount=5, needLogin=true)
@RequestMapping(value="/path", method=RequestMethod.GET)
@ResponseBody
public Result<String> getSeckillPath(User user, @RequestParam("goodsId") long goodsId) {
if (user == null) {
return Result.error(CodeMsg.USER_NO_LOGIN);
}
String path = seckillService.createPath(user, goodsId);
return Result.success(path);
}Repository
https://gitee.com/jike11231/sec-kill-product
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
SpringMeng
Focused on software development, sharing source code and tutorials for various systems.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
