• 分布式、锁、延时任务


    1. redission

    redission 原理

    Redis分布式锁-这一篇全了解(Redission实现分布式锁完美方案)
    3

    2.zk

    2.1 指令

    ls / / 下有哪些子节点
    get /zookeeper 查看某个子节点内容
    create /aa “test”
    delete /aa
    set /aa “test01”

    2.2 创建节点

    模式 默认创建永久
    create -e 创建临时
    create -e /zz “hello zz”
    create -s 创建 有序节点
    3
    create -s -e 临时序列化节点
    3

    2.3 节点事件监听

    一次性的监听

    1. 节点创建 事件监听 NodeCreated
      stat -w /xx

    2. 节点 删除 监听 NodeDeleted
      stat -w

    3. 节点数据 变化 监听 NodeDataChanged
      get -w /bb
      set /bb “cc” //修改节点数据

    4. 子节点 监听 NodeChildrenChanged
      ls -w /bb

    create /bb/cc “test”

    2.4 zk 分布式锁

    1. 独占、排它, 其他线程获取不到
    2. 阻塞 解决自旋的消耗 临时 有序节点 + 事件 监听(监听 比他小1的节点),

    让最小的节点 获取到锁, 44节点监听43节点,43 节点删除后,唤醒后续44节点 获得锁。公平锁
    3. 锁的可重入性
    思路1: 在节点的内容中记录 服务器、线程、已经重入信息
    思路2 :Threadloacal: 线程的局部变量,线程私有

    阻塞操作 获取不到监听 并阻塞

    3
    创建 临时 有序节点 返回节点路径
    // 获取前置节点 ,如果前置节点为空,那么获得锁成功,否则监听 前置节点

    countdownLatch(1)
    在监听事件的内部 进行一个countDown();
    3

    可重入锁

    threadloacal
    在 trylock()
    3
    unlock 里面 判断:
    3

    2.5 锁

    zk 的节点 存储机制+通知机制
    zk 有4中节点类型,持久节点持久顺序节点临时节点临时顺序节点
    持久和临时 判断标准依赖于客户端 的生命周期
    3
    顺序: 节点对应的id

    • 持久节点
      • 持久顺序节点
      • 持久的非顺序节点
    • 临时节点
      • 临时的非顺序节点( 实现分布式锁的条件)
      • 临时的顺序节点
        3

    利用zk 支持的临时顺序节点 +通知机制 可以实现分布式锁

    • 加锁:判断是否持有了分布式锁,判断客户端 创建的节点是否是 有序节点中 序号最小的一个
    • 释放锁: 将自己的创建的节点 删除即可。

    3. redission zk 分布式锁对比

    4. 延时任务

    4.0 场景

    3

    4.1 定时任务数据库轮询

    1. 定时任务轮询数据库,
      采用定时任务失效延迟、对业务表进行轮询判断,到点执行。有一点点误差
      1) 使用单机版本的spring schedule +分布式锁的实现。
      同一个服务,多个节点 同时执行 可能都会执行,需要加分布式锁
      3
    1. 分布式调度框架 xxl-job
      如果处理数据量较大,可以利用分布式调度系统的分片功能并行处理,大大提升数据的处理能力,加快处理速度。

    优点
    单机版 基于spring,实现简单。不用引入各种中间件,各个模块可以自行定义延迟规则。
    缺点
    2. 完全由业务代码进行控制,重复代码多,不论是否有待执行的数据,都要空轮询cpu 且频繁的访问数据库 io 消耗
    3. 由于是定时轮询,存在一点误差。

    基于@schedule

    3

    取消订单操作
    3
    3

    基于xxl-job 的

    3

    4.2 Java 的DelayQueue

    Java DelayQueue的使用及应用场景

    DelayQueue 的实现原理。
    1) DelayQueue 是JDK提供的一个无界 BlockQueue ,用于放置实现了Delayed 接口的对象。其中的对象只能在其到期时,才能从队列中拿走。
    这种队列 是 有序的,即 队头对象的延迟到期时间最长。
    注意不能将null 元素放置 到这种队列中。

    2)实现注意事项
    队列 里面的元素 需要实现Delayed 这个接口。getDelay 方法用于设置 延迟时间。 compareTo 方法用于对队列的元素 进行排序。

    public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
        implements BlockingQueue<E> 
    // 入队
    put(){
    offer();} 线程安全  reentrantlock 加锁 
    offer(); 线程安全
    // 出队 
    poll(); 非阻塞方法,没有到期元素 直接返回 null
    take(); 阻塞式 获取,没有到期元素,线程将会进行等待。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    优点:
    jdk 自带的,不需要引入其他框架 中间件,实现简单。
    缺点:

    1. 不支持分布式或者 持久化的,重启会丢失。
    2. 如果 并发量 非常大,因为DelayQueue式无界的,队列内的对象越多。可能会造成oom 的风险。
    3. 所以使用 delayQueue实现的定时任务,只适用于任务量较小的情况。

    3

    4.3 消息中间件

    rocketmq如何实现延时队列

    rocketmq 先把消息 按照延时时间段(1s,5s,10s) 发到指定的队列中,然后通过一个定时器轮询这些队列。如果到期,就把 这个消息发到指定的topic 队列。
    注意点:
    3

    1. rocketmq 延时消息的时长不支持随机时长的延迟。是通过特定的延迟等级来指定的。
      默认支持18个等级的延迟消息。
      延时等级 在rocketmq服务端的MessageStoreConfig类中
    private String messageDelayLevel ='1s 5s 10s 30s   
    		1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 30m 1h 2h'
    		发消息时: 设置delayLevel 等级即可  msg.setDelayLevel( level);
    		level ==0 消息非延迟
    		1<=level <=maxlevel 消息延迟特定的时间。
    		level >maxlevel  那么 level=maxlevel  例如leve=20,延迟2h
    		
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    优点:
    基于消息中间件可以快速实现延时队列,而且天然支持消息消费的有序性、消息持久化、ack机制
    缺点:
    需要额外的部署 和运维成本。

    4.4 redis zset、 key 过期回调

    zrangebyScore

    redis 127.0.0.1:6379> ZADD salary 2500 jack               
             # 测试数据
    (integer) 0
    redis 127.0.0.1:6379> ZADD salary 5000 tom
    (integer) 0
    redis 127.0.0.1:6379> ZADD salary 12000 peter
    (integer) 0
    
    redis 127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf           
        # 显示整个有序集
    1) "jack"
    2) "tom"
    3) "peter"
    
    redis 127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf WITHSCORES  
      # 显示整个有序集及成员的 score 值
    1) "jack"
    2) "2500"
    3) "tom"
    4) "5000"
    5) "peter"
    6) "12000"
    
    redis 127.0.0.1:6379> ZRANGEBYSCORE salary -inf 5000 WITHSCORES  
      # 显示工资 <=5000 的所有成员
    1) "jack"
    2) "2500"
    3) "tom"
    4) "5000"
    
    redis 127.0.0.1:6379> ZRANGEBYSCORE salary (5000 400000     
           # 显示工资大于 5000 小于等于 400000 的成员
    1) "peter"
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    3

    4.5 时间轮算法

    3

    核心参数

    每个 刻度 代表一个duration 时长。

    • tickDuration s 、mins、 h
      每个刻度的时长
    • round
      第几圈 后执行,使用延期时长/ 一圈的时长得来
      一个刻度1s,15s 后执行,那么 需要 round =2,没到指定位置,round -1;
    • ticksPerWheel
      一圈下来有几个刻度

    工作原理

    • 指针停在0处
    • tickDuration=1
    • ticksPerWheel=12

    如果一个25秒才执行的延时任务添加进来,首先它会计算它的round和index,round=25/12 =2
    index=25%12=1.
    所以时间轮长这样:
    34

    当指针转到index=1的刻度时,会判断第一个task的round是不是为0,如果为0则取出来,去执行,如果大于0,则将round-1.

  • 相关阅读:
    【无标题】
    火狐浏览器翻译页面功能如何设置
    数据容器分类总结
    GaussDB技术解读——GaussDB架构介绍(五)
    附导读 |《网络安全标准实践指南——网络数据安全风险评估实施指引》解读
    继承-学习2
    全球顶尖开源项目相聚外滩大会,绘制国际化开源生态新蓝图
    .Net4.0 Web.config 配置实践
    一起学数据结构(5)——栈和队列
    R语言绘制环状柱状堆积图+分组+显著性
  • 原文地址:https://blog.csdn.net/weixin_48155735/article/details/132510903