redis消息队列学习

0 环境

  • 系统环境: centos7
  • 编辑器: xshell
  • IDE:IDEA

1 前言

感觉还是用思维导图总结 更加方便/更直观 无论以后回看 还是内容补充/分享

reids思维导图图片版

2 正文

1 准备

在这里插入图片描述
事先准备的CallRedisDemo类以及CallWithJedis接口

public class CallRedisDemo {
    private JedisPool jedisPool;

    // 配置
    public CallRedisDemo() {
        GenericObjectPoolConfig config = new GenericObjectPoolConfig();
        // 连接池最大空闲数
        config.setMaxIdle(300);
        // 最大连接数
        config.setMaxTotal(1000);
        // 连接最大等待时间 若是-1 则无限制
        config.setMaxWaitMillis(200000);
        // 在空闲时检查有效性
        config.setTestOnBorrow(true);

        /*
        * GenericObjectPoolConfig poolConfig, String host, int port, int timeout, String password
        * 1 redis地址
        * 2 redis端口
        * 3 连接超时时间
        * 4 密码
        * */
        jedisPool = new JedisPool(config, "127.0.0.1", 6379, 20000, "123456");

    }

    // 执行
    // 执行失败 --> 请求重试(这样语法糖就不能用了 试太多也没意思 估计有问题)
    public void execute(CallWithJedis callWithJedis){
        try (Jedis jedis = jedisPool.getResource()){
            callWithJedis.call(jedis);
        }
    }


}

public interface CallWithJedis {
    void call(Jedis jedis);
}

2 导入依赖

<dependencies>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.2.0</version>
            <type>jar</type>
            <scope>compile</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
            <scope>provided</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.10.3</version>
        </dependency>

    </dependencies>

3 定义消息类

@Data
@ToString
public class Message {
    private String id;
    private Object data;
}

4 消息处理类

@AllArgsConstructor
public class DelayMsgsQueue {
    private Jedis jedis;
    // key
    private String queue;

    /**
    * @Description: 消息入队 Message类 存在id和数据 将对象序列化为string
     *              打印当前时间 为什么用zadd(ZADD key score member)
     *              因为需要延时 score(当前时间戳+延时时间) member(序列化数字)
    * @Param: data --> 发送消息
    * @return:
    * @Author: 
    * @Date: 
    */
    public void queue(Object data){
        // 构造一个Message
        Message message = new Message();

        message.setId(UUID.randomUUID().toString());
        message.setData(data);

        try {
            // 将对象序列号
            String s = new ObjectMapper().writeValueAsString(message);
            // 打印日期 以便与延迟时间对比
            System.out.println("消息:" + new Date());

            // 消息发布延时6s 需要秒(值)相加 --> score member --> s
            // ZADD key score1 member1 [score2 member2]
            jedis.zadd(queue, System.currentTimeMillis() + 6000, s);

        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

    }

    /**
    * @Description: 消费消息 当不中断时 利用zrangeByScore 每次只获取一个成员
     *                if 消费者score < 提供者的延时时间: 非空判断+休眠+continue
     *                else:
     *                  队列读取 在判断是否可移除
     *                  若可移除(反序列化 转化为对象) 打印消息
    * @Param:
    * @return:
    * @Author: 
    * @Date: 
    */
    public void loop(){
        while (!Thread.interrupted()){
            // ZRANGE key start stop [WITHSCORES]
            // ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT]
            // 读取0-当前时间戳区间 offset = 0 -> 不跳过任何成员 count = 1 --> 只读取一个成员
            Set<String> strings = jedis.zrangeByScore(queue, 0, System.currentTimeMillis(), 0, 1);

            if (strings.isEmpty()) {
                // 若消息为空 睡眠600ms 然后在开启
                try {
                    Thread.sleep(600);
                } catch (InterruptedException e) {
                    break;
                }

                continue;
            }

            // 队列中有消息 读取
            String next = strings.iterator().next();

            // ZREM key member [member ...]
            // 移除成功1个 返回1 假如是n个 返回n
            if (jedis.zrem(queue, next) > 0 && jedis.zrem(queue, next) < 2) {
                // 拿到 处理 反序列化
                try {
                    Message message = new ObjectMapper().readValue(next, Message.class);
                    System.out.println("接收的消息:" + new Date() + message);
                } catch (JsonProcessingException e) {
                    e.printStackTrace();
                }
            }

        }

    }

}

5 调用类

public class DelayMsgTest {
    public static void main(String[] args) {

        CallRedisDemo redisDemo = new CallRedisDemo();
        redisDemo.execute(jedis -> {
            // 构造消息队列
            DelayMsgsQueue queue = new DelayMsgsQueue(jedis, "hello");

            // 提供者
            Thread provider = new Thread() {
                @Override
                public void run() {
                    for (int i = 0; i < 6; i++) {
                        // 调用DelayMsgsQueue类中提供者的方法
                        queue.queue("你饿不饿 >>>>>>>> " + i);
                    }
                }
            };

            // 消费者
            Thread consumer = new Thread() {
                @Override
                public void run() {
                    for (int i = 0; i < 6; i++) {
                        // 调用DelayMsgsQueue类中消费者的方法
                        queue.loop();
                    }
                }
            };

            // 运行线程
            provider.run();
            consumer.run();

            // 等待一会 运行结束 中断消费者线程
            try {
                Thread.sleep(9000);
                consumer.interrupted();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }


        });

    }
}

6 对比打印结果

posted @ 2020-04-15 09:15  焜掱玚  阅读(289)  评论(0编辑  收藏  举报
levels of contents