分布式锁框架--Redisson

Redisson的github地址
Redisson的github的文档
官网地址

一.Redisson简介

1. redisson概述

redisson概述

2.整合Redisson

1).依赖

依赖

<!--使用redisson作为所有分布式锁,分布式对象等功能性框架-->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.12.0</version>
</dependency>

2).Redisson配置

a.配置方法
在这里插入图片描述
b.第三方框架整合
在这里插入图片描述

c. 单Redis节点模式 --redis不是集群
单Redis节点模式
在这里插入图片描述

2.1)redisson配置文件

在这里插入图片描述

@Configuration
public class MyRedissonConfig {

    //所有对Redisson的使用,都是RedissonClient对象
    @Bean(destroyMethod = "shutdown")
   public RedissonClient redisson() throws IOException {
        //错误: Redis url should start with redis:// or rediss:// (for SSL connection)
        //1.创建配置
        Config config = new Config();
        //设置单节点模式
        config.useSingleServer().setAddress("redis://192.168.56.10:6379");
        //2.根据Config创建出RedissonClient实例
        RedissonClient redissonClient = Redisson.create(config);
        return redissonClient;
    }
}

二、Redisson完成分布式锁功能

分布式对象
分布式集合
分布式锁和同步器

1.分布式锁和同步器

1.1. 可重入锁(Reentrant Lock)*

基于Redis的Redisson分布式 可重入锁RLock
Java对象实现了java.util.concurrent.locks.Lock接口。同时还提供了异步(Async)、反射式(Reactive)和RxJava2标准的接口。

RLock lock = redisson.getLock(“anyLock”); // 最常见的使用方法 lock.lock();
解锁
另外Redisson还通过加锁的方法提供了leaseTime的参数来指定加锁的时间。超过这个时间后锁便自动解开了。
// 加锁以后10秒钟自动解锁
// 无需调用unlock方法手动解锁
lock.lock(10, TimeUnit.SECONDS);
// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
try {

} finally {
lock.unlock();
}
}

1).测试-分布式–lock

分布式测试product,启动10000,10001
http://localhost:10000/hello,
http://localhost:10001/hello
在这里插入图片描述

    @ResponseBody
    @GetMapping("/hello")
    private String hello() {
        //1.获取一把锁,只要锁名一样,就是同一把锁
        RLock lock = redisson.getLock("my-lock");
        //2.加锁(默认过期时间30s)
        lock.lock();//阻塞式等待
        //1).锁的自动续期(如果业务超长,会自动给锁加上30s。防止业务时间长,锁自动过期被删除)
        //2).加锁业务只要运行完成,不会给当前锁续期;若不解锁,锁默认30s后自动解锁
        try {
            System.out.println("加锁成功,执行业务 "+Thread.currentThread().getId());
            Thread.sleep(10000);
        } catch (Exception e) {

        } finally {
            //3.解锁--假如解锁异常,redis会不会死锁?
            System.out.println("释放锁,"+Thread.currentThread().getId());
            lock.unlock();
        }
        return "hello";
    }
a.优点:自动续期、自动解锁
b.手动设置 锁的过期时间

问题

如果过期时间小于业务执行时间,会出现异常(因为解锁的不是自己的锁)

attempt to unlock lock, not locked by current thread by node id: 1e679a77-58f9-4e9a-a3ad-8007086d665d thread-id: 105

原理:
在这里插入图片描述

c.最佳实践

在这里插入图片描述

设置时间30秒

2)trylock
// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
   try {
     ...
   } finally {
       lock.unlock();
   }
}

1.2 公平锁(Fair Lock)

RLock fairLock = redisson.getFairLock("anyLock");
// 最常见的使用方法
fairLock.lock();

1.3 读写锁(ReadWriteLock)*

读写锁(ReadWriteLock)

1).原理: 读好像无锁,写有锁
2).介绍

基于Redis的Redisson分布式可重入读写锁RReadWriteLock
Java对象实现了java.util.concurrent.locks.ReadWriteLock接口。其中读锁和写锁都继承了RLock接口。

分布式可重入读写锁允许同时有多个读锁和一个写锁处于加锁状态。

RReadWriteLock rwlock = redisson.getReadWriteLock("anyRWLock");
// 最常见的使用方法
rwlock.readLock().lock();
// 
rwlock.writeLock().lock();

