• 《Redis深度历险》三(异步消息队列)


    在异步消息队列的应用

    队列延迟

    在队列中,如果空了,消费者不断的循环pop新的,会导致CPU和redis的QPS升高。若使用sleep强制降下来,当消费者很多的话,redis依然会不断的轮询查询。

    blpop/brpop可以在队列没有数据的时候休眠,一旦有数据就可以醒过来,延时几乎为0.

    # 对非空列表进行操作
    
    redis> RPUSH job programming
    (integer) 1
    
    redis> MULTI
    OK
    
    redis> BLPOP job 30
    QUEUED
    
    redis> EXEC           # 不阻塞,立即返回
    1) 1) "job"
       2) "programming"
    
    
    # 对空列表进行操作
    
    redis> LLEN job      # 空列表
    (integer) 0
    
    redis> MULTI
    OK
    
    redis> BLPOP job 30
    QUEUED
    
    redis> EXEC         # 不阻塞,立即返回
    1) (nil)
    

    空闲连接

    如果线程闲置久了,redis服务端会自动断开,blpop会抛出异常,要注意。

    锁冲突处理

    直接抛出特定类型异常

    用户重试

    sleep

    sleep 会阻塞当前的消息处理线程,会导致队列的后续消息处理出现延迟。如果碰撞的比 较频繁或者队列里消息比较多,sleep 可能并不合适。如果因为个别死锁的 key 导致加锁不成 功,线程会彻底堵死,导致后续消息永远得不到及时处理。

    延时队列

    延时队列可以通过 Redis 的 zset(有序列表) 来实现。我们将消息序列化成一个字符串作 为 zset 的 value,这个消息的到期处理时间作为 score,然后用多个线程轮询 zset 获取到期 的任务进行处理,多个线程是为了保障可用性,万一挂了一个线程还有其它线程可以继续处 理。因为有多个线程,所以需要考虑并发争抢任务,确保任务不能被多次执行。

    • java延时队列的实现

      package com.hjj.test;
      import com.alibaba.fastjson.JSON;
      import com.alibaba.fastjson.TypeReference;
      import javafx.concurrent.Task;
      import redis.clients.jedis.Jedis;
      
      import java.lang.reflect.Type;
      import java.util.Set;
      import java.util.UUID;
      
      /**
       * @author Jimmy He
       * @date 2020-09-06
          */
      
        public class RedisDelayingQueue<T> {
          
          static class TaskItem<T>{
              public String id;
              public T msg;
          }
          
          private Type TaskType = new TypeReference<TaskItem<T>>(){}.getType();
          
          private Jedis jedis;
          private String queueKey;
          
          public RedisDelayingQueue(Jedis jedis,String queueKey){
              this.jedis=jedis;
              this.queueKey = queueKey;
          
          }
          
          public void delay(T msg){
              TaskItem<T> task = new TaskItem<>();
              // 分配唯一的uuid
              task.id = UUID.randomUUID().toString();
              task.msg = msg;
              // fastjson序列化
              String s = JSON.toJSONString(task);
              // 塞入延时队列
              jedis.zadd(queueKey,System.currentTimeMillis() + 5000,s);
          
          }
          
          public void loop(){
              while (!Thread.interrupted()){
                  // 只取一条
                  Set<String> values = jedis.zrangeByScore(queueKey, 0, System.currentTimeMillis(), 0, 1);
                  if (values.isEmpty()){
                      try{
                          // 休息5s再取
                          Thread.sleep(500);
                      } catch (InterruptedException e) {
                          break;
                      }
                      continue;
                  }
                  String s = values.iterator().next();
                  // 抢到了
                  if (jedis.zrem(queueKey,s) > 0){
                      // 反序列化
                      TaskItem<T> task = JSON.parseObject(s, TaskType);
                      this.handleMsg(task.msg);
                  }
              }
          }
          
          public void handleMsg(T msg){
              System.out.println(msg);
          }
          
          public static void main(String[] args) {
              Jedis jedis = new Jedis();
              final RedisDelayingQueue<String> queue = new RedisDelayingQueue<>(jedis, "q-demo");
              Thread producer = new Thread() {
                  public void run() {
                      for (int i = 0; i < 10; i++) {
                          queue.delay("codehole" + i);
                      }
                  }
              };
              Thread consumer = new Thread() {
                  public void run() {
                      queue.loop();
                  }
              };
              
              producer.start();
              consumer.start();
              
              try {
                  producer.join();
                  Thread.sleep(6000);
                  consumer.interrupt();
                  consumer.join();
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
        }
      
  • 相关阅读:
    Oracle Redo 并行机制
    ORA16032 Can not Start Instance via srvctl but via sqlplus is fine [ID 1062071.1]
    Linux 各文件夹的作用
    Private strand flush not complete 说明
    Executing root.sh errors with "Failed To Upgrade Oracle Cluster Registry Configuration" [ID 466673.1]
    openfiler 搭建虚拟存储 并 配置服务端
    Oracle RAC CRS0184 Cannot communicate with the CRS daemon
    Redhat 5.4 RAC multipath 配置raw,运行root.sh 时报错Failed to upgrade Oracle Cluster Registry configuration 解决方法
    Openfiler + Redhat 5.4 Oracle 11gR2 RAC 安装文档
    How to Troubleshoot Grid Infrastructure Startup Issues [ID 1050908.1]
  • 原文地址:https://www.cnblogs.com/jimmyhe/p/13976192.html
Copyright © 2020-2023  润新知