先看一下消息处理中心类(InMemoryStorage)的实现
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingDeque;
/**
@author james mu
@date 2020/7/27 21:47
*/
public final class InMemoryStorage {
//保存消息数据的容器, 键值对
private final ConcurrentHashMap> storage;
private static InMemoryStorage instance;
private InMemoryStorage() {
storage = new ConcurrentHashMap<>();
}
//利用双重检查加锁(double-checked locking),首先检查是否示例已经创建了,如果尚未创建,"才"进行同步。这样以来,只有第一次会同步,这正是我们想要的。
public static InMemoryStorage getInstance() {
if (instance == null) {
synchronized (InMemoryStorage.class) {
if (instance == null) {
instance = new InMemoryStorage();
}
}
}
return instance;
}
//保存消息到主题中,若topic对应的value为空,会将第二个参数的返回值存入并返回
public boolean put(String topic, QueueMsg msg) {
return storage.computeIfAbsent(topic, (t) -> new LinkedBlockingDeque<>()).add(msg);
}
//获得主题中的消息
public List get(String topic) {
//判断map中是否包含此topic
if (storage.containsKey(topic)) {
List entities;
//从此主题对应的阻塞队列中出队一个元素
T first = (T) storage.get(topic).poll();
if (first != null) {
entities = new ArrayList<>();
entities.add(first);
List otherList = new ArrayList<>();
//移动阻塞队列中最大999个元素到arrayList中
storage.get(topic).drainTo(otherList, 999);
for (QueueMsg other : otherList) {
entities.add((T) other);
}
} else {
entities = Collections.emptyList();
}
}
return Collections.emptyList();
}
//删除此map中所有的键值对
public void cleanup() {
storage.clear();
}
}
作为一个消息处理中心中,至少要有一个数据容器用来保存接受到的消息。
Java中的队列(Queue)是提供该功能的一种简单的数据结构,同时为简化队列操作的并发访问处理,我们选择了它的一个子类LinkedBlockingDeque。该类提供了对数据的插入、获取、查询等操作,其底层将数据以链表的形式保存。如果用 offer方法插入数据时队列没满,则数据插入成功,并立 即返回:如果队列满了,则直接返回 false。 如果用 poll方法删除数据时队列不为空, 则返回队 列头部的数据;如果队列为空,则立刻返回 null。
消息格式定义
队列消息接口定义(QueueMsg)
/**
@author james mu
@date 2020/7/27 22:00
*/
public interface QueueMsg {
//消息键
String getKey();
//消息头
QueueMsgHeaders getHeaders();
//消息负载byte数组
byte[] getData();
}
队列消息头接口定义(QueueMsgHeaders)
import java.util.Map;
/**
@author james mu
@date 2020/7/27 21:55
*/
public interface QueueMsgHeaders {
//消息头放入
byte[] put(String key, byte[] value);
//消息头通过key获取byte数组
byte[] get(String key);
//消息头数据全部读取方法
Map getData();
}
队列消息格式(ProtoQueueMsg)
/**
@author jamesmsw
@date 2021/2/19 2:23 下午
*/
public class ProtoQueueMsg implements QueueMsg {
private final String key;
private final String value;
private final QueueMsgHeaders headers;
public ProtoQueueMsg(String key, String value) {
this(key, value, new DefaultQueueMsgHeaders());
}
public ProtoQueueMsg(String key, String value, QueueMsgHeaders headers) {
this.key = key;
this.value = value;
this.headers = headers;
}
@Override
public String getKey() {
return key;
}
@Override
public QueueMsgHeaders getHeaders() {
return headers;
}
@Override
public byte[] getData() {
return value.getBytes();
}
}
默认队列消息头(DefaultQueueMsgHeaders)
import java.util.HashMap;
import java.util.Map;
/**
@author james mu
@date 2020/7/27 21:57
*/
public class DefaultQueueMsgHeaders implements QueueMsgHeaders {
protected final Map data = new HashMap<>();
@Override
public byte[] put(String key, byte[] value) {
return data.put(key, value);
}
@Override
public byte[] get(String key) {
return data.get(key);
}
@Override
public Map getData() {
return data;
}
}
消息生产者
import iot.technology.mqtt.storage.msg.QueueMsg;
import iot.technology.mqtt.storage.queue.QueueCallback;
/**
@author james mu
@date 2020/8/31 11:05
*/
public class Producer {
private final InMemoryStorage storage = InMemoryStorage.getInstance();
private final String defaultTopic;
public Producer(String defaultTopic) {
this.defaultTopic = defaultTopic;
}
public void send(String topicName, T msg) {
boolean result = storage.put(topicName, msg);
}
}
消息消费者
import lombok.extern.slf4j.Slf4j;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
@author james mu
//虚构函数
public Consumer(String topic) {
this.topic = topic;
stopped = false;
}
public String getTopic() {
return topic;
}
public void subscribe() {
topics = Collections.singleton(topic);
subscribed = true;
}
//批量订阅主题
public void subscribe(Set topics) {
this.topics = topics;
subscribed = true;
}
public void unsubscribe() {
stopped = true;
}
//不断读取topic集合下阻塞队列中的数据集合
public List poll(long durationInMillis) {
if (subscribed) {
List messages = topics
.stream()
.map(storage::get)
.flatMap(List::stream)
.map(msg -> (T) msg).collect(Collectors.toList());
if (messages.size() > 0) {
return messages;
}
try {
Thread.sleep(durationInMillis);
} catch (InterruptedException e) {
if (!stopped) {
log.error("Failed to sleep.", e);
}
}
}
return Collections.emptyList();
}
}
亚马逊测评 www.yisuping.cn