指定加锁的时间。超过这个时间后锁便自动解开了。

// 10秒钟以后自动解锁
// 无需调用unlock方法手动解锁
rwlock.readLock().lock(10, TimeUnit.SECONDS);
// 
rwlock.writeLock().lock(10, TimeUnit.SECONDS);

// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = rwlock.readLock().tryLock(100, 10, TimeUnit.SECONDS);
// 
boolean res = rwlock.writeLock().tryLock(100, 10, TimeUnit.SECONDS);
...
lock.unlock();
3).代码–写锁、读锁
a.写锁

在这里插入图片描述

    //读写锁(ReadWriteLock)--允许同时有多个读锁和一个写锁处于加锁状态。
    @ResponseBody
    @GetMapping("/write")
    public String writeValue() {
        //1.写操作,加写锁
        RReadWriteLock lock = redisson.getReadWriteLock("rw-lock");
        RLock rLock = lock.writeLock();//写锁
        String s = null;
        try {
           //写锁
            rLock.lock();
            s = UUID.randomUUID().toString();
            Thread.sleep(10000);
            redisTemplate.opsForValue().set("writeValue",s);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //释放锁
            rLock.unlock();
        }
        return s;
    }
b.读锁

在这里插入图片描述

	@ResponseBody
    @GetMapping("/read")
    public String readValue() {
        //1.读操作,加读锁
        RReadWriteLock lock = redisson.getReadWriteLock("rw-lock");
        RLock rLock = lock.readLock();//读锁
        //加读锁
        rLock.lock();
        String s =null;
        try {
            s= (String) redisTemplate.opsForValue().get("writeValue");
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //释放锁
            rLock.unlock();
        }
        return s;
    }

优点:一定能读到最新数据修改期间,写锁是排它锁(互斥锁、独享锁),读锁是共享锁。写锁没释放,读锁必须对待
4).场景模拟

在这里插入图片描述

a.读+读

相当于无锁,并发读,只会在redis记录所有当前读锁,他们会同时加锁成功
直接访问2个读

b.写+读,必须等待写锁完成

读锁必须在写锁完成,才能读
先访问写操作,再访问读操作

c.写+写,阻塞状态

写锁是阻塞状态,写+写还是阻塞状态
先访问一个写,再访问一个写操作

d.读+写。有读锁,写锁需等待

先访问读操作,再访问写操作

总结:只要有写锁,必须等待(读锁是共享锁,写锁是互斥锁)

1.4 信号量(Semaphore)*

信号量(Semaphore)

1).原理: 释放release后,才能获得acquire
2).介绍

基于Redis的Redisson的分布式信号量(Semaphore)Java对象RSemaphore采用了与java.util.concurrent.Semaphore相似的接口和用法。同时还提供了异步(Async)、反射式(Reactive)和RxJava2标准的接口。

RSemaphore semaphore = redisson.getSemaphore("semaphore");
semaphore.acquire();//阻塞队列
//
semaphore.acquireAsync();
semaphore.acquire(23);
semaphore.tryAcquire();//非阻塞队列
//
semaphore.tryAcquireAsync();
semaphore.tryAcquire(23, TimeUnit.SECONDS);
//
semaphore.tryAcquireAsync(23, TimeUnit.SECONDS);
semaphore.release(10);
semaphore.release();
//
semaphore.releaseAsync();
3) 场景 车辆入库、出库

车辆出库,车位才可以入库 没有出库,没有车位,入库失败

4) 代码

在这里插入图片描述

     //信号量
    /**
     * 停车入库
     * 3号位
     * 信号量可以作为分布式限流
     */
    @ResponseBody
    @GetMapping("/park")
    //停车(车辆入库)
    public String park() {
        RSemaphore park = redisson.getSemaphore("park");
//        try {
//            park.acquire();//获取一个信号,获取一个值,占一个车位 (阻塞队列)
//            System.out.println("车辆入库成功。。。");
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }

        boolean b = park.tryAcquire();//非阻塞队列
        if (b) {
            //执行业务
            System.out.println("车辆入库成功。。。");
        } else {
            System.out.println("车辆入库失败。。。");
            return "error";
        }
        return "ok=>" + b;
