Redis应用(3)——Redis的项目应用(二):抢购图书 —> Redis高并发的问题 & 分布式锁Redission的使用

不管现实多么惨不忍睹,都要持之以恒地相信,这只是黎明前短暂的黑暗而已。不要惶恐眼前的难关迈不过去,不要担心此刻的付出没有回报,别再花时间等待天降好运。真诚做人,努力做事!你想要的,岁月都会给你。Redis应用(3)——Redis的项目应用(二):抢购图书 —> Redis高并发的问题 & 分布式锁Redission的使用,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

引出


1.Redis是线程安全的,但是高并发时出现数据不安全的问题;
2.解决办法,加锁,Redission分布式锁的使用;
3.Redis项目应用,图书的抢购,不加锁,数据不安全;
4.加了Redission中间件,保证原子性,数据安全;

Redis的高并发问题

在这里插入图片描述

redis的高并发问题

在这里插入图片描述

Redisson中间件

Redisson是一个基于Redis的Java驻留内存数据网格(In-Memory Data Grid)。它封装了Redis客户端API,并提供了一个分布式锁、分布式集合、分布式对象、分布式Map等常用的数据结构和服务。

引入Redisson

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.21.3</version>
</dependency>

Redisson配置

@Configuration
public class RedissionConfig {
    @Value("${spring.redis.host}")
    private String host;
   // @Bean
    public RedissonClient redissonClient(){
        Config config = new Config();
        config.setTransportMode(TransportMode.EPOLL);
        config.useSingleServer().setAddress("redis://192.168.198.130:6379");
        return Redisson.create(config);
    }
}

Redisson应用

@Autowired
private RedissonClient redissonClient;

// 1.获取锁对象
RLock myLock = redissonClient.getLock("myLock");

try{
	// 2.准备锁代码,并进行加锁
	myLock.lock();
    
}finally{
    // 3.解除锁
    myLock.unlock(); // 把锁释放
}
报错:java.lang.NoClassDefFoundErro

java.lang.NoClassDefFoundError: org/springframework/data/redis/connection/zset/Tuple

org.springframework.data.redis.connection.zset.Tuple

在这里插入图片描述

        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>3.9.1</version>
        </dependency>

Redis的项目应用(二):抢购图书

1.0版本,Java代码:数据不安全

controller层的代码

package com.tianju.redisDemo.controller;

import com.tianju.redisDemo.dto.HttpResp;
import com.tianju.redisDemo.dto.ResultCode;
import com.tianju.redisDemo.service.IBookService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;

@Controller
@RequestMapping("/api/book")
@Slf4j
public class BookController {

    @Autowired
    private IBookService bookService;

    @GetMapping("/rush")
    public HttpResp rushBook(String key){
        Integer rushBuy = bookService.rushBuy(key);
        // 如果为null,说明图书还没进入抢购的队列
        if (rushBuy==null){
            return HttpResp.results(ResultCode.BOOK_RUSH_ERROR,new Date(),"图书还不能秒杀抢购");
        }
        log.debug("》》》》图书剩余库存:"+rushBuy);
        return HttpResp.results(ResultCode.BOOK_RUSH_SUCCESS,new Date(),null);

    }
}

service层的代码

package com.tianju.redisDemo.service.impl;

import com.tianju.redisDemo.service.IBookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class IBookServiceImpl implements IBookService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public Integer rushBuy(String key) {
        String numStr = stringRedisTemplate.opsForValue().get(key);
        if (numStr==null){
            return null; // 商品剩余数量还没有进入缓存,不能抢购
        }
        int num = Integer.parseInt(numStr);
        // 如果库存数量大于1;执行-1,去库存操作
        if (num>0){
            num--; // 去库存
            // 更新当前库存数量
            stringRedisTemplate.opsForValue().set(key, String.valueOf(num));
        }
        return num;
    }
}

application.yml配置文件

server:
  port: 9099

spring:
  # redis的相关配置
  redis:
    host: localhost
    port: 6379
    database: 0


# 日志需要配置一下
logging:
  level:
    com.tianju.redisDemo: debug

测试方法

1.用client方法测试

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

2.用JMeter进行高并发测试

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

问题:redis出现了数据不安全的情况

在这里插入图片描述

在这里插入图片描述

2.0版本,改进:加锁分布式锁Redission,保证原子性

Redisson中间件

Redisson是一个基于Redis的Java驻留内存数据网格(In-Memory Data Grid)。它封装了Redis客户端API,并提供了一个分布式锁、分布式集合、分布式对象、分布式Map等常用的数据结构和服务。

要点:

  • 获取锁对象;
  • 准备锁代码;
  • 进行加锁;
  • 解除锁,把锁释放;
@Autowired
private RedissonClient redissonClient;

@Override
public Integer rushBuy(String key) {
    // 1.获取锁对象
    RLock myLock = redissonClient.getLock("myLock");
    try {
        // 2.准备锁代码,并进行加锁
        myLock.lock();
        // 进行图书的抢购,去库存 -1
        }
        return num;
    } finally {
        // 3.解除锁
        myLock.unlock(); // 把锁释放

在这里插入图片描述

1.导包+配置类

<!--        分布式锁-->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>3.9.1</version>
        </dependency>

RedissonConfig.java

package com.tianju.springboot.config;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RedissonConfig {
    @Value("${spring.redis.host}")
    private String host;

    @Bean // 别人写的对象,放到spring中
    public RedissonClient redissonClient(){
        Config config = new Config();
        // "redis://192.168.198.130:6379"
        config.useSingleServer().setAddress("redis://"+host+":6379");

        return Redisson.create(config);
    }
}

2.进行加锁

package com.tianju.redisDemo.service.impl;

import com.tianju.redisDemo.service.IBookService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class IBookServiceImpl implements IBookService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    @Override
    public Integer rushBuy(String key) {
        // 1.获取锁对象
        RLock myLock = redissonClient.getLock("myLock");

        try {
            // 2.准备锁代码,并进行加锁
            myLock.lock();

            // 进行图书的抢购,去库存 -1
            String numStr = stringRedisTemplate.opsForValue().get(key);
            if (numStr==null){
                return null; // 商品剩余数量还没有进入缓存,不能抢购
            }
            int num = Integer.parseInt(numStr);
            // 如果库存数量大于1;执行-1,去库存操作
            if (num>0){
                num--; // 去库存
                // 更新当前库存数量
                stringRedisTemplate.opsForValue().set(key, String.valueOf(num));
            }
            return num;
        } finally {
            // 3.解除锁
            myLock.unlock(); // 把锁释放

        }
    }
}

3.高并发测试

在这里插入图片描述


总结

1.Redis是线程安全的,但是高并发时出现数据不安全的问题;
2.解决办法,加锁,Redission分布式锁的使用;
3.Redis项目应用,图书的抢购,不加锁,数据不安全;
4.加了Redission中间件,保证原子性,数据安全;

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/165098.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!