Skip to content

Commit 50a36aa

Browse files
jevinjiangJiangShuJu
and
JiangShuJu
authored
[ISSUE #4788] Support disruptor as memory queue (#4844)
* [ISSUE #4788] Support disruptor as memory queue * [ISSUE #4788] fix code style --------- Co-authored-by: JiangShuJu <[email protected]>
1 parent 015b6e9 commit 50a36aa

File tree

14 files changed

+385
-270
lines changed

14 files changed

+385
-270
lines changed

Diff for: eventmesh-storage-plugin/eventmesh-storage-standalone/build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
dependencies {
1919
implementation project(":eventmesh-common")
2020
implementation project(":eventmesh-storage-plugin:eventmesh-storage-api")
21+
implementation "com.lmax:disruptor"
2122

2223
compileOnly 'org.projectlombok:lombok'
2324
annotationProcessor 'org.projectlombok:lombok'

Diff for: eventmesh-storage-plugin/eventmesh-storage-standalone/src/main/java/org/apache/eventmesh/storage/standalone/admin/StandaloneAdmin.java

+4-22
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
import org.apache.eventmesh.api.admin.AbstractAdmin;
2121
import org.apache.eventmesh.api.admin.TopicProperties;
22-
import org.apache.eventmesh.storage.standalone.broker.MessageQueue;
22+
import org.apache.eventmesh.storage.standalone.broker.Channel;
2323
import org.apache.eventmesh.storage.standalone.broker.StandaloneBroker;
2424
import org.apache.eventmesh.storage.standalone.broker.model.TopicMetadata;
2525

@@ -42,11 +42,11 @@ public StandaloneAdmin() {
4242

4343
@Override
4444
public List<TopicProperties> getTopic() throws Exception {
45-
ConcurrentHashMap<TopicMetadata, MessageQueue> messageContainer = this.standaloneBroker.getMessageContainer();
45+
ConcurrentHashMap<TopicMetadata, Channel> messageContainer = this.standaloneBroker.getMessageContainer();
4646
List<TopicProperties> topicList = new ArrayList<>();
4747
messageContainer.keySet().forEach(topicMetadata -> {
48-
MessageQueue messageQueue = messageContainer.get(topicMetadata);
49-
final int messageCount = messageQueue.getPutIndex() - messageQueue.getTakeIndex();
48+
Channel channel = messageContainer.get(topicMetadata);
49+
final int messageCount = channel.getMessageCount();
5050
topicList.add(new TopicProperties(
5151
topicMetadata.getTopicName(),
5252
messageCount));
@@ -65,25 +65,7 @@ public void deleteTopic(String topicName) {
6565
standaloneBroker.deleteTopicIfExist(topicName);
6666
}
6767

68-
@Override
69-
public List<CloudEvent> getEvent(String topicName, int offset, int length) throws Exception {
70-
if (!this.standaloneBroker.checkTopicExist(topicName)) {
71-
throw new Exception("The topic name doesn't exist in the message queue");
72-
}
73-
ConcurrentHashMap<TopicMetadata, MessageQueue> messageContainer = this.standaloneBroker.getMessageContainer();
74-
long topicOffset = messageContainer.get(new TopicMetadata(topicName)).getTakeIndex();
7568

76-
List<CloudEvent> messageList = new ArrayList<>();
77-
for (int index = 0; index < length; index++) {
78-
long messageOffset = topicOffset + offset + index;
79-
CloudEvent event = this.standaloneBroker.getMessage(topicName, messageOffset);
80-
if (event == null) {
81-
break;
82-
}
83-
messageList.add(event);
84-
}
85-
return messageList;
86-
}
8769

8870
@Override
8971
public void publish(CloudEvent cloudEvent) throws Exception {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.eventmesh.storage.standalone.broker;
19+
20+
import org.apache.eventmesh.api.LifeCycle;
21+
import org.apache.eventmesh.common.EventMeshThreadFactory;
22+
import org.apache.eventmesh.storage.standalone.broker.model.MessageEntity;
23+
import org.apache.eventmesh.storage.standalone.broker.model.TopicMetadata;
24+
import org.apache.eventmesh.storage.standalone.broker.provider.DisruptorProvider;
25+
26+
import com.lmax.disruptor.BlockingWaitStrategy;
27+
import com.lmax.disruptor.EventHandler;
28+
import com.lmax.disruptor.IgnoreExceptionHandler;
29+
import com.lmax.disruptor.RingBuffer;
30+
import com.lmax.disruptor.dsl.Disruptor;
31+
import com.lmax.disruptor.dsl.ProducerType;
32+
33+
import lombok.Getter;
34+
35+
36+
public class Channel implements LifeCycle {
37+
38+
public static final Integer DEFAULT_SIZE = 4096 << 1 << 1;
39+
@Getter
40+
private DisruptorProvider provider;
41+
private final Integer size;
42+
private final EventHandler<MessageEntity> eventHandler;
43+
private volatile boolean started = false;
44+
private final TopicMetadata topic;
45+
private static final String THREAD_NAME_PREFIX = "standalone_disruptor_provider_";
46+
47+
public Channel(TopicMetadata topic, EventHandler<MessageEntity> eventHandler) {
48+
this(DEFAULT_SIZE, topic, eventHandler);
49+
}
50+
51+
52+
public Channel(final Integer ringBufferSize, final TopicMetadata topic, final EventHandler<MessageEntity> eventHandler) {
53+
this.size = ringBufferSize;
54+
this.topic = topic;
55+
this.eventHandler = eventHandler;
56+
}
57+
58+
59+
@Override
60+
public boolean isStarted() {
61+
return started;
62+
}
63+
64+
@Override
65+
public boolean isClosed() {
66+
return !isStarted();
67+
}
68+
69+
public synchronized void start() {
70+
if (isClosed()) {
71+
doStart();
72+
started = true;
73+
}
74+
}
75+
76+
public void doStart() {
77+
Disruptor<MessageEntity> disruptor = new Disruptor<>(
78+
MessageEntity::new,
79+
size,
80+
new EventMeshThreadFactory(THREAD_NAME_PREFIX + topic.getTopicName(), true),
81+
ProducerType.MULTI,
82+
new BlockingWaitStrategy()
83+
);
84+
85+
disruptor.handleEventsWith(eventHandler);
86+
disruptor.setDefaultExceptionHandler(new IgnoreExceptionHandler());
87+
RingBuffer<MessageEntity> ringBuffer = disruptor.getRingBuffer();
88+
provider = new DisruptorProvider(ringBuffer, disruptor);
89+
provider.start();
90+
}
91+
92+
public int getMessageCount() {
93+
return provider.getMessageCount();
94+
}
95+
96+
@Override
97+
public synchronized void shutdown() {
98+
if (isStarted()) {
99+
provider.shutdown();
100+
provider = null;
101+
started = false;
102+
}
103+
}
104+
105+
}

Diff for: eventmesh-storage-plugin/eventmesh-storage-standalone/src/main/java/org/apache/eventmesh/storage/standalone/broker/StandaloneBroker.java

+51-62
Original file line numberDiff line numberDiff line change
@@ -19,71 +19,75 @@
1919

2020
import org.apache.eventmesh.storage.standalone.broker.model.MessageEntity;
2121
import org.apache.eventmesh.storage.standalone.broker.model.TopicMetadata;
22-
import org.apache.eventmesh.storage.standalone.broker.task.HistoryMessageClear;
23-
import org.apache.eventmesh.storage.standalone.broker.task.HistoryMessageClearTask;
24-
25-
import org.apache.commons.lang3.tuple.Pair;
22+
import org.apache.eventmesh.storage.standalone.broker.task.Subscribe;
2623

2724
import java.util.concurrent.ConcurrentHashMap;
28-
import java.util.concurrent.atomic.AtomicLong;
2925

3026
import io.cloudevents.CloudEvent;
3127

28+
import lombok.Getter;
29+
import lombok.extern.slf4j.Slf4j;
30+
3231
/**
3332
* This broker used to store event, it just support standalone mode, you shouldn't use this module in production environment
3433
*/
34+
@Slf4j
3535
public class StandaloneBroker {
3636

37-
private final ConcurrentHashMap<TopicMetadata, MessageQueue> messageContainer;
37+
// message source by topic
38+
@Getter
39+
private final ConcurrentHashMap<TopicMetadata, Channel> messageContainer;
3840

39-
// todo: move the offset manage to consumer
40-
private final ConcurrentHashMap<TopicMetadata, AtomicLong> offsetMap;
41+
@Getter
42+
private final ConcurrentHashMap<TopicMetadata, Subscribe> subscribeContainer;
4143

4244
private StandaloneBroker() {
4345
this.messageContainer = new ConcurrentHashMap<>();
44-
this.offsetMap = new ConcurrentHashMap<>();
45-
startHistoryMessageCleanTask();
46-
}
47-
48-
public ConcurrentHashMap<TopicMetadata, MessageQueue> getMessageContainer() {
49-
return this.messageContainer;
46+
this.subscribeContainer = new ConcurrentHashMap<>();
5047
}
5148

52-
public ConcurrentHashMap<TopicMetadata, AtomicLong> getOffsetMap() {
53-
return this.offsetMap;
54-
}
5549

5650
public static StandaloneBroker getInstance() {
57-
return StandaloneBrokerInstanceHolder.instance;
51+
return StandaloneBrokerInstanceHolder.INSTANCE;
5852
}
5953

6054
/**
6155
* put message
6256
*
6357
* @param topicName topic name
6458
* @param message message
65-
* @throws InterruptedException
6659
*/
67-
public MessageEntity putMessage(String topicName, CloudEvent message) throws InterruptedException {
68-
Pair<MessageQueue, AtomicLong> pair = createTopicIfAbsent(topicName);
69-
AtomicLong topicOffset = pair.getRight();
70-
MessageQueue messageQueue = pair.getLeft();
71-
72-
MessageEntity messageEntity = new MessageEntity(
73-
new TopicMetadata(topicName), message, topicOffset.getAndIncrement(), System.currentTimeMillis());
74-
messageQueue.put(messageEntity);
75-
60+
public MessageEntity putMessage(String topicName, CloudEvent message) {
61+
TopicMetadata topicMetadata = new TopicMetadata(topicName);
62+
if (!messageContainer.containsKey(topicMetadata)) {
63+
createTopic(topicName);
64+
}
65+
Channel channel = messageContainer.get(topicMetadata);
66+
MessageEntity messageEntity = new MessageEntity(new TopicMetadata(topicName), message);
67+
channel.getProvider().onData(messageEntity);
7668
return messageEntity;
7769
}
7870

71+
public Channel createTopic(String topicName) {
72+
TopicMetadata topicMetadata = new TopicMetadata(topicName);
73+
return messageContainer.computeIfAbsent(topicMetadata, k -> {
74+
Subscribe subscribe = subscribeContainer.get(topicMetadata);
75+
if (subscribe == null) {
76+
throw new IllegalStateException("the topic not exist subscribe ");
77+
}
78+
Channel channel = new Channel(topicMetadata, subscribe);
79+
channel.start();
80+
return channel;
81+
});
82+
}
83+
7984
/**
8085
* Get the message, if the queue is empty then await
8186
*
8287
* @param topicName
8388
*/
8489
public CloudEvent takeMessage(String topicName) throws InterruptedException {
85-
TopicMetadata topicMetadata = new TopicMetadata(topicName);
86-
return messageContainer.computeIfAbsent(topicMetadata, k -> new MessageQueue()).take().getMessage();
90+
return null;
8791
}
8892

8993
/**
@@ -92,12 +96,7 @@ public CloudEvent takeMessage(String topicName) throws InterruptedException {
9296
* @param topicName
9397
*/
9498
public CloudEvent getMessage(String topicName) {
95-
TopicMetadata topicMetadata = new TopicMetadata(topicName);
96-
MessageEntity head = messageContainer.computeIfAbsent(topicMetadata, k -> new MessageQueue()).getHead();
97-
if (head == null) {
98-
return null;
99-
}
100-
return head.getMessage();
99+
return null;
101100
}
102101

103102
/**
@@ -108,21 +107,9 @@ public CloudEvent getMessage(String topicName) {
108107
* @return CloudEvent
109108
*/
110109
public CloudEvent getMessage(String topicName, long offset) {
111-
TopicMetadata topicMetadata = new TopicMetadata(topicName);
112-
MessageEntity messageEntity = messageContainer.computeIfAbsent(topicMetadata, k -> new MessageQueue()).getByOffset(offset);
113-
if (messageEntity == null) {
114-
return null;
115-
}
116-
return messageEntity.getMessage();
110+
return null;
117111
}
118112

119-
private void startHistoryMessageCleanTask() {
120-
HistoryMessageClear historyMessageClear = new HistoryMessageClear(messageContainer);
121-
Thread thread = new Thread(new HistoryMessageClearTask(historyMessageClear));
122-
thread.setDaemon(true);
123-
thread.setName("StandaloneBroker-HistoryMessageCleanTask");
124-
thread.start();
125-
}
126113

127114
public boolean checkTopicExist(String topicName) {
128115
return messageContainer.containsKey(new TopicMetadata(topicName));
@@ -132,13 +119,10 @@ public boolean checkTopicExist(String topicName) {
132119
* if the topic does not exist, create the topic
133120
*
134121
* @param topicName topicName
135-
* @return messageQueue and offset
122+
* @return Channel
136123
*/
137-
public Pair<MessageQueue, AtomicLong> createTopicIfAbsent(String topicName) {
138-
TopicMetadata topicMetadata = new TopicMetadata(topicName);
139-
MessageQueue messageQueue = messageContainer.computeIfAbsent(topicMetadata, k -> new MessageQueue());
140-
AtomicLong offset = offsetMap.computeIfAbsent(topicMetadata, k -> new AtomicLong());
141-
return Pair.of(messageQueue, offset);
124+
public Channel createTopicIfAbsent(String topicName) {
125+
return createTopic(topicName);
142126
}
143127

144128
/**
@@ -148,18 +132,23 @@ public Pair<MessageQueue, AtomicLong> createTopicIfAbsent(String topicName) {
148132
*/
149133
public void deleteTopicIfExist(String topicName) {
150134
TopicMetadata topicMetadata = new TopicMetadata(topicName);
135+
Channel channel = createTopicIfAbsent(topicName);
136+
channel.shutdown();
151137
messageContainer.remove(topicMetadata);
152138
}
153139

154-
public void updateOffset(TopicMetadata topicMetadata, long offset) {
155-
offsetMap.computeIfPresent(topicMetadata, (k, v) -> {
156-
v.set(offset);
157-
return v;
158-
});
140+
public void subscribed(String topicName, Subscribe subscribe) {
141+
TopicMetadata topicMetadata = new TopicMetadata(topicName);
142+
if (getMessageContainer().containsKey(topicMetadata)) {
143+
log.warn("the topic already subscribed");
144+
return;
145+
}
146+
subscribeContainer.put(topicMetadata, subscribe);
159147
}
160148

149+
161150
private static class StandaloneBrokerInstanceHolder {
162151

163-
private static final StandaloneBroker instance = new StandaloneBroker();
152+
private static final StandaloneBroker INSTANCE = new StandaloneBroker();
164153
}
165-
}
154+
}

Diff for: eventmesh-storage-plugin/eventmesh-storage-standalone/src/main/java/org/apache/eventmesh/storage/standalone/broker/model/MessageEntity.java

+8
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121

2222
import io.cloudevents.CloudEvent;
2323

24+
import lombok.NoArgsConstructor;
25+
26+
@NoArgsConstructor
2427
public class MessageEntity implements Serializable {
2528

2629
private static final long serialVersionUID = 6646148767540524786L;
@@ -40,6 +43,11 @@ public MessageEntity(TopicMetadata topicMetadata, CloudEvent message, long offse
4043
this.createTimeMills = currentTimeMills;
4144
}
4245

46+
public MessageEntity(TopicMetadata topicMetadata, CloudEvent message) {
47+
this.topicMetadata = topicMetadata;
48+
this.message = message;
49+
}
50+
4351
public TopicMetadata getTopicMetadata() {
4452
return topicMetadata;
4553
}

0 commit comments

Comments
 (0)