//        return "ok";
    }
    //开走车
    @ResponseBody
    @GetMapping("/go")
    //车辆出库
    public String go() {
        RSemaphore park = redisson.getSemaphore("park");
        //释放一个车位
        park.release();
        System.out.println("释放车位成功。。。");
        return "ok";
    }

1.5 闭锁(CountDownLatch)

1).原理: 计数器为0,才闭锁
2).介绍

基于Redisson的Redisson分布式闭锁(CountDownLatch)Java对象RCountDownLatch采用了与java.util.concurrent.CountDownLatch相似的接口和用法。

RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
latch.trySetCount(1);
latch.await();

// 在其他线程或其他JVM里
RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
latch.countDown();
3). 场景-学校锁门

只有全部班级走,才能锁门

4).代码

在这里插入图片描述

    // 闭锁(CountDownLatch)
    /*
    * 放假,锁门
    *1班没人了
    * 5个班全部走完,才能锁门
    * */
    @ResponseBody
    @GetMapping("/lockDoor")
    //场景:
    public String lockDoor() {
        RCountDownLatch door = redisson.getCountDownLatch("door");
        door.trySetCount(5);
        try {
            door.await();//等待闭锁完成
            System.out.println("放假了。。。");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "放假了。。。";
    }
    @ResponseBody
    @GetMapping("/gogo/{id}")
    public String gogo(@PathVariable("id") Long id){
        RCountDownLatch door = redisson.getCountDownLatch("door");
        door.countDown();//计数减一
        System.out.println(id+" 班的人都走了。。。");
        return id+" 班的人都走了。。。";
    }
5).测试

http://localhost:10000/lockDoor
http://localhost:10000/gogo/1
http://localhost:10000/gogo/2
http://localhost:10000/gogo/3
http://localhost:10000/gogo/4
http://localhost:10000/gogo/5

6).总结: 闭锁,必须在计数为0,才闭锁

1.6 使用JUC也可以实现以上逻辑

在这里插入图片描述

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
SeimiCrawler An agile,powerful,distributed crawler framework. SeimiCrawler的目标是成为Java世界最好用最实用的爬虫框架。简介      SeimiCrawler是一个敏捷的,支持分布式的Java爬虫开发框架,希望能在最大程度上降低新手开发一个可用性高且性能不差的爬虫系统的门槛,以及提升开发爬虫系统的开发效率。在SeimiCrawler的世界里,绝大多数人只需关心去写抓取的业务逻辑就够了,其余的Seimi帮你搞定。设计思想上SeimiCrawler受Python的爬虫框架Scrapy启发很大,同时融合了Java语言本身特点与Spring的特性,并希望在国内更方便且普遍的使用更有效率的XPath解析HTML,所以SeimiCrawler默认的HTML解析器是JsoupXpath,默认解析提取HTML数据工作均使用XPath来完成(当然,数据处理亦可以自行选择其他解析器)。 原理示例基本原理集群原理快速开始 添加maven依赖(已经同步到中央maven库,最新版本参见项目主页):     cn.wanghaomiao     SeimiCrawler     0.1.0 在包crawlers下添加爬虫规则,例如:@Crawler(name = "basic") public class Basic extends BaseSeimiCrawler {     @Override     public String[] startUrls() {         return new String[]{"http://www.cnblogs.com/"};     }     @Override     public void start(Response response) {         JXDocument doc = response.document();         try {             List<Object> urls = doc.sel("//a[@class='titlelnk']/@href");             logger.info("{}", urls.size());             for (Object s:urls){                 push(new Request(s.toString(),"getTitle"));             }         } catch (Exception e) {             e.printStackTrace();         }     }     public void getTitle(Response response){         JXDocument doc = response.document();         try {             logger.info("url:{} {}", response.getUrl(), doc.sel("//h1[@class='postTitle']/a/text()|//a[@id='cb_post_title_url']/text()"));             //do something         } catch (Exception e) {             e.printStackTrace();         }     } } 然后随便某个包下添加启动Main函数,启动SeimiCrawler:public class Boot {     public static void main(String[] args){         Seimi s = new Seimi();         s.start("basic");     } } 以上便是一个最简单的爬虫系统开发流程。 更多文档      目前可以参考demo工程中的样例,基本包含了主要的特性用法。更为细致的文档移步SeimiCrawler主页中进一步查看 标签:爬虫

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值