1. 程式人生 > >SpringCloud實戰:redisson 分散式鎖案例

SpringCloud實戰:redisson 分散式鎖案例

對前面講解 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