Skip to content

Commit 3d5cf49

Browse files
committed
🍱 deal with actor module
1 parent 581b440 commit 3d5cf49

File tree

11 files changed

+299
-5
lines changed

11 files changed

+299
-5
lines changed

IOT-Guide-Actor/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@
3434
<groupId>ch.qos.logback</groupId>
3535
<artifactId>logback-classic</artifactId>
3636
</dependency>
37+
<dependency>
38+
<groupId>junit</groupId>
39+
<artifactId>junit</artifactId>
40+
<scope>test</scope>
41+
</dependency>
3742
<dependency>
3843
<groupId>org.mockito</groupId>
3944
<artifactId>mockito-core</artifactId>

IOT-Guide-Actor/src/main/java/iot/technology/actor/AbstractActor.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
package iot.technology.actor;
22

3+
import iot.technology.actor.exception.ActorException;
4+
import lombok.Getter;
5+
36
/**
47
* @author mushuwei
58
*/
69
public abstract class AbstractActor implements Actor {
710

11+
@Getter
812
protected ActorCtx ctx;
913

1014
@Override
11-
public void init() throws ActorException {
15+
public void init(ActorCtx ctx) throws ActorException {
1216
this.ctx = ctx;
1317
}
1418

IOT-Guide-Actor/src/main/java/iot/technology/actor/Actor.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package iot.technology.actor;
22

3+
import iot.technology.actor.exception.ActorException;
34
import iot.technology.actor.message.ActorMsg;
45

56
/**
@@ -11,7 +12,7 @@ public interface Actor {
1112

1213
ActorRef getActorRef();
1314

14-
default void init() throws ActorException {
15+
default void init(ActorCtx ctx) throws ActorException {
1516
}
1617

1718
default void destroy() throws ActorException {

IOT-Guide-Actor/src/main/java/iot/technology/actor/ActorMailbox.java

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package iot.technology.actor;
22

33
import iot.technology.actor.message.ActorMsg;
4+
import iot.technology.actor.message.ActorStopReason;
5+
import iot.technology.actor.message.MsgType;
46
import lombok.Data;
57
import lombok.extern.slf4j.Slf4j;
68

79
import java.util.List;
810
import java.util.concurrent.ConcurrentLinkedQueue;
11+
import java.util.concurrent.TimeUnit;
912
import java.util.concurrent.atomic.AtomicBoolean;
1013
import java.util.function.Predicate;
1114
import java.util.function.Supplier;
@@ -23,17 +26,134 @@ public final class ActorMailbox implements ActorCtx {
2326
public static final boolean NOT_READY = false;
2427
public static final boolean READY = true;
2528

29+
private final ActorSystem system;
30+
private final ActorSystemSettings settings;
31+
private final ActorId selfId;
32+
private final Actor actor;
33+
private final Dispatcher dispatcher;
2634
private final ConcurrentLinkedQueue<ActorMsg> highPriorityMsgs = new ConcurrentLinkedQueue<>();
2735
private final ConcurrentLinkedQueue<ActorMsg> normalPriorityMsgs = new ConcurrentLinkedQueue<>();
36+
2837
private final AtomicBoolean busy = new AtomicBoolean(FREE);
2938
private final AtomicBoolean ready = new AtomicBoolean(NOT_READY);
3039
private final AtomicBoolean destroyInProgress = new AtomicBoolean();
40+
private volatile ActorStopReason stopReason;
41+
42+
public void initActor() {
43+
dispatcher.getExecutor().execute(() -> tryInit(1));
44+
}
45+
46+
private void tryInit(int attempt) {
47+
try {
48+
log.debug("[{}] Trying to init actor, attempt: {}", selfId, attempt);
49+
if (!destroyInProgress.get()) {
50+
actor.init(this);
51+
if (!destroyInProgress.get()) {
52+
ready.set(READY);
53+
tryProcessQueue(false);
54+
}
55+
}
56+
} catch (Throwable t) {
57+
log.debug("[{}] Failed to init actor, attempt: {}", selfId, attempt, t);
58+
int attemptIdx = attempt + 1;
59+
InitFailureStrategy strategy = actor.onInitFailure(attempt, t);
60+
if (strategy.isStop() ||
61+
(settings.getMaxActorInitAttempts() > 0 && attemptIdx > settings.getMaxActorInitAttempts())) {
62+
log.info("[{}] Failed to init actor, attempt {}, going to stop attempts.", selfId, attempt, t);
63+
stopReason = ActorStopReason.INIT_FAILED;
64+
destroy();
65+
} else if (strategy.getRetryDelay() > 0) {
66+
log.info("[{}] Failed to init actor, attempt {}, going to retry in attempts in {}ms", selfId, attempt,
67+
strategy.getRetryDelay());
68+
log.debug("[{}] Error", selfId, t);
69+
system.getScheduler().schedule(() -> dispatcher.getExecutor().execute(() -> tryInit(attemptIdx)), strategy.getRetryDelay(),
70+
TimeUnit.MICROSECONDS);
71+
} else {
72+
log.info("[{}] Failed to init actor, attempt {}, going to retry immediately", selfId, attempt);
73+
log.debug("[{}] Error", selfId, t);
74+
dispatcher.getExecutor().execute(() -> tryInit(attemptIdx));
75+
}
76+
}
77+
}
78+
79+
public void destroy() {
80+
if (stopReason == null) {
81+
stopReason = ActorStopReason.STOPPED;
82+
}
83+
destroyInProgress.set(true);
84+
dispatcher.getExecutor().execute(() -> {
85+
try {
86+
ready.set(NOT_READY);
87+
actor.destroy();
88+
highPriorityMsgs.forEach(msg -> msg.onActorStopped(stopReason));
89+
normalPriorityMsgs.forEach(msg -> msg.onActorStopped(stopReason));
90+
} catch (Throwable t) {
91+
log.warn("[{}] Failed to destroy actor: {}", selfId, t);
92+
}
93+
});
94+
}
3195

3296
private void enqueue(ActorMsg msg, boolean highPriority) {
3397
if (!destroyInProgress.get()) {
3498
if (highPriority) {
99+
highPriorityMsgs.add(msg);
100+
} else {
101+
normalPriorityMsgs.add(msg);
102+
}
103+
tryProcessQueue(true);
104+
} else {
105+
if (highPriority && msg.getMsgType().equals(MsgType.UPDATED_MSG)) {
106+
synchronized (this) {
107+
if (stopReason == ActorStopReason.INIT_FAILED) {
108+
destroyInProgress.set(false);
109+
stopReason = null;
110+
initActor();
111+
} else {
112+
msg.onActorStopped(stopReason);
113+
}
114+
}
115+
} else {
116+
msg.onActorStopped(stopReason);
117+
}
118+
}
119+
}
35120

121+
private void tryProcessQueue(boolean newMsg) {
122+
if (ready.get() == READY) {
123+
if (newMsg || !highPriorityMsgs.isEmpty() || !normalPriorityMsgs.isEmpty()) {
124+
if (busy.compareAndSet(FREE, BUSY)) {
125+
dispatcher.getExecutor().execute(this::processMailbox);
126+
} else {
127+
log.trace("[{}] MessageBox is busy, new msg: {}", selfId, newMsg);
128+
}
129+
} else {
130+
log.trace("[{}] MessageBox is empty, new msg: {}", selfId, newMsg);
36131
}
132+
} else {
133+
log.trace("[{}] MessageBox is not ready, new msg: {}", selfId, newMsg);
134+
}
135+
}
136+
137+
private void processMailbox() {
138+
boolean noMoreElements = false;
139+
for (int i = 0; i < settings.getActorThroughput(); i++) {
140+
ActorMsg msg = highPriorityMsgs.poll();
141+
if (msg == null) {
142+
msg = normalPriorityMsgs.poll();
143+
}
144+
if (msg != null) {
145+
log.debug("[{}] Going to process message: {}", selfId, msg);
146+
actor.process(msg);
147+
} else {
148+
noMoreElements = true;
149+
break;
150+
}
151+
}
152+
if (noMoreElements) {
153+
busy.set(FREE);
154+
dispatcher.getExecutor().execute(() -> tryProcessQueue(false));
155+
} else {
156+
dispatcher.getExecutor().execute(this::processMailbox);
37157
}
38158
}
39159

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package iot.technology.actor;
2+
3+
import java.util.concurrent.ThreadFactory;
4+
import java.util.concurrent.atomic.AtomicInteger;
5+
6+
/**
7+
* Copy of Executors.DefaultThreadFactory but with ability to set name of the pool
8+
*
9+
* @author mushuwei
10+
*/
11+
public class ActorThreadFactory implements ThreadFactory {
12+
public static final AtomicInteger poolNumber = new AtomicInteger(1);
13+
private final ThreadGroup group;
14+
private static final AtomicInteger threadNumber = new AtomicInteger(1);
15+
private final String namePrefix;
16+
17+
public static ActorThreadFactory forName(String name) {
18+
return new ActorThreadFactory(name);
19+
}
20+
21+
public ActorThreadFactory(String name) {
22+
SecurityManager s = System.getSecurityManager();
23+
group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
24+
namePrefix = name + "-" + poolNumber.getAndIncrement() + "-thread-";
25+
}
26+
27+
@Override
28+
public Thread newThread(Runnable r) {
29+
Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
30+
if (t.isDaemon()) {
31+
t.setDaemon(false);
32+
}
33+
if (t.getPriority() != Thread.NORM_PRIORITY) {
34+
t.setPriority(Thread.NORM_PRIORITY);
35+
}
36+
return t;
37+
}
38+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package iot.technology.actor;
2+
3+
import iot.technology.actor.message.ActorMsg;
4+
import lombok.Getter;
5+
6+
import java.util.List;
7+
import java.util.concurrent.*;
8+
import java.util.function.Predicate;
9+
10+
/**
11+
* @author mushuwei
12+
*/
13+
public class DefaultActorSystem implements ActorSystem {
14+
15+
private final ConcurrentMap<String, Dispatcher> dispatchers = new ConcurrentHashMap<>();
16+
17+
@Getter
18+
private final ActorSystemSettings settings;
19+
@Getter
20+
private final ScheduledExecutorService scheduler;
21+
22+
public DefaultActorSystem(ActorSystemSettings settings) {
23+
this.settings = settings;
24+
this.scheduler =
25+
Executors.newScheduledThreadPool(settings.getSchedulerPoolSize(), ActorThreadFactory.forName("actor-system-scheduler"));
26+
}
27+
28+
29+
@Override
30+
public void createDispatcher(String dispatcherId, ExecutorService executor) {
31+
32+
}
33+
34+
@Override
35+
public void destroyDispatcher(String dispatcherId) {
36+
37+
}
38+
39+
@Override
40+
public ActorRef getActor(ActorId actorId) {
41+
return null;
42+
}
43+
44+
@Override
45+
public ActorRef createRootActor(String dispatcherId, ActorCreator creator) {
46+
return null;
47+
}
48+
49+
@Override
50+
public ActorRef createChildActor(String dispatcherId, ActorCreator creator, ActorId actorId) {
51+
return null;
52+
}
53+
54+
@Override
55+
public void tell(ActorId target, ActorMsg actorMsg) {
56+
57+
}
58+
59+
@Override
60+
public void tellWithHighPriority(ActorId target, ActorMsg actorMsg) {
61+
62+
}
63+
64+
@Override
65+
public void stop(ActorRef actorRef) {
66+
67+
}
68+
69+
@Override
70+
public void stop(ActorId actorId) {
71+
72+
}
73+
74+
@Override
75+
public void stop() {
76+
77+
}
78+
79+
@Override
80+
public void broadcastToChildren(ActorId parent, ActorMsg msg) {
81+
82+
}
83+
84+
@Override
85+
public void broadcastToChildren(ActorId parent, Predicate<ActorId> childFilter, ActorMsg msg) {
86+
87+
}
88+
89+
@Override
90+
public List<ActorId> filterChildren(ActorId parent, Predicate<ActorId> childFilter) {
91+
return null;
92+
}
93+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package iot.technology.actor;
2+
3+
import lombok.Data;
4+
5+
import java.util.concurrent.ExecutorService;
6+
7+
/**
8+
* @author mushuwei
9+
*/
10+
@Data
11+
public class Dispatcher {
12+
13+
private final String dispatcherId;
14+
private final ExecutorService executor;
15+
}

IOT-Guide-Actor/src/main/java/iot/technology/actor/ActorException.java renamed to IOT-Guide-Actor/src/main/java/iot/technology/actor/exception/ActorException.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package iot.technology.actor;
1+
package iot.technology.actor.exception;
22

33
/**
44
* @author mushuwei

IOT-Guide-Actor/src/main/java/iot/technology/actor/message/MsgType.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,8 @@
44
* @author mushuwei
55
*/
66
public enum MsgType {
7-
7+
/**
8+
* Special message to indicate update request
9+
*/
10+
UPDATED_MSG,
811
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package iot.technology.actor;
2+
3+
import lombok.extern.slf4j.Slf4j;
4+
import org.junit.runner.RunWith;
5+
import org.mockito.junit.MockitoJUnitRunner;
6+
7+
/**
8+
* @author mushuwei
9+
*/
10+
@Slf4j
11+
@RunWith(MockitoJUnitRunner.class)
12+
public class ActorSystemTest {
13+
14+
private volatile ActorSystem actorSystem;
15+
}

0 commit comments

Comments
 (0)