springboot使用Redisson實現分布式鎖


1.Redisson介紹

Redisson是Redis官方推薦的Java版的Redis客戶端。它提供的功能非常多,也非常強大,此處我們只用它的分布式鎖功能。

https://github.com/redisson/redisson

2.實現分布式鎖

 <dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.15.0</version>
 </dependency>
server:
  port: 8070
spring:
  application:
    name: dkn-provider-store
  jackson:
    default-property-inclusion: non_null
    date-format: YYYY-MM-dd HH:mm:ss
    time-zone: GMT+8
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/dkn-provider-store?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
    username: root
    password: root

logging:
  level:
    com.dkn: debug
    org.springframework.web: trace

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
-- ----------------------------
-- Table structure for store
-- ----------------------------
DROP TABLE IF EXISTS `store`;
CREATE TABLE `store` (
  `id` int(11) NOT NULL COMMENT '商品id',
  `num` int(11) DEFAULT NULL COMMENT '庫存數量',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Records of store
-- ----------------------------
INSERT INTO `store` VALUES ('101', '100');
@Configuration
public class RedissonConfig {
    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        RedissonClient redisson = Redisson.create(config);
        return redisson;
    }

}
@Service
public class StoreServiceImpl extends ServiceImpl<StoreMapper,Store> implements StoreService {

    @Autowired
    RedissonClient redissonClient;

    //沒有任何鎖
    @Override
    public void buy1(Integer id, Integer num) {
        Store store = this.getById(id);

        //模擬業務處理休息50毫秒
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        store.setNum(store.getNum()-1);
        this.updateById(store);
    }

    //同步
    @Override
    public synchronized void buy2(Integer id, Integer num) {
        Store store = this.getById(id);

        //模擬業務處理休息50毫秒
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        store.setNum(store.getNum()-1);
        this.updateById(store);
    }

    //分布式鎖
    @Override
    public void buy3(Integer id, Integer num) {
        String lockKey = "buy:product:" + id;
        RLock lock = redissonClient.getLock(lockKey);
        try {
            boolean res = lock.tryLock(10, TimeUnit.SECONDS);
            if (res) {
                Store store = this.getById(id);

                //模擬業務處理休息50毫秒
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                store.setNum(store.getNum()-1);
                this.updateById(store);
            }
        } catch (Exception ex) {
            log.error("獲取所超時!", ex);
        } finally {
            lock.unlock();
        }
    }

}
@RestController
@RequestMapping("/Store")
public class StoreController {

	@Autowired
	private StoreService storeService;

	//模擬購買商品 沒有任何鎖
	@GetMapping("buy1")
	public AjaxResult buy1(Integer id, Integer num){
		storeService.buy1(id,num);
		return AjaxResult.success();
	}

	//模擬購買商品 synchronized本地同步鎖
	@GetMapping("buy2")
	public AjaxResult buy2(Integer id, Integer num){
		storeService.buy2(id,num);
		return AjaxResult.success();
	}

	//模擬購買商品 redisson分布鎖
	@GetMapping("buy3")
	public AjaxResult buy3(Integer id, Integer num){
		storeService.buy3(id,num);
		return AjaxResult.success();
	}
	
}

3.測試

@SpringBootTest
@RunWith(SpringRunner.class)
public class StoreBuyTest {

    @Autowired
    RestTemplate restTemplate;

    //測試結果100個商品,庫存只減少1個,應該減少50個
    @Test
    public void testBuy1(){
        //模擬50個用戶同時購買一件物品
        ThreadPoolExecutor executor=new ThreadPoolExecutor(50,50,60, TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());

        for(int i=0;i<50;i++) {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("===============================");
                    restTemplate.getForEntity("http://localhost:8070/Store/buy1?id=101&num=1", String.class);
                }
            });
        }

        while (true){
            if(executor.getQueue().size()==0 && executor.getActiveCount()==0){
                executor.shutdown();
                break;
            }

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("end~");
    }

    //單個服務測試-->測試結果正確,測試結果100個商品,庫存只減少50個
    @Test
    public void testBuy2_1(){
        //模擬50個用戶同時購買一件物品
        ThreadPoolExecutor executor=new ThreadPoolExecutor(50,50,60, TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());

        for(int i=0;i<50;i++) {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("===============================");
                    restTemplate.getForEntity("http://localhost:8070/Store/buy2?id=101&num=1", String.class);
                }
            });
        }

        while (true){
            if(executor.getQueue().size()==0 && executor.getActiveCount()==0){
                executor.shutdown();
                break;
            }

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("end~");
    }

    //啟動2個服務測試,端口分別是8070,8071-->測試結果不正常,測試結果100個商品,庫存只減少26個,應該減少50個
    @Test
    public void testBuy2_2(){
        //模擬50個用戶同時購買一件物品
        ThreadPoolExecutor executor=new ThreadPoolExecutor(50,50,60, TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());

        for(int i=0;i<50;i++) {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("===============================");
                    if(RandomUtils.nextInt(0,100)<50){
                        restTemplate.getForEntity("http://localhost:8070/Store/buy2?id=101&num=1", String.class);
                    }else{
                        restTemplate.getForEntity("http://localhost:8071/Store/buy2?id=101&num=1", String.class);
                    }
                }
            });
        }

        while (true){
            if(executor.getQueue().size()==0 && executor.getActiveCount()==0){
                executor.shutdown();
                break;
            }

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("end~");
    }

    //啟動2個服務測試,端口分別是8070,8071-->測試結果正確,測試結果100個商品,庫存減少50個
    @Test
    public void testBuy3(){
        //模擬50個用戶同時購買一件物品
        ThreadPoolExecutor executor=new ThreadPoolExecutor(50,50,60, TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());

        for(int i=0;i<50;i++) {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("===============================");
                    if(RandomUtils.nextInt(0,100)<50){
                        restTemplate.getForEntity("http://localhost:8070/Store/buy3?id=101&num=1", String.class);
                    }else{
                        restTemplate.getForEntity("http://localhost:8071/Store/buy3?id=101&num=1", String.class);
                    }
                }
            });
        }

        while (true){
            if(executor.getQueue().size()==0 && executor.getActiveCount()==0){
                executor.shutdown();
                break;
            }

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("end~");
    }


}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM