IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> redis分布式锁和简单秒杀限流的实现 -> 正文阅读

[Java知识库]redis分布式锁和简单秒杀限流的实现

分布式锁

?看门狗防止死锁

redission初始化

spring环境

? <!--整合redission框架start-->
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.springframework.boot</groupId>
? ? ? ? ? ? <artifactId>spring-boot-starter-data-redis</artifactId>
? ? ? ? </dependency>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.redisson</groupId>
? ? ? ? ? ? <artifactId>redisson-spring-boot-starter</artifactId>
? ? ? ? ? ? <version>3.12.5</version>
? ? ? ? </dependency>
? ? ? ? <!--整合redission框架enc-->
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.apache.commons</groupId>
? ? ? ? ? ? <artifactId>commons-lang3</artifactId>
? ? ? ? </dependency>

yml配置

spring:
? #redisson配置,默认连接库0,无密码只配置连接地址即可
? redis:
? ? host: 127.0.0.1
? ? database: 0

? ?#如果没有设置密码,下面这个需要删除
? ? password:

非spring环境

?redission使用案例_毕业即失业吗的博客-CSDN博客_redission

依赖

<dependency>
? ? <groupId>org.redisson</groupId>
? ? <artifactId>redisson</artifactId>
? ? <version>version</version>
</dependency>

初始化代码?

//单机
RedissonClient redisson = Redisson.create();
Config config = new Config();
config.useSingleServer().setAddress("myredisserver:6379");
RedissonClient redisson = Redisson.create(config);


//主从

Config config = new Config();
config.useMasterSlaveServers()
? ? .setMasterAddress("127.0.0.1:6379")
? ? .addSlaveAddress("127.0.0.1:6389", "127.0.0.1:6332", "127.0.0.1:6419")
? ? .addSlaveAddress("127.0.0.1:6399");
RedissonClient redisson = Redisson.create(config);


//哨兵
Config config = new Config();
config.useSentinelServers()
? ? .setMasterName("mymaster")
? ? .addSentinelAddress("127.0.0.1:26389", "127.0.0.1:26379")
? ? .addSentinelAddress("127.0.0.1:26319");
RedissonClient redisson = Redisson.create(config);


//集群
Config config = new Config();
config.useClusterServers()
? ? .setScanInterval(2000) // cluster state scan interval in milliseconds
? ? .addNodeAddress("127.0.0.1:7000", "127.0.0.1:7001")
? ? .addNodeAddress("127.0.0.1:7002");
RedissonClient redisson = Redisson.create(config);

?

普通锁

@Controller
public class TestRedissonClient {
    @Autowired
    RedissonClient redisson;

    @ResponseBody
    @GetMapping("/hello")
    public String hello(){
        // 1、获取一把锁,只要锁的名字一样,既是同一把锁
        RLock lock = redisson.getLock ("my-lock");

        // 2、加锁
        lock.lock ();// 阻塞式等待

        try {
            System.out.println ("加锁成功,执行业务..."+Thread.currentThread ().getId () );
            // 模拟超长等待
            Thread.sleep (20000);
        } catch (Exception e) {
            e.printStackTrace ( );
        }finally {
            // 3、解锁
            System.out.println ("释放锁..."+Thread.currentThread ().getId () );
            lock.unlock ();
        }
        return "hello";
    }
}
 ???//1. 普通的可重入锁
    RLock lock = redissonClient.getLock("generalLock");
 
    // 拿锁失败时会不停的重试
    // 具有Watch Dog 自动延期机制 默认续30s 每隔30/3=10 秒续到30s
    lock.lock();
 
    // 尝试拿锁10s后停止重试,返回false
    // 具有Watch Dog 自动延期机制 默认续30s
    boolean res1 = lock.tryLock(10, TimeUnit.SECONDS);
 
    // 拿锁失败时会不停的重试
    // 没有Watch Dog ,10s后自动释放
    lock.lock(10, TimeUnit.SECONDS);
 
    // 尝试拿锁100s后停止重试,返回false
    // 没有Watch Dog ,10s后自动释放
    boolean res2 = lock.tryLock(100, 10, TimeUnit.SECONDS);

公平锁

?//2. 公平锁 保证 Redisson 客户端线程将以其请求的顺序获得锁
? ? RLock fairLock = redissonClient.getFairLock("fairLock");

读写锁

?? ?//3. 读写锁 没错与JDK中ReentrantLock的读写锁效果一样
? ? RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("readWriteLock");
? ? readWriteLock.readLock().lock();
? ? readWriteLock.writeLock().lock();

结论:

保证一定能读到最新数据,修改期间,写锁是一个排它锁(互斥锁)。读锁是一个共享锁
(1)读+读:相当于无锁,并发读情况下,只会在 redis 中记录好,所有当前的读锁,他们都会加锁成功。
(2)写+读:等待写锁释放
(3)写+写:阻塞方式
(4)读+写:有读锁,写也需要等待

