更新時間:2021-08-13 10:38:01 來源:動力節點 瀏覽1358次
<!-- redis依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
spring:
#Redis配置
redis:
host: localhost
password: 123456
接口類
/**
* @author 劉路生
*/
public interface SellService {
/**
* 根據商品ID搶購商品并且返回商品的搶購詳情
* @param productId
* @return
*/
String orderGoods(String productId);
/**
* 根據商品ID查詢商品搶購詳情
* @param productId
* @return
*/
String queryGoods(String productId);
}
實現類
@Service
@Slf4j
/**
* @author 劉路生
*/
public class SellServiceImpl implements SellService {
@Autowired
private RedisLock redisLock;
/**
設置超時時間10秒
*/
private static final int TIMEOUT = 10*1000;
/**
* 例如國慶大甩賣 圖書大甩賣 庫存 1000 件
*/
/**
* 庫存
*/
static Map<String, Integer> products;
/**
* 庫存余量
*/
static Map<String, Integer> stock;
/**
* 搶購成功者信息
*/
static Map<String, String> orders;
static {
products = new HashMap<>();
stock = new HashMap<>();
orders = new HashMap<>();
products.put("book", 1000);
stock.put("book", 1000);
}
public String queryMap(String productId){
return "國慶圖書大甩賣,庫存 " + products.get(productId) + " 件,現余 " + stock.get(productId) + " 件,已被搶購 " + orders.size() + " 件";
}
@Override
public String orderGoods(String productId) {
//先獲取商品余量
int number = stock.get(productId);
if(number == 0){
throw new RuntimeException("商品已搶購完,請您下次再來,謝謝您的理解...");
}else {
//模擬下單(不同用戶擁有不同ID)
orders.put(String.valueOf(UUID.randomUUID()), productId);
//減庫存
number = number - 1;
//模擬延遲
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
stock.put(productId, number);
}
log.info("共搶購 {} 件,搶購詳情:{}", orders.size(), orders);
//再返回商品的搶購詳情
return this.queryMap(productId);
}
@Override
public String queryGoods(String productId) {
return this.queryMap(productId);
}
}
/**
* @author 劉路生
*/
@RestController
public class SellController {
@Autowired
private SellService sellService;
/**
* 根據商品ID進行搶購
* @param productId
* @return 商品搶購詳情
*/
@GetMapping("/order/{productId}")
public String sellGoods(@PathVariable String productId){
return sellService.orderGoods(productId);
}
/**
* 根據商品ID進行查詢余量
* @param productId
* @return 商品搶購詳情
*/
@GetMapping("/query/{productId}")
public String queryGoods(@PathVariable String productId){
return sellService.queryGoods(productId);
}
}
使用 Apache ab 模擬高并發
ab -n 500 -c 80 http://localhost:8080/order/book
(1)實現Redis分布式鎖
/**
* @author 劉路生
*/
@Component
@Slf4j
public class RedisLock {
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 加鎖
* @param key
* @param value 當前時間+超時時間
* @return
*/
public boolean lock(String key, String value){
if(redisTemplate.opsForValue().setIfAbsent(key, value)){
return true;
}
String currentValue = redisTemplate.opsForValue().get(key);
//如果鎖過期
if(!StringUtils.isEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()){
//獲取上一個鎖的時間
String oldValue = redisTemplate.opsForValue().getAndSet(key, value);
if(!StringUtils.isEmpty(oldValue) && currentValue.equals(oldValue)){
return true;
}
}
return false;
}
/**
* 解鎖
* @param key
* @param value 當前時間+超時時間
*/
public void unlock(String key, String value){
try{
String currentValue = redisTemplate.opsForValue().get(key);
if(!StringUtils.isEmpty(currentValue) && currentValue.equals(value)){
redisTemplate.opsForValue().getOperations().delete(key);
}
}catch (Exception e){
log.error("【Redis分布式鎖】 解鎖異常 {}", e.getMessage());
}
}
}
(2)利用分布式鎖處理Service層方法
/**
* 第一種方法 synchronized 鎖機制,解決高并發產生的超賣問題 但效率大大降低 不推薦使用
* 第二種方法 使用 Redis 分布式鎖,解決高并發產生的超賣問題 并且效率相對高很多
*/
@Override
public String orderGoods(String productId) {
//加鎖
Long time = System.currentTimeMillis() + TIMEOUT;
//加鎖失敗 說明有人正在使用
if(!redisLock.lock(productId, String.valueOf(time))){
log.info("搶購失敗,請再試試吧...");
//return null;
throw new RuntimeException("服務器剛才好像睡著了,請再試試吧...");
}
//先獲取商品余量
int number = stock.get(productId);
if(number == 0){
throw new RuntimeException("商品已搶購完,請您下次再來,謝謝您的理解...");
}else {
//模擬下單(不同用戶擁有不同ID)
orders.put(String.valueOf(UUID.randomUUID()), productId);
//減庫存
number = number - 1;
//模擬延遲
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
stock.put(productId, number);
}
log.info("共搶購 {} 件,搶購詳情:{}", orders.size(), orders);
//解鎖
redisLock.unlock(productId, String.valueOf(time));
//再返回商品的搶購詳情
return this.queryMap(productId);
}
使用 Apache ab 模擬高并發
ab -n 500 -c 80 http://localhost:8080/order/book
瀏覽器顯示
控制臺打印
以上就是動力節點小編介紹的"利用Redis分布式鎖處理高并發",希望對大家有幫助,想了解更多可查看Java在線學習。動力節點在線學習教程,針對沒有任何Java基礎的讀者學習,讓你從入門到精通,主要介紹了一些Java基礎的核心知識,讓同學們更好更方便的學習和了解Java編程,感興趣的同學可以關注一下。
0基礎 0學費 15天面授
有基礎 直達就業
業余時間 高薪轉行
工作1~3年,加薪神器
工作3~5年,晉升架構
提交申請后,顧問老師會電話與您溝通安排學習