场景:三个答题设备向教师端同步答题状态数据的场景。
发布/订阅模式:实现简单,实时性好,但消息不持久化,教师端掉线会丢失消息
Stream模式:消息持久化,支持消息回溯,可靠性更高,适合生产环境
方案一:Redis 发布/订阅模式实现
教师端(订阅者)
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPubSub;
public class TeacherSubscriber {
public static void main(String[] args) {
Jedis jedis = new Jedis("localhost", 6379);
System.out.println(" 教师端已启动,等待接收答题状态...");
jedis.subscribe(new JedisPubSub() {
@Override
public void onMessage(String channel, String message) {
System.out.println(" 教师端收到消息: " + message);
}
}, "exam_status_channel");
}
}
答题设备(发布者)
import redis.clients.jedis.Jedis;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class AnswerDevicePublisher {
private static final String[] STATUSES = {
"开始答题",
"第一题已作答",
"第二题已作答",
"正在检查答案",
"已完成所有题目"
};
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(3);
// 模拟三个答题设备
executor.execute(() -> simulateDevice(1, "192.168.1.101"));
executor.execute(() -> simulateDevice(2, "192.168.1.102"));
executor.execute(() -> simulateDevice(3, "192.168.1.103"));
executor.shutdown();
executor.awaitTermination(1, TimeUnit.MINUTES);
}
private static void simulateDevice(int deviceId, String ip) {
Jedis jedis = new Jedis("localhost", 6379);
Random random = new Random();
for (String status : STATUSES) {
String message = String.format(" 设备%d(%s): %s", deviceId, ip, status);
jedis.publish("exam_status_channel", message);
try {
TimeUnit.MILLISECONDS.sleep(1000 + random.nextInt(2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
方案二:Redis Stream 实现
教师端(消费者)
import redis.clients.jedis.Jedis;
import redis.clients.jedis.StreamEntry;
import redis.clients.jedis.StreamEntryID;
import java.util.List;
import java.util.Map;
public class TeacherStreamConsumer {
public static void main(String[] args) {
Jedis jedis = new Jedis("localhost", 6379);
StreamEntryID lastId = new StreamEntryID("0-0");
System.out.println(" 教师端(Stream)已启动,等待接收答题状态...");
while (true) {
// 阻塞读取,最多等待5秒
Map<String, List<StreamEntry>> messages = jedis.xread(
5000, // 阻塞时间
new StreamEntryID(lastId), // 从上次ID开始
"exam_status_stream" // Stream key
);
if (messages != null && !messages.isEmpty()) {
for (List<StreamEntry> entries : messages.values()) {
for (StreamEntry entry : entries) {
String deviceId = entry.getFields().get("device_id");
String ip = entry.getFields().get("ip_address");
String status = entry.getFields().get("status");
System.out.printf(" 教师端收到消息(ID:%s): 设备%s(%s) - %s%n",
entry.getID(), deviceId, ip, status);
lastId = entry.getID();
}
}
}
}
}
}
答题设备(生产者)
import redis.clients.jedis.Jedis;
import redis.clients.jedis.StreamEntryID;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class AnswerDeviceStreamProducer {
private static final String[] STATUSES = {
"开始答题",
"第一题已作答",
"第二题已作答",
"正在检查答案",
"已完成所有题目"
};
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(3);
// 模拟三个答题设备
executor.execute(() -> simulateStreamDevice(1, "192.168.1.101"));
executor.execute(() -> simulateStreamDevice(2, "192.168.1.102"));
executor.execute(() -> simulateStreamDevice(3, "192.168.1.103"));
executor.shutdown();
executor.awaitTermination(1, TimeUnit.MINUTES);
}
private static void simulateStreamDevice(int deviceId, String ip) {
Jedis jedis = new Jedis("localhost", 6379);
Random random = new Random();
for (String status : STATUSES) {
Map<String, String> message = new HashMap<>();
message.put("device_id", String.valueOf(deviceId));
message.put("ip_address", ip);
message.put("status", status);
StreamEntryID id = jedis.xadd("exam_status_stream", null, message);
System.out.printf(" 设备%d 发送消息(ID:%s): %s%n", deviceId, id, status);
try {
TimeUnit.MILLISECONDS.sleep(1000 + random.nextInt(2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}