我是靠谱客的博主 害羞毛豆,最近开发中收集的这篇文章主要介绍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

前面项目中,封装了

  • OCP开源项目:数据库公共组件的实现(db-spring-boot-starter) 、

  • OCP开源项目:日志公共组件的实现(log-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~

微信公众号

最后

以上就是害羞毛豆为你收集整理的OCP开源项目:Redis公共组件的实现(redis-spring-boot-starter)的全部内容,希望文章能够帮你解决OCP开源项目:Redis公共组件的实现(redis-spring-boot-starter)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(42)

评论列表共有 0 条评论

立即
投稿
返回
顶部