只要有写的存在,都必须等待

信号量:Semaphore

    /**
     * 车库停车:
     * 信号量也可以做分布式限流
     * @return
     */
    @GetMapping("/park")
    public String park()throws Exception{
        RSemaphore park = redisson.getSemaphore ("park");

        // 获取一个信号,获取一个值,占一个车位
        //park.acquire ();
        //park.acquire (23);// 占用23个

        // 如果有数量则占用,没有则失败
        boolean b = park.tryAcquire ( );
        if (b){
            // 执行业务
        }else {
            return "没有可以占用的,失败";
        }
        return "占用成功,当前剩余车可占用量:"+park.availablePermits ();
    }



    @GetMapping("/go")
    public String go(){
        RSemaphore park = redisson.getSemaphore ("park");
        //park.release ();// 开发一个车位
        park.release (30);// 开发 30个车位
        return "增加车位成功";
    }

闭锁:CountDownLatch

    /**
     * 模拟 5 个班级人走完,学校放假(闭校)
     * @return
     * @throws Exception
     */
    @GetMapping("lockDoor")
    public String lockDoor()throws Exception{
        RCountDownLatch door = redisson.getCountDownLatch ("door");
        door.trySetCount (5);
        door.await ();// 等待闭锁都完成
        return "放假了";
    }

    @GetMapping("/gogo/{id}")
    public String gogo(@PathVariable("id")String id){
        RCountDownLatch door = redisson.getCountDownLatch ("door");
        door.countDown ();// 计数-1
        long count = door.getCount ( );
        return id+"班走完,剩余:"+count;
    }

RedissonRedLock

分布式锁缺点

RedLock加锁机制

由于分布式锁在哨兵模式下存在的缺点,因此在多redis实例的情况下,引入RedLock,保证锁的可靠性。

Redisson提供了RedissonRedLock锁实现了RedLock,需要同时使用多个独立的Redis实例分别进行加锁,只有超过一半的锁加锁成功,则认为是成功加锁。
?

使用场景:
多个服务间保证同一时刻同一时间段内同一用户只能有一个请求(防止关键业务出现并发攻击);
这个锁的算法实现了多redis实例的情况,相对于单redis节点来说,优点在于 防止了 单节点故障造成整个服务停止运行的情况;并且在多节点中锁的设计,及多节点同时崩溃等各种意外情况有自己独特的设计方法;

代码示例

RLock lock1 = redissonInstance1.getLock("lock1");
RLock lock2 = redissonInstance2.getLock("lock2");
RLock lock3 = redissonInstance3.getLock("lock3");
 
RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);
// 同时加锁:lock1 lock2 lock3
// 红锁在大部分节点上加锁成功就算成功。
lock.lock();
...
lock.unlock();

秒杀

秒杀最主要是要解决超卖和超时问题。

核心示例

@RestController
public class indexController {
    @Autowired
    private Redisson redisson;
    @Autowired
    private  StringRedisTemplate stringRedisTemplate;

    @RequestMapping("/deduct_stock")
    public String deductStock(){

        String lockKey = "lockKey";//简单的不完善的分布式状态锁
        RLock reLock = redisson.getLock(lockKey);
        try {
            reLock.lock();//相当于setIfAbsent("lockKey", "zhujian",10, TimeUnit.SECONDS);
            int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
            if(stock > 0){
                int realStock = stock - 1;
                stringRedisTemplate.opsForValue().set("stock",realStock + " ");
                System.out.println("扣减成功,剩余库存:"+realStock);
            }else {
                System.out.println("扣减失败,库存不足");
            }
        } finally {
            reLock.unlock();
        }
        return "end";
    }
}

限流

我们是有面临高并发下需要对接口或者业务逻辑限流的问题,我们可以采用Guaua依赖下的RateLimiter 实现,实际上,Redisssion也有类似的限流功能。
RateLimiter 被称为令牌桶限流,此类限流是首先定义好一个令牌桶,指明在一定时间内生成多少个令牌,每次访问时从令牌桶获取指定数量令牌,如果获取成功,则设为有效访问。

1.获取限流实例

?2.设置令牌桶规则

设置令牌桶规则,例如 1分钟秒内,生成6个有效令牌

?3.对限流的业务进行令牌获取尝试

// 尝试获取令牌 底层默认是获取一个令牌
boolean tryAcquire();

// 尝试获取指定令牌
boolean tryAcquire(long permits);

// 一定时间内尝试获取1个令牌
boolean tryAcquire(long timeout, TimeUnit unit);

4.限流实战

RateType.OVERALL 表示针对所有客户端

