Redis推送订阅


目前工业物联网项目 数据推送使用Redis 推送订阅方式,特作下此篇,算个记录吧;


一、Redis 推送订阅特性

官网介绍:https://redis.com.cn/redis-pub-sub.html
Redis 发布/订阅是一种消息传模式,其中发送者(在Redis术语中称为发布者)发送消息,而接收者(订阅者)接收消息。传递消息的通道称为channel。

Redis的发布和订阅 消息没有持久化,所发布的消息是 当时没收到便后续就收不到了;就目前验证的现象来看,redis 发布者的消息属于 没有消费就丢弃,并没有在channel 里边缓存;

订阅模式见下
在这里插入图片描述

二、发布订阅实现

1. 引入库

<dependency>
	<groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>5.0.2</version>
</dependency>

2. 发布端代码

注意 Jedis jedis = getRedisConnect().getResource();
这行代码是每次从连接池里边取出空闲连接,注意需使用Jedis 变量 然后再去操作,不然容易报无法获得连接

package org.jetlinks.community.mq;


import com.google.gson.Gson;
import com.google.gson.JsonObject;
import redis.clients.jedis.Jedis;

import java.util.Date;

import static org.jetlinks.community.mq.JedisConnectUtil.getRedisConnect;

public class RedisPublish {

    private Gson gson = new Gson();
    private Jedis jedis = getRedisConnect().getResource();

    public static void main(String[] args) throws InterruptedException {
        RedisPublish redisPublish = new RedisPublish();
        for (int i = 0; i < 60; i++) {
            redisPublish.publishOne(i);
            Thread.currentThread().sleep(1000);
            //redisPublish.publishTwo(i);
        }
        /*RedisPublish redisPublish = new RedisPublish();
        redisPublish.publish();*/
    }

    private void publishOne(int num) {
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("id", 20016l);
        jsonObject.addProperty("pos_x", 420622);
        jsonObject.addProperty("pos_y", 175052);
        jsonObject.addProperty("ip", "10.104.114.50");
        jsonObject.addProperty("num", num);
        String mes = gson.toJson(jsonObject);
        System.out.println(new Date().toLocaleString() + " redis 发布: " + mes);
        jedis.publish("channel", mes);
    }

    private void publishTwo(int num) {
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("num", num);
        jsonObject.addProperty("two", "two");
        String mes = gson.toJson(jsonObject);
        System.out.println(new Date().toLocaleString() + " redis 发布: " + mes);
        jedis.publish("xxx", mes);
    }


}

3. 订阅端代码

package org.jetlinks.community.mq;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import org.jetbrains.annotations.NotNull;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Scheduler;
import reactor.core.scheduler.Schedulers;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPubSub;

import java.io.IOException;
import java.util.Date;

import static org.jetlinks.community.mq.JedisConnectUtil.getRedisConnect;

public class RedisSubscribe extends JedisPubSub {
    private static Jedis jedis = getRedisConnect().getResource();

    private static Gson gson = new Gson();

    private static RedisSubscribe redisSubscribe = new RedisSubscribe();

    public static void main(String[] args) throws IOException {
        start();
        System.out.println("继续执行=====");
        System.in.read();
    }

    public static void start() {
        new Thread(() ->
            jedis.subscribe(redisSubscribe, "channel")
        ).start();
    }


    @Override
    public void onMessage(String channel, String message) {
        System.out.println(new Date().toLocaleString() +
            " 订阅 onMessage channel" + channel + " message : " + message+ " subNum: "+redisSubscribe.getSubscribedChannels());
        JsonObject jsonObject = gson.fromJson(message, JsonObject.class);
        int num = jsonObject.get("num").getAsInt();
       /* System.out.println("解除不存在的 channel ==");
        redisSubscribe.unsubscribe("channel");*/
        if (num == 30) {
            System.out.println(" 重新订阅 =========== " + num);
            //redisSubscribe.unsubscribe(channel);
            jedis.subscribe(redisSubscribe, "xxx");
            jedis.subscribe(redisSubscribe, "channel");
        } else if (num == 35) {
           // getRedisConnect().getResource().close();
        }

        //super.onMessage(channel, message);
    }

    @Override
    public void onSubscribe(String channel, int subscribedChannels) {
        //RedisSubscribe redisSubscribe = new RedisSubscribe();
        System.out.println(new Date().toLocaleString() + " 订阅 onSubscribe channel" + channel + " subscribedChannels : " + subscribedChannels);
        // super.onSubscribe(channel, subscribedChannels);
        //jedis.pubsubChannels().forEach(channel1 -> System.out.println("channel: " + channel1));
        //redisSubscribe.jedis.pubsubChannels(channel).forEach(channel1 -> System.out.println("channel: " + channel1));
    }
}

4. 解除订阅

  • 解除订阅之前先判断该redis 是否有订阅,不然直接 unsubscribe() 会报错;
  • 取消订阅只需要RedisSubscribe.unsubscribe() 就行;无需再 jedis.subscribe(redisSubscribe, channel);
  • 当执行该代码后,目前看到的现象是还会消费 1-2 条消息;可能是在解除订阅的间隙过程中发送了到channel;
// 判断当前redis 订阅的channel
int subscribedNum = subscribe.getSubscribedChannels();
// 解除该channel 的订阅:
redisSubscribe.unsubscribe(channel);
// 解除全部订阅
redisSubscribe.unsubscribe();
//  查询所有订阅的topic
jedis.pubsubChannels();

5. 注意事项

目前项目上使用的是project.Reactor ,在使用jedis api 的过程中遇到一些问题如下:

  • 在 执行 jedis.subscribe(redisSubscribe, channel); 后,会立即进入到 JedisPubSub 里边的 onSubscribe() / onMessage()方法,代码会立即陷入阻塞;如果有一些前端接口操作需将 jedis.subscribe(redisSubscribe, channel); 这一行做成异步操作;
   private ExecutorService executor = Executors.newSingleThreadExecutor();
   executor.submit(() -> jedis.subscribe(subscribe, redisChannels));
  • Mono.fromRunnable() 后续需看下这个代码作用,是否有异步;

总结

  1. 在使用Jedis api 还是有些不清晰,导致在开发的过程中占用的大量时间调试问题;
  2. 在Project.Reactor api应用上也不甚熟悉,也缺乏正确资料,导致在异步的过程中尝试不成功,最后回到显示的线程池方案;
  • 22
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值