SpringCloud實戰:redisson 分散式鎖案例
阿新 • • 發佈:2018-12-17
對前面講解 redisson 實現分散式鎖的文章做個補充(上篇文章地址),上篇文章測試的不太準確,本篇將使用jmeter專業測試工具,模擬併發請求
背景:啟動 redis,6379埠,SpringCloud微服務,模擬秒殺搶購場景,100庫存,用jmeter測試,起300個執行緒併發請求2次,總計600個請求數,最後檢視庫存是否為負數,證明分散式鎖是否鎖住了庫存。
- 註冊中心 10025埠
- 消費者服務 9700埠
- 秒殺服務 8083、8084,啟動2個服務
測試流程如下:
- 1.啟動註冊中心,消費者服務與秒殺服務都註冊到註冊中心
- 2.啟動消費者服務,通過feign 以負載均衡方式呼叫秒殺服務
- 3.啟動秒殺服務,請求秒殺服務時,修改商品庫存,商品庫存儲存在redis中,預設100
- 4下面是整體架構圖
庫存設定為100
通過jmeter執行http請求,模擬使用者搶購
檢視結果,從jmeter上可以看到有600個請求,有13個失敗了
再看redis中的庫存,為0,說明鎖住了,沒有發生超賣現象
總共測試了多次,貼出有代表性的:
- 1秒內啟動 200執行緒,迴圈請求2次,總計400請求,成功了幾十個請求,其餘請求被hystrix降級返回了
- 2秒內啟動 300執行緒,迴圈請求2次,總計600請求,請求全部成功,庫存為0
- 3秒內啟動 500執行緒,迴圈請求2次,總計1000請求,請求全部成功,庫存為0
- 5秒內啟動 1000執行緒,迴圈請求2次,總計2000請求,請求全部成功,庫存為0
說明在1秒內啟動200個執行緒併發請求,程式無法處理過來
再貼出秒殺服務的主要程式碼,註冊中心與消費者服務的程式碼請檢視 SpringCloud 實戰系列
秒殺服務主要依賴了 redisson 與 netty , 其他都是微服務開發需引入的
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!--web 模組--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.6.5</version> </dependency> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.25.Final</version> </dependency>
啟動主類的程式碼如下:
@EnableDiscoveryClient
@SpringBootApplication
public class DistributedLockApplication {
public static void main(String[] args) {
SpringApplication.run(DistributedLockApplication.class, args);
}
//新增redisson的bean
@Bean
public Redisson redisson(){
Config config = new Config();
//此示例是單庫的,可以是主從、sentinel、叢集等模式
config.useSingleServer().setAddress("redis://localhost:6379");
return (Redisson)Redisson.create(config);
}
}
然後是提供秒殺功能的控制器
@RestController
public class IndexController {
private static String commodityCount = "commodityCount";//商品key
private static String lockKey = "testRedisson";//分散式鎖的key
@Autowired private StringRedisTemplate redisTemplate;
@Autowired private Redisson redisson;
/**
* 查詢是否健康
* @return
*/
@RequestMapping(value = "/health" , method = RequestMethod.GET)
public String health(){
return "health";
}
/**
* 設定商品數量為100個
* @param value
* @return
*/
@RequestMapping("/setValue")
public String setValue(int value){
redisTemplate.opsForValue().set(commodityCount , value + "");
return "success";
}
/**
* 模擬秒殺搶購,併發200個請求過來,檢視是否出現超賣
* @return
*/
@RequestMapping("/spike")
public String spike(){
String flag = "success";
RLock lock = redisson.getLock(lockKey);
try{
//lock.lockAsync(5 , TimeUnit.SECONDS);
//lock.lock(5, TimeUnit.SECONDS); //設定60秒自動釋放鎖 (預設是30秒自動過期)
Future<Boolean> res = lock.tryLockAsync(100, 5, TimeUnit.SECONDS);
boolean result = res.get();
System.out.println("result:" + result);
if(result){
int stock = Integer.parseInt(redisTemplate.opsForValue().get(commodityCount).toString());
if(stock > 0){
redisTemplate.opsForValue().set(commodityCount,(stock-1)+"");
}else{
flag = "fail";
}
}
}catch (Exception e){
e.printStackTrace();
}
finally{
lock.unlock(); //釋放鎖
}
return flag;
}
}
原始碼已上傳至碼雲,獲取原始碼
redisson config配置參考:https://github.com/redisson/redisson/wiki/2.-Configuration#261-single-instance-settings