RRateLimiter rateLimiter;  
@PostConstruct
public void initRateLimiter(){
    RRateLimiter ra = redissonClient.getRateLimiter("rate-limiter");
    ra.setRate(RateType.OVERALL, 6, 1, RateIntervalUnit.MINUTES);
    rateLimiter = ra;
}
---------
@GetMapping("/rate/limiter")
public String testRateLimiter() {
    return lockService.testRateLimiter();
}
---------
public String testRateLimiter() {
    boolean b = rateLimiter.tryAcquire();
    if (b) {
        return "ok";
    }
    return "fail";
}

5.规则设置注意事项

// setRate 我们项目服务重启,就会强制重置之前的限流配置与状态,以当前为准
ra.setRate(RateType.OVERALL, 6, 1, RateIntervalUnit.MINUTES);

// trySetRate 我们项目服务重启,不会更新限流配置与限流状态,但参数更改后亦不会生效!比如之前是十分钟内颁布令牌100个,更改为5分钟内颁布令牌30个并不会生效
ra.trySetRate(RateType.OVERALL, 7, 2, RateIntervalUnit.MINUTES);

事务

为RMap、RMapCache、RLocalCachedMap、RSet、RSetCache和RBucket这样的对象提供了具有ACID属性的事务功能Redisson事务通过分布式锁保证了连续写入的原子性,同时在内部通过操作指令队列实现了Redis原本没有的提交与滚回功能当提交与滚回遇到问题的时候,将通过org.redisson.transaction.TransactionException告知用户
?

示例

package com.demo.redis.transaction;

import org.redisson.api.RBucket;
import org.redisson.api.RTransaction;
import org.redisson.api.RedissonClient;
import org.redisson.api.TransactionOptions;
import org.redisson.client.codec.StringCodec;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import javax.annotation.Resource;

/**
 * RedisTransaction
 *
 * @author wangmingcong
 */
@Component
public class RedisTransaction {

    @Resource
    private RedissonClient redissonClient;

    /**
     * 获取 RTransaction
     *
     * @return 返回 值
     */
    public RTransaction getTransaction() {
        RTransaction transaction = redissonClient.createTransaction(TransactionOptions.defaults());
        Assert.notNull(transaction, "transaction is null");
        return transaction;
    }

    /**
     * 获取事务
     *
     * @param name 名称
     * @return 返回 RBucket
     */
    public RBucket<String> getTransactionBucket(String name) {
        RBucket<String> bucket = this.getTransaction().getBucket(name, StringCodec.INSTANCE);
        Assert.notNull(bucket, "bucket is null");
        return bucket;
    }

    /**
     * 事务提交
     *
     * @param transaction 事务
     */
    public void commit(RTransaction transaction) {
        transaction.commit();
    }

    /**
     * 事务提交 (异步)
     *
     * @param transaction 事务
     */
    public void commitAsync(RTransaction transaction) {
        transaction.commitAsync();
    }

    /**
     * 事务 回滚
     *
     * @param transaction 事务
     */
    public void rollback(RTransaction transaction) {
        transaction.rollback();
    }

    /**
     * 事务 回滚 (异步)
     *
     * @param transaction 事务
     */
    public void rollbackAsync(RTransaction transaction) {
        transaction.rollbackAsync();
    }
}

package com.demo.redis.transaction;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.redisson.api.RBucket;
import org.redisson.api.RTransaction;
import org.redisson.client.codec.StringCodec;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;

@Slf4j
@SpringBootTest
@RunWith(SpringRunner.class)
public class RedisTransactionTest {

    private static final String NAME = "demo:redisTransaction";

    @Resource
    private RedisTransaction redisTransaction;

    @Test
    public void test() {
        // 创建事务
        RTransaction transaction = redisTransaction.getTransaction();
        try {
            // 执行事务的命令
            RBucket<String> bucket = transaction.getBucket(NAME);
            bucket.set("value");
            // 或者
            transaction.getBucket(NAME, StringCodec.INSTANCE).set("value");
            transaction.getMap(NAME).put("key", "value");
            // 提交事务
            transaction.commit();
        } catch (Exception ex) {
            log.error("", ex);
            // 异常回滚
            transaction.rollback();
        }
    }

}

来源

Redission 分布式锁框架 - 简书

Redis秒杀案例(基于springboot)_芝士汉堡 ????的博客-CSDN博客_redis springboot 秒杀

Redis 秒杀案例_bladejsW的博客-CSDN博客_redis 秒杀

springboot2.x整合Redission_保护我方胖虎的博客-CSDN博客_springboot 整合redission

【分布式】Redis分布式之事务(Transaction)操作_王思勤(勤思)的博客-CSDN博客_redis处理分布式事务

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-06-01 15:02:20  更:2022-06-01 15:07:21 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 20:06:48-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码