OCP开源项目:Redis公共组件的实现(redis-spring-boot-starter)

OCP开源项目:Redis公共组件的实现(redis-spring-boot-starter)

前言

企业微服务开放平台 ,历经多家公司生产考验

  • 基于layui前后端分离的企业级微服务架构

  • 兼容spring cloud netflix & spring cloud alibaba

  • 优化Spring Security内部实现,实现API调用的统一出口和权限认证授权中心

  • 提供完善的企业微服务流量监控,日志监控能力

  • 提供完善的压力测试方案

  • 提供完善的微服务部署方案

项目演示地址

http://59.110.164.254:8066/login.html 用户名/密码:admin/admin

项目监控地址

http://47.98.236.203:3000 用户名/密码:admin/1q2w3e4r

入群学习:(备注:Coder编程)
群1:483725710(满2000)
群2:897924507

redis-spring-boot-starter

前面项目中,封装了

为了支持redis的操作,自定义redis-spring-boot-starter。

源码分享

  • 代码一览

    image
  • RedisAutoConfig

    image

回顾db-spring-boot-starter的章节

  • @EnableAutoConfiguration 作用:从classpath中搜索所有META-INF/spring.factories配置文件
    并且其中org.springframework.boot.autoconfigure.EnableAutoConfiguration key对应的配置项加载到spring容器

分布式锁redssion

集成方式

image

大致使用

image

代码分析

  • 获取锁

    image

    调用getLock()方法后实际返回一个RedissonLock对象

  • 加锁

    image
    image

    在RedissonLock对象的lock()方法主要调用tryAcquire()方法,由于leaseTime == -1,于是走tryLockInnerAsync()方法,

  • 加锁细节

    image

    结合上面的参数声明,我们可以知道,这里KEYS[1]就是getName(),ARGV[2]是getLockName(threadId),假设前面获取锁时传的name是“anyLock”,假设调用的线程ID是Thread-1,假设成员变量UUID类型的id是85b196ce-e6f2-42ff-b3d7-6615b6748b5d:65那么KEYS[1]=anyLock,ARGV[2]=85b196ce-e6f2-42ff-b3d7-6615b6748b5d:Thread-1 ,因此,这段脚本的意思是
    1、判断有没有一个叫“anyLock”的key
    2、如果没有,则在其下设置一个字段为“85b196ce-e6f2-42ff-b3d7-6615b6748b5d:Thread-1”,值为“1”的键值对 ,并设置它的过期时间
    3、如果存在,则进一步判断“85b196ce-e6f2-42ff-b3d7-6615b6748b5d:Thread-1”是否存在,若存在,则其值加1,并重新设置过期时间
    4、返回“anyLock”的生存时间(毫秒)

  • 加锁redis结构

    image

    这里用的数据结构是hash,hash的结构是:key  字段1  值1 字段2  值2  。。。用在锁这个场景下,key就表示锁的名称,也可以理解为临界资源,字段就表示当前获得锁的线程所有竞争这把锁的线程都要判断在这个key下有没有自己线程的字段,如果没有则不能获得锁,如果有,则相当于重入,字段值加1(次数)

  • 解锁

    image

    我们还是假设name=anyLock,假设线程ID是Thread-1,同理,我们可以知道KEYS[1]是getName(),即KEYS[1]=anyLock,KEYS[2]是getChannelName(),即KEYS[2]=redisson_lock__channel:{anyLock},ARGV[1]是LockPubSub.unlockMessage,即ARGV[1]=0,ARGV[2]是生存时间,ARGV[3]是getLockName(threadId),即ARGV[3]=85b196ce-e6f2-42ff-b3d7-6615b6748b5d:Thread-1,因此,上面脚本的意思是:
    1、判断是否存在一个叫“anyLock”的key
    2、如果不存在,向Channel中广播一条消息,广播的内容是0,并返回1。
    3、如果存在,进一步判断字段85b196ce-e6f2-42ff-b3d7-6615b6748b5d:Thread-1是否存在。
    4、若字段不存在,返回空,若字段存在,则字段值减1,
    5、若减完以后,字段值仍大于0,则返回0。
    6、减完后,若字段值小于或等于0,则广播一条消息,广播内容是0,并返回1;可以猜测,广播0表示资源可用,即通知那些等待获取锁的线程现在可以获得锁了

  • 等待

     private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException {
            long threadId = Thread.currentThread().getId();
            Long ttl = tryAcquire(leaseTime, unit, threadId);
            // lock acquired
            if (ttl == null) {
                return;
            }    RFuture<RedissonLockEntry> future = subscribe(threadId);
        if (interruptibly) {
            commandExecutor.syncSubscriptionInterrupted(future);
        } else {
            commandExecutor.syncSubscription(future);
        }
    
        try {
            while (true) {
                ttl = tryAcquire(leaseTime, unit, threadId);
                // lock acquired
                if (ttl == null) {
                    break;
                }
    
                // waiting for message
                if (ttl >= 0) {
                    try {
                        future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
                    } catch (InterruptedException e) {
                        if (interruptibly) {
                            throw e;
                        }
                        future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
                    }
                } else {
                    if (interruptibly) {
                        future.getNow().getLatch().acquire();
                    } else {
                        future.getNow().getLatch().acquireUninterruptibly();
                    }
                }
            }
        } finally {
            unsubscribe(future, threadId);
        }
    //        get(lockAsync(leaseTime, unit));
        }
    

    这里会订阅Channel,当资源可用时可以及时知道,并抢占,防止无效的轮询而浪费资源当资源可用用的时候,循环去尝试获取锁,由于多个线程同时去竞争资源,所以这里用了信号量,对于同一个资源只允许一个线程获得锁,其它的线程阻塞

  • 流程总结

    流程一
    流程二

文末

欢迎关注个人微信公众号:Coder编程
欢迎关注Coder编程公众号,主要分享数据结构与算法、Java相关知识体系、框架知识及原理、Spring全家桶、微服务项目实战、DevOps实践之路、每日一篇互联网大厂面试或笔试题以及PMP项目管理知识等。更多精彩内容正在路上~
也分享一些杂文~

文章收录至
Github: https://github.com/CoderMerlin/coder-programming
Gitee: https://gitee.com/573059382/coder-programming
欢迎关注并star~

微信公众号
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值