38 changed files with 1604 additions and 0 deletions
-
4application/pom.xml
-
27common/actor/pom.xml
-
36common/actor/src/main/java/com/thing/common/actor/Actor.java
-
56common/actor/src/main/java/com/thing/common/actor/ActorBizContext.java
-
12common/actor/src/main/java/com/thing/common/actor/ActorCreator.java
-
27common/actor/src/main/java/com/thing/common/actor/ActorId.java
-
16common/actor/src/main/java/com/thing/common/actor/ActorMsg.java
-
29common/actor/src/main/java/com/thing/common/actor/ActorRef.java
-
90common/actor/src/main/java/com/thing/common/actor/ActorSysContext.java
-
44common/actor/src/main/java/com/thing/common/actor/ActorSystem.java
-
16common/actor/src/main/java/com/thing/common/actor/enumeration/ActorBizType.java
-
19common/actor/src/main/java/com/thing/common/actor/enumeration/ActorMsgType.java
-
11common/actor/src/main/java/com/thing/common/actor/enumeration/ActorStopReason.java
-
38common/actor/src/main/java/com/thing/common/actor/exception/ActorException.java
-
17common/actor/src/main/java/com/thing/common/actor/exception/ActorNotRegisteredException.java
-
23common/actor/src/main/java/com/thing/common/actor/system/AbstractActor.java
-
18common/actor/src/main/java/com/thing/common/actor/system/AbstractActorCreator.java
-
30common/actor/src/main/java/com/thing/common/actor/system/AbstractActorId.java
-
5common/actor/src/main/java/com/thing/common/actor/system/ActorDispatcher.java
-
263common/actor/src/main/java/com/thing/common/actor/system/ActorMailbox.java
-
10common/actor/src/main/java/com/thing/common/actor/system/ActorSystemSettings.java
-
242common/actor/src/main/java/com/thing/common/actor/system/DefaultActorSystem.java
-
29common/actor/src/main/java/com/thing/common/actor/system/InitFailureStrategy.java
-
23common/actor/src/main/java/com/thing/common/actor/system/ProcessFailureStrategy.java
-
1common/pom.xml
-
27modules/actor-biz/pom.xml
-
50modules/actor-biz/src/main/java/com/thing/common/actor/biz/actors/company/CompanyActor.java
-
37modules/actor-biz/src/main/java/com/thing/common/actor/biz/actors/company/CompanyActorId.java
-
16modules/actor-biz/src/main/java/com/thing/common/actor/biz/actors/company/CompanyChangedMsg.java
-
42modules/actor-biz/src/main/java/com/thing/common/actor/biz/actors/company/NonCompanyActor.java
-
28modules/actor-biz/src/main/java/com/thing/common/actor/biz/actors/root/ActorSysProperties.java
-
95modules/actor-biz/src/main/java/com/thing/common/actor/biz/actors/root/RootActor.java
-
16modules/actor-biz/src/main/java/com/thing/common/actor/biz/actors/root/RootInitMsg.java
-
58modules/actor-biz/src/main/java/com/thing/common/actor/biz/base/BizContextAwareActor.java
-
38modules/actor-biz/src/main/java/com/thing/common/actor/biz/base/DefaultActorBizContext.java
-
100modules/actor-biz/src/main/java/com/thing/common/actor/biz/lifecycle/ActorLifecycle.java
-
1modules/pom.xml
-
10pom.xml
@ -0,0 +1,27 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
<parent> |
|||
<groupId>com.thing</groupId> |
|||
<artifactId>common</artifactId> |
|||
<version>5.1</version> |
|||
</parent> |
|||
|
|||
<groupId>com.thing.common</groupId> |
|||
<artifactId>actor</artifactId> |
|||
<packaging>jar</packaging> |
|||
<name>ThingBI Server Common Actor</name> |
|||
|
|||
<dependencies> |
|||
<dependency> |
|||
<groupId>com.thing.common</groupId> |
|||
<artifactId>util</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.projectlombok</groupId> |
|||
<artifactId>lombok</artifactId> |
|||
</dependency> |
|||
</dependencies> |
|||
</project> |
|||
@ -0,0 +1,36 @@ |
|||
package com.thing.common.actor; |
|||
|
|||
import com.thing.common.actor.enumeration.ActorStopReason; |
|||
import com.thing.common.actor.exception.ActorException; |
|||
import com.thing.common.actor.system.InitFailureStrategy; |
|||
import com.thing.common.actor.system.ProcessFailureStrategy; |
|||
|
|||
/** |
|||
* @author siyang |
|||
* @date 2024/8/1 10:27 |
|||
* @description Actor模型定义 |
|||
*/ |
|||
public interface Actor { |
|||
|
|||
/** 收到消息后的处理逻辑 */ |
|||
void process(ActorMsg msg); |
|||
|
|||
/** 获取一个Actor代理对象 */ |
|||
ActorRef getActorRef(); |
|||
|
|||
default void init(ActorSysContext actorSysContext) {} |
|||
|
|||
default void destroy(ActorStopReason stopReason, Throwable cause) throws ActorException {} |
|||
|
|||
default InitFailureStrategy onInitFailure(int attempt, Throwable t) { |
|||
return InitFailureStrategy.retryWithDelay(5000L * attempt); |
|||
} |
|||
|
|||
default ProcessFailureStrategy onProcessFailure(ActorMsg msg, Throwable t) { |
|||
if (t instanceof Error) { |
|||
return ProcessFailureStrategy.stop(); |
|||
} else { |
|||
return ProcessFailureStrategy.resume(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,56 @@ |
|||
package com.thing.common.actor; |
|||
|
|||
import java.util.concurrent.ScheduledExecutorService; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
/** |
|||
* @author siyang |
|||
* @date 2024/8/7 11:07 |
|||
* @description 业务上下文 |
|||
*/ |
|||
public interface ActorBizContext { |
|||
ActorRef getRootActor(); |
|||
|
|||
ActorSystem getActorSystem(); |
|||
|
|||
default ScheduledExecutorService getScheduler() { |
|||
return getActorSystem().getScheduler(); |
|||
} |
|||
|
|||
default void tell(ActorMsg actorMsg) { |
|||
getRootActor().tell(actorMsg); |
|||
} |
|||
|
|||
default void tellImportant(ActorMsg actorMsg) { |
|||
getRootActor().tellImportant(actorMsg); |
|||
} |
|||
|
|||
/** |
|||
* 定时通知(固定时间间隔) |
|||
* |
|||
* @param actor actor代理 |
|||
* @param actorMsg actor消息 |
|||
* @param initialDelay 初始延时(ms) |
|||
* @param interval 任务间隔(ms) |
|||
*/ |
|||
default void scheduleTell(ActorRef actor, ActorMsg actorMsg, long initialDelay, long interval) { |
|||
getScheduler() |
|||
.scheduleWithFixedDelay( |
|||
() -> actor.tell(actorMsg), initialDelay, interval, TimeUnit.MILLISECONDS); |
|||
} |
|||
|
|||
/** |
|||
* 延迟通知 |
|||
* |
|||
* @param actor actor代理 |
|||
* @param actorMsg actor消息 |
|||
* @param delay 延迟时间(ms) |
|||
*/ |
|||
default void delayTell(ActorRef actor, ActorMsg actorMsg, long delay) { |
|||
if (delay > 0) { |
|||
getScheduler().schedule(() -> actor.tell(actorMsg), delay, TimeUnit.MILLISECONDS); |
|||
} else { |
|||
actor.tell(actorMsg); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
package com.thing.common.actor; |
|||
|
|||
/** |
|||
* @author siyang |
|||
* @date 2024/8/1 14:07 |
|||
* @description |
|||
*/ |
|||
public interface ActorCreator { |
|||
ActorId createActorId(); |
|||
|
|||
Actor createActor(); |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
package com.thing.common.actor; |
|||
|
|||
import com.thing.common.actor.enumeration.ActorBizType; |
|||
import com.thing.common.actor.system.AbstractActorId; |
|||
|
|||
import java.io.Serializable; |
|||
|
|||
/** |
|||
* @author siyang |
|||
* @date 2024/8/1 11:51 |
|||
* @description actor唯一标识,由id和bizType两部分组成。唯一性判定逻辑参考{@link AbstractActorId} |
|||
*/ |
|||
public interface ActorId { |
|||
/** |
|||
* 每个actor的id都是唯一的,不可重复 |
|||
* |
|||
* @return 唯一的id |
|||
*/ |
|||
Serializable id(); |
|||
|
|||
/** |
|||
* actor业务类型 |
|||
* |
|||
* @return 不同actor的业务类型可以相同 |
|||
*/ |
|||
ActorBizType bizType(); |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
package com.thing.common.actor; |
|||
|
|||
import com.thing.common.actor.enumeration.ActorMsgType; |
|||
import com.thing.common.actor.enumeration.ActorStopReason; |
|||
|
|||
/** |
|||
* @author siyang |
|||
* @date 2024/8/1 10:30 |
|||
* @description |
|||
*/ |
|||
public interface ActorMsg { |
|||
ActorMsgType getMsgType(); |
|||
|
|||
/** 按业务场景,在合适的子类中实现,针对不同的actor歇菜原因做不同的处理 */ |
|||
default void onActorStopped(ActorStopReason reason) {} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
package com.thing.common.actor; |
|||
|
|||
/** |
|||
* @author siyang |
|||
* @date 2024/8/1 10:39 |
|||
* @description actor代理对象接口 |
|||
*/ |
|||
public interface ActorRef { |
|||
/** |
|||
* 获取actor唯一标识对象 |
|||
* |
|||
* @return actor唯一标识对象 |
|||
*/ |
|||
ActorId getActorId(); |
|||
|
|||
/** |
|||
* 普通的通知 |
|||
* |
|||
* @param actorMsg 消息 |
|||
*/ |
|||
void tell(ActorMsg actorMsg); |
|||
|
|||
/** |
|||
* 重要通知:邮箱中同时存在"通知"和"重要通知",会优先执行重要通知的邮件 |
|||
* |
|||
* @param actorMsg 消息 |
|||
*/ |
|||
void tellImportant(ActorMsg actorMsg); |
|||
} |
|||
@ -0,0 +1,90 @@ |
|||
package com.thing.common.actor; |
|||
|
|||
import com.thing.common.actor.enumeration.ActorBizType; |
|||
|
|||
import java.util.List; |
|||
import java.util.function.Predicate; |
|||
import java.util.function.Supplier; |
|||
|
|||
/** |
|||
* @author siyang |
|||
* @date 2024/8/1 14:04 |
|||
* @description 系统上下文 |
|||
*/ |
|||
public interface ActorSysContext extends ActorRef { |
|||
/** 获取自己的唯一标识 */ |
|||
ActorId getSelf(); |
|||
|
|||
/** 获取父级 Actor的代理对象 */ |
|||
ActorRef getParentRef(); |
|||
|
|||
/** |
|||
* 向目标actor通知消息 |
|||
* |
|||
* @param target 目标actor的唯一标识 |
|||
* @param actorMsg 消息 |
|||
*/ |
|||
void tell(ActorId target, ActorMsg actorMsg); |
|||
|
|||
/** |
|||
* 向目标actor群发消息 |
|||
* |
|||
* @param targetList actor唯一标识列表 |
|||
* @param actorMsg 消息 |
|||
*/ |
|||
void tell(List<ActorId> targetList, ActorMsg actorMsg); |
|||
|
|||
/** |
|||
* 停止目标actor |
|||
* |
|||
* @param target actor唯一标识 |
|||
*/ |
|||
void stop(ActorId target); |
|||
|
|||
/** |
|||
* 获取或者创建子Actor代理对象 |
|||
* |
|||
* @param actorId actor唯一标识 |
|||
* @param dispatcher 执行分发器 |
|||
* @param creator actor创建器 |
|||
* @param createCondition actor创建条件 |
|||
* @return ActorRef |
|||
*/ |
|||
ActorRef getOrCreateChildActor( |
|||
ActorId actorId, |
|||
Supplier<String> dispatcher, |
|||
Supplier<ActorCreator> creator, |
|||
Supplier<Boolean> createCondition); |
|||
|
|||
/** |
|||
* 过滤Actor |
|||
* |
|||
* @param childFilter 过滤条件 |
|||
* @return 子ActorId列表 |
|||
*/ |
|||
List<ActorId> filterChildren(Predicate<ActorId> childFilter); |
|||
|
|||
/** |
|||
* 向actor广播消息 |
|||
* |
|||
* @param msg 消息 |
|||
* @param highPriority 是否高优先级 |
|||
*/ |
|||
void broadcastToChildren(ActorMsg msg, boolean highPriority); |
|||
|
|||
/** |
|||
* 向actor广播消息 |
|||
* |
|||
* @param msg 消息 |
|||
* @param actorBizType actor业务类型 |
|||
*/ |
|||
void broadcastToChildrenByType(ActorMsg msg, ActorBizType actorBizType); |
|||
|
|||
/** |
|||
* 向子actor广播消息 |
|||
* |
|||
* @param msg 消息 |
|||
* @param childFilter actor过滤条件 |
|||
*/ |
|||
void broadcastToChildren(ActorMsg msg, Predicate<ActorId> childFilter); |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
package com.thing.common.actor; |
|||
|
|||
import java.util.List; |
|||
import java.util.concurrent.ExecutorService; |
|||
import java.util.concurrent.ScheduledExecutorService; |
|||
import java.util.function.Predicate; |
|||
|
|||
/** |
|||
* @author siyang |
|||
* @date 2024/8/1 14:37 |
|||
* @description actor系统能力定义 |
|||
*/ |
|||
public interface ActorSystem { |
|||
|
|||
ScheduledExecutorService getScheduler(); |
|||
|
|||
void createDispatcher(String dispatcherId, ExecutorService executor); |
|||
|
|||
void destroyDispatcher(String dispatcherId); |
|||
|
|||
ActorRef getActor(ActorId actorId); |
|||
|
|||
ActorRef createRootActor(String dispatcherId, ActorCreator creator); |
|||
|
|||
ActorRef createChildActor(String dispatcherId, ActorCreator creator, ActorId parent); |
|||
|
|||
void tell(ActorId target, ActorMsg actorMsg); |
|||
|
|||
void tellImportant(ActorId target, ActorMsg actorMsg); |
|||
|
|||
void stop(ActorRef actorRef); |
|||
|
|||
void stop(ActorId actorId); |
|||
|
|||
void stop(); |
|||
|
|||
void broadcastToChildren(ActorId parent, ActorMsg msg); |
|||
|
|||
void broadcastToChildren(ActorId parent, ActorMsg msg, boolean highPriority); |
|||
|
|||
void broadcastToChildren(ActorId parent, Predicate<ActorId> childFilter, ActorMsg msg); |
|||
|
|||
List<ActorId> filterChildren(ActorId parent, Predicate<ActorId> childFilter); |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
package com.thing.common.actor.enumeration; |
|||
|
|||
/** |
|||
* @author siyang |
|||
* @date 2024/8/1 11:52 |
|||
* @description actor业务类型 |
|||
*/ |
|||
public enum ActorBizType { |
|||
COMPANY, |
|||
NON_COMPANY, |
|||
DEVICE; |
|||
|
|||
public static boolean same(ActorBizType a, ActorBizType b) { |
|||
return a.equals(b); |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
package com.thing.common.actor.enumeration; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Getter; |
|||
|
|||
/** |
|||
* @author siyang |
|||
* @date 2024/8/1 10:28 |
|||
* @description 消息类型 |
|||
*/ |
|||
@Getter |
|||
@AllArgsConstructor |
|||
public enum ActorMsgType { |
|||
SYSTEM_INIT_MSG(false), |
|||
COMPANY_CHANGED_MSG(false); |
|||
|
|||
/** 表示当前消息是否依赖租户信息 */ |
|||
private final Boolean tenantBased; |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
package com.thing.common.actor.enumeration; |
|||
|
|||
/** |
|||
* @author siyang |
|||
* @date 2024/8/1 10:33 |
|||
* @description actor歇菜原因 |
|||
*/ |
|||
public enum ActorStopReason { |
|||
INIT_FAILED, |
|||
STOPPED |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
package com.thing.common.actor.exception; |
|||
|
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
|
|||
/** |
|||
* @author siyang |
|||
* @date 2024/8/1 15:08 |
|||
* @description actor错误,在发生时需给出判断:是否还能抢救一下。如果无法抢救,给unrecoverable赋值为true,在后续逻辑中做相应处理 |
|||
*/ |
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
public class ActorException extends Exception { |
|||
private final boolean unrecoverable; |
|||
|
|||
public ActorException(String message, Throwable cause) { |
|||
super(message, cause); |
|||
this.unrecoverable = false; |
|||
} |
|||
|
|||
public ActorException(String message) { |
|||
this(message, false); |
|||
} |
|||
|
|||
public ActorException(String message, boolean unrecoverable) { |
|||
super(message); |
|||
this.unrecoverable = unrecoverable; |
|||
} |
|||
|
|||
public ActorException(Exception e) { |
|||
this(e, false); |
|||
} |
|||
|
|||
public ActorException(Exception e, boolean unrecoverable) { |
|||
super(e); |
|||
this.unrecoverable = unrecoverable; |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
package com.thing.common.actor.exception; |
|||
|
|||
import com.thing.common.actor.ActorId; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
public class ActorNotRegisteredException extends RuntimeException { |
|||
|
|||
private ActorId target; |
|||
|
|||
public ActorNotRegisteredException(ActorId target, String message) { |
|||
super(message); |
|||
this.target = target; |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
package com.thing.common.actor.system; |
|||
|
|||
import com.thing.common.actor.Actor; |
|||
import com.thing.common.actor.ActorRef; |
|||
import com.thing.common.actor.ActorSysContext; |
|||
|
|||
import lombok.Getter; |
|||
|
|||
@Getter |
|||
public abstract class AbstractActor implements Actor { |
|||
|
|||
protected ActorSysContext actorSysContext; |
|||
|
|||
@Override |
|||
public void init(ActorSysContext actorSysContext) { |
|||
this.actorSysContext = actorSysContext; |
|||
} |
|||
|
|||
@Override |
|||
public ActorRef getActorRef() { |
|||
return actorSysContext; |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
package com.thing.common.actor.system; |
|||
|
|||
import com.thing.common.actor.ActorBizContext; |
|||
import com.thing.common.actor.ActorCreator; |
|||
|
|||
/** |
|||
* @author siyang |
|||
* @date 2024/8/2 10:07 |
|||
* @description 基于上下文的actor创建器 |
|||
*/ |
|||
public abstract class AbstractActorCreator implements ActorCreator { |
|||
protected final transient ActorBizContext actorBizContext; |
|||
|
|||
public AbstractActorCreator(ActorBizContext actorBizContext) { |
|||
super(); |
|||
this.actorBizContext = actorBizContext; |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
package com.thing.common.actor.system; |
|||
|
|||
|
|||
import com.thing.common.actor.ActorId; |
|||
import com.thing.common.actor.enumeration.ActorBizType; |
|||
|
|||
/** |
|||
* @author siyang |
|||
* @date 2024/8/2 10:40 |
|||
*/ |
|||
public abstract class AbstractActorId implements ActorId { |
|||
|
|||
@Override |
|||
public boolean equals(Object o) { |
|||
if (this == o) return true; |
|||
if (o == null || getClass() != o.getClass()) return false; |
|||
AbstractActorId that = (AbstractActorId) o; |
|||
return id().equals(that.id()) && ActorBizType.same(bizType(), that.bizType()); |
|||
} |
|||
|
|||
@Override |
|||
public int hashCode() { |
|||
return (bizType() + "_" + id()).hashCode(); |
|||
} |
|||
|
|||
@Override |
|||
public String toString() { |
|||
return bizType() + ":" + id(); |
|||
} |
|||
} |
|||
@ -0,0 +1,5 @@ |
|||
package com.thing.common.actor.system; |
|||
|
|||
import java.util.concurrent.ExecutorService; |
|||
|
|||
public record ActorDispatcher(String dispatcherId, ExecutorService executor) {} |
|||
@ -0,0 +1,263 @@ |
|||
package com.thing.common.actor.system; |
|||
|
|||
import com.thing.common.actor.*; |
|||
import com.thing.common.actor.enumeration.ActorBizType; |
|||
import com.thing.common.actor.enumeration.ActorStopReason; |
|||
import com.thing.common.actor.exception.ActorException; |
|||
|
|||
import lombok.Getter; |
|||
import lombok.RequiredArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
|
|||
import java.util.List; |
|||
import java.util.Objects; |
|||
import java.util.concurrent.ConcurrentLinkedQueue; |
|||
import java.util.concurrent.TimeUnit; |
|||
import java.util.concurrent.atomic.AtomicBoolean; |
|||
import java.util.function.Predicate; |
|||
import java.util.function.Supplier; |
|||
|
|||
@Slf4j |
|||
@Getter |
|||
@RequiredArgsConstructor |
|||
public final class ActorMailbox implements ActorSysContext { |
|||
private static final boolean HIGH_PRIORITY = true; |
|||
private static final boolean NORMAL_PRIORITY = false; |
|||
|
|||
private static final boolean FREE = false; |
|||
private static final boolean BUSY = true; |
|||
|
|||
private static final boolean NOT_READY = false; |
|||
private static final boolean READY = true; |
|||
|
|||
private final ActorSystem system; |
|||
private final ActorSystemSettings settings; |
|||
private final ActorId selfId; |
|||
private final ActorRef parentRef; |
|||
private final Actor actor; |
|||
private final ActorDispatcher dispatcher; |
|||
|
|||
private final ConcurrentLinkedQueue<ActorMsg> highPriorityMQ = new ConcurrentLinkedQueue<>(); |
|||
private final ConcurrentLinkedQueue<ActorMsg> normalPriorityMQ = new ConcurrentLinkedQueue<>(); |
|||
private final AtomicBoolean busy = new AtomicBoolean(FREE); |
|||
private final AtomicBoolean ready = new AtomicBoolean(NOT_READY); |
|||
private final AtomicBoolean destroyInProgress = new AtomicBoolean(); |
|||
private volatile ActorStopReason stopReason; |
|||
|
|||
public void initActor() { |
|||
dispatcher.executor().execute(() -> tryInit(1)); |
|||
} |
|||
|
|||
private void tryInit(int attempt) { |
|||
try { |
|||
log.debug("[{}] Trying to init actor, attempt: {}", selfId, attempt); |
|||
if (!destroyInProgress.get()) { |
|||
actor.init(this); |
|||
if (!destroyInProgress.get()) { |
|||
ready.set(READY); |
|||
tryProcessQueue(false); |
|||
} |
|||
} |
|||
} catch (Throwable t) { |
|||
InitFailureStrategy strategy; |
|||
int attemptIdx = attempt + 1; |
|||
if (isUnrecoverable(t)) { |
|||
strategy = InitFailureStrategy.stop(); |
|||
} else { |
|||
log.debug("[{}] Failed to init actor, attempt: {}", selfId, attempt, t); |
|||
strategy = actor.onInitFailure(attempt, t); |
|||
} |
|||
if (strategy.isStop() |
|||
|| (settings.maxActorInitAttempts() > 0 |
|||
&& attemptIdx > settings.maxActorInitAttempts())) { |
|||
log.info( |
|||
"[{}] Failed to init actor, attempt {}, going to stop attempts.", |
|||
selfId, |
|||
attempt, |
|||
t); |
|||
stopReason = ActorStopReason.INIT_FAILED; |
|||
destroy(t.getCause()); |
|||
} else if (strategy.getRetryDelay() > 0) { |
|||
log.info( |
|||
"[{}] Failed to init actor, attempt {}, going to retry in attempts in {}ms", |
|||
selfId, |
|||
attempt, |
|||
strategy.getRetryDelay()); |
|||
system.getScheduler() |
|||
.schedule( |
|||
() -> dispatcher.executor().execute(() -> tryInit(attemptIdx)), |
|||
strategy.getRetryDelay(), |
|||
TimeUnit.MILLISECONDS); |
|||
} else { |
|||
log.info( |
|||
"[{}] Failed to init actor, attempt {}, going to retry immediately", |
|||
selfId, |
|||
attempt); |
|||
dispatcher.executor().execute(() -> tryInit(attemptIdx)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private static boolean isUnrecoverable(Throwable t) { |
|||
if (t instanceof ActorException) { |
|||
if (Objects.nonNull(t.getCause())) { |
|||
return isUnrecoverable(t.getCause()); |
|||
} else { |
|||
return ((ActorException) t).isUnrecoverable(); |
|||
} |
|||
} else { |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
private void enqueue(ActorMsg msg, boolean highPriority) { |
|||
if (!destroyInProgress.get()) { |
|||
if (highPriority) { |
|||
highPriorityMQ.add(msg); |
|||
} else { |
|||
normalPriorityMQ.add(msg); |
|||
} |
|||
tryProcessQueue(true); |
|||
} else { |
|||
msg.onActorStopped(stopReason); |
|||
} |
|||
} |
|||
|
|||
private void tryProcessQueue(boolean newMsg) { |
|||
// 当前邮箱还未就绪 |
|||
if (ready.get() != READY) { |
|||
log.trace("[{}] MailBox is not ready, new msg: {}", selfId, newMsg); |
|||
return; |
|||
} |
|||
// 当前消息不是新的,且邮箱为空 |
|||
if (!newMsg && highPriorityMQ.isEmpty() && normalPriorityMQ.isEmpty()) { |
|||
log.trace("[{}] MailBox is empty, new msg: {}", selfId, false); |
|||
return; |
|||
} |
|||
// 当前消息是新消息,或者邮箱非空,则通过CAS方式设置邮箱为繁忙状态。CAS竞争失败则表示当前邮箱正在处理消息 |
|||
if (busy.compareAndSet(FREE, BUSY)) { |
|||
dispatcher.executor().execute(this::processMailbox); |
|||
} else { |
|||
log.trace("[{}] MailBox is busy, new msg: {}", selfId, newMsg); |
|||
} |
|||
} |
|||
|
|||
private void processMailbox() { |
|||
boolean noMoreElements = false; |
|||
for (int i = 0; i < settings.actorThroughput(); i++) { |
|||
ActorMsg msg = highPriorityMQ.poll(); |
|||
if (Objects.isNull(msg)) { |
|||
msg = normalPriorityMQ.poll(); |
|||
} |
|||
if (Objects.nonNull(msg)) { |
|||
try { |
|||
log.debug("[{}] Going to process message: {}", selfId, msg); |
|||
actor.process(msg); |
|||
} catch (Throwable t) { |
|||
log.debug("[{}] Failed to process message: {}", selfId, msg, t); |
|||
ProcessFailureStrategy strategy = actor.onProcessFailure(msg, t); |
|||
if (strategy.isStop()) { |
|||
system.stop(selfId); |
|||
} |
|||
} |
|||
} else { |
|||
noMoreElements = true; |
|||
break; |
|||
} |
|||
} |
|||
if (noMoreElements) { |
|||
busy.set(FREE); |
|||
dispatcher.executor().execute(() -> tryProcessQueue(false)); |
|||
} else { |
|||
dispatcher.executor().execute(this::processMailbox); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public ActorId getSelf() { |
|||
return selfId; |
|||
} |
|||
|
|||
@Override |
|||
public void tell(ActorId target, ActorMsg actorMsg) { |
|||
system.tell(target, actorMsg); |
|||
} |
|||
|
|||
@Override |
|||
public void tell(List<ActorId> targetList, ActorMsg actorMsg) { |
|||
targetList.forEach(target -> tell(target, actorMsg)); |
|||
} |
|||
|
|||
@Override |
|||
public void broadcastToChildren(ActorMsg msg, boolean highPriority) { |
|||
system.broadcastToChildren(selfId, msg, highPriority); |
|||
} |
|||
|
|||
@Override |
|||
public void broadcastToChildrenByType(ActorMsg msg, ActorBizType actorBizType) { |
|||
broadcastToChildren(msg, actorId -> actorBizType.equals(actorId.bizType())); |
|||
} |
|||
|
|||
@Override |
|||
public void broadcastToChildren(ActorMsg msg, Predicate<ActorId> childFilter) { |
|||
system.broadcastToChildren(selfId, childFilter, msg); |
|||
} |
|||
|
|||
@Override |
|||
public List<ActorId> filterChildren(Predicate<ActorId> childFilter) { |
|||
return system.filterChildren(selfId, childFilter); |
|||
} |
|||
|
|||
@Override |
|||
public void stop(ActorId target) { |
|||
system.stop(target); |
|||
} |
|||
|
|||
@Override |
|||
public ActorRef getOrCreateChildActor( |
|||
ActorId actorId, |
|||
Supplier<String> dispatcher, |
|||
Supplier<ActorCreator> creator, |
|||
Supplier<Boolean> createCondition) { |
|||
ActorRef actorRef = system.getActor(actorId); |
|||
if (Objects.isNull(actorRef) && createCondition.get()) { |
|||
return system.createChildActor(dispatcher.get(), creator.get(), selfId); |
|||
} else { |
|||
return actorRef; |
|||
} |
|||
} |
|||
|
|||
public void destroy(Throwable cause) { |
|||
if (Objects.isNull(stopReason)) { |
|||
stopReason = ActorStopReason.STOPPED; |
|||
} |
|||
destroyInProgress.set(true); |
|||
dispatcher |
|||
.executor() |
|||
.execute( |
|||
() -> { |
|||
try { |
|||
ready.set(NOT_READY); |
|||
actor.destroy(stopReason, cause); |
|||
highPriorityMQ.forEach(msg -> msg.onActorStopped(stopReason)); |
|||
normalPriorityMQ.forEach(msg -> msg.onActorStopped(stopReason)); |
|||
} catch (Throwable ignore) { |
|||
} |
|||
}); |
|||
} |
|||
|
|||
@Override |
|||
public ActorId getActorId() { |
|||
return selfId; |
|||
} |
|||
|
|||
@Override |
|||
public void tell(ActorMsg actorMsg) { |
|||
enqueue(actorMsg, NORMAL_PRIORITY); |
|||
} |
|||
|
|||
@Override |
|||
public void tellImportant(ActorMsg actorMsg) { |
|||
enqueue(actorMsg, HIGH_PRIORITY); |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
package com.thing.common.actor.system; |
|||
|
|||
/** |
|||
* actor系统设置 |
|||
* |
|||
* @param actorThroughput actor系统吞吐量:当一个actor处理完该数量消息后,若还有消息,则重新生成任务再执行。目的是为了避免单个actor占用太多cpu时间 |
|||
* @param schedulerPoolSize 定时调度线程池大小 |
|||
* @param maxActorInitAttempts actor初始化失败重试次数 |
|||
*/ |
|||
public record ActorSystemSettings(int actorThroughput, int schedulerPoolSize, int maxActorInitAttempts) {} |
|||
@ -0,0 +1,242 @@ |
|||
package com.thing.common.actor.system; |
|||
|
|||
import java.util.Collections; |
|||
import java.util.List; |
|||
import java.util.Objects; |
|||
import java.util.Set; |
|||
import java.util.concurrent.*; |
|||
import java.util.concurrent.locks.Lock; |
|||
import java.util.concurrent.locks.ReentrantLock; |
|||
import java.util.function.Predicate; |
|||
import java.util.stream.Collectors; |
|||
|
|||
import com.thing.common.actor.*; |
|||
import com.thing.common.actor.exception.ActorNotRegisteredException; |
|||
import com.thing.common.util.thread.ThingThreadFactory; |
|||
import lombok.Data; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
|
|||
/** |
|||
* @author siyang |
|||
* @date 2024/8/1 14:50 |
|||
* @description 默认的Actor系统实现 |
|||
*/ |
|||
@Data |
|||
@Slf4j |
|||
public class DefaultActorSystem implements ActorSystem { |
|||
|
|||
private final ConcurrentMap<String, ActorDispatcher> dispatchers = new ConcurrentHashMap<>(); |
|||
private final ConcurrentMap<ActorId, ActorMailbox> actors = new ConcurrentHashMap<>(); |
|||
private final ConcurrentMap<ActorId, ReentrantLock> actorCreationLocks = new ConcurrentHashMap<>(); |
|||
private final ConcurrentMap<ActorId, Set<ActorId>> parentChildMap = new ConcurrentHashMap<>(); |
|||
|
|||
private final ActorSystemSettings settings; |
|||
private final ScheduledExecutorService scheduler; |
|||
|
|||
public DefaultActorSystem(ActorSystemSettings settings) { |
|||
this.settings = settings; |
|||
this.scheduler = |
|||
Executors.newScheduledThreadPool( |
|||
settings.schedulerPoolSize(), |
|||
ThingThreadFactory.forName("actor-system-scheduler")); |
|||
} |
|||
|
|||
@Override |
|||
public void createDispatcher(String dispatcherId, ExecutorService executor) { |
|||
ActorDispatcher current = |
|||
dispatchers.putIfAbsent(dispatcherId, new ActorDispatcher(dispatcherId, executor)); |
|||
if (Objects.nonNull(current)) { |
|||
throw new RuntimeException( |
|||
"Dispatcher with id [" + dispatcherId + "] is already registered!"); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void destroyDispatcher(String dispatcherId) { |
|||
ActorDispatcher actorDispatcher = dispatchers.remove(dispatcherId); |
|||
if (Objects.nonNull(actorDispatcher)) { |
|||
actorDispatcher.executor().shutdownNow(); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public ActorRef getActor(ActorId actorId) { |
|||
return actors.get(actorId); |
|||
} |
|||
|
|||
@Override |
|||
public ActorRef createRootActor(String dispatcherId, ActorCreator creator) { |
|||
return createActor(dispatcherId, creator, null); |
|||
} |
|||
|
|||
@Override |
|||
public ActorRef createChildActor(String dispatcherId, ActorCreator creator, ActorId parent) { |
|||
return createActor(dispatcherId, creator, parent); |
|||
} |
|||
|
|||
private ActorRef createActor(String dispatcherId, ActorCreator creator, ActorId parent) { |
|||
ActorDispatcher actorDispatcher = dispatchers.get(dispatcherId); |
|||
if (Objects.isNull(actorDispatcher)) { |
|||
log.warn("Dispatcher with id [{}] is not registered!", dispatcherId); |
|||
throw new RuntimeException( |
|||
"Dispatcher with id [" + dispatcherId + "] is not registered!"); |
|||
} |
|||
|
|||
ActorId actorId = creator.createActorId(); |
|||
ActorMailbox actorMailbox = actors.get(actorId); |
|||
if (Objects.nonNull(actorMailbox)) { |
|||
return actorMailbox; |
|||
} |
|||
|
|||
Lock actorCreationLock = |
|||
actorCreationLocks.computeIfAbsent(actorId, id -> new ReentrantLock()); |
|||
actorCreationLock.lock(); |
|||
try { |
|||
actorMailbox = actors.get(actorId); |
|||
if (Objects.nonNull(actorMailbox)) { |
|||
return actorMailbox; |
|||
} |
|||
|
|||
log.debug("Creating actor with id [{}]!", actorId); |
|||
Actor actor = creator.createActor(); |
|||
ActorRef parentRef = null; |
|||
if (Objects.nonNull(parent)) { |
|||
parentRef = getActor(parent); |
|||
if (Objects.isNull(parentRef)) { |
|||
throw new ActorNotRegisteredException( |
|||
parent, "Parent Actor with id [" + parent + "] is not registered!"); |
|||
} |
|||
} |
|||
ActorMailbox mailbox = |
|||
new ActorMailbox(this, settings, actorId, parentRef, actor, actorDispatcher); |
|||
actors.put(actorId, mailbox); |
|||
mailbox.initActor(); |
|||
actorMailbox = mailbox; |
|||
if (Objects.nonNull(parent)) { |
|||
parentChildMap |
|||
.computeIfAbsent(parent, id -> ConcurrentHashMap.newKeySet()) |
|||
.add(actorId); |
|||
} |
|||
} finally { |
|||
actorCreationLock.unlock(); |
|||
actorCreationLocks.remove(actorId); |
|||
} |
|||
return actorMailbox; |
|||
} |
|||
|
|||
@Override |
|||
public void tellImportant(ActorId target, ActorMsg actorMsg) { |
|||
tell(target, actorMsg, true); |
|||
} |
|||
|
|||
@Override |
|||
public void tell(ActorId target, ActorMsg actorMsg) { |
|||
tell(target, actorMsg, false); |
|||
} |
|||
|
|||
private void tell(ActorId target, ActorMsg actorMsg, boolean highPriority) { |
|||
ActorMailbox mailbox = actors.get(target); |
|||
if (Objects.isNull(mailbox)) { |
|||
throw new ActorNotRegisteredException( |
|||
target, "Actor with id [" + target + "] is not registered!"); |
|||
} |
|||
if (highPriority) { |
|||
mailbox.tellImportant(actorMsg); |
|||
} else { |
|||
mailbox.tell(actorMsg); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void broadcastToChildren(ActorId parent, ActorMsg msg) { |
|||
broadcastToChildren(parent, msg, false); |
|||
} |
|||
|
|||
@Override |
|||
public void broadcastToChildren(ActorId parent, ActorMsg msg, boolean highPriority) { |
|||
broadcastToChildren(parent, id -> true, msg, highPriority); |
|||
} |
|||
|
|||
@Override |
|||
public void broadcastToChildren(ActorId parent, Predicate<ActorId> childFilter, ActorMsg msg) { |
|||
broadcastToChildren(parent, childFilter, msg, false); |
|||
} |
|||
|
|||
private void broadcastToChildren( |
|||
ActorId parent, Predicate<ActorId> childFilter, ActorMsg msg, boolean highPriority) { |
|||
Set<ActorId> children = parentChildMap.get(parent); |
|||
if (Objects.nonNull(children)) { |
|||
children.stream() |
|||
.filter(childFilter) |
|||
.forEach( |
|||
id -> { |
|||
try { |
|||
tell(id, msg, highPriority); |
|||
} catch (ActorNotRegisteredException e) { |
|||
log.warn("Actor is missing for {}", id); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public List<ActorId> filterChildren(ActorId parent, Predicate<ActorId> childFilter) { |
|||
Set<ActorId> children = parentChildMap.get(parent); |
|||
if (Objects.nonNull(children)) { |
|||
return children.stream().filter(childFilter).collect(Collectors.toList()); |
|||
} else { |
|||
return Collections.emptyList(); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void stop(ActorRef actorRef) { |
|||
stop(actorRef.getActorId()); |
|||
} |
|||
|
|||
@Override |
|||
public void stop(ActorId actorId) { |
|||
Set<ActorId> children = parentChildMap.remove(actorId); |
|||
if (Objects.nonNull(children)) { |
|||
for (ActorId child : children) { |
|||
stop(child); |
|||
} |
|||
} |
|||
parentChildMap.values().forEach(parentChildren -> parentChildren.remove(actorId)); |
|||
|
|||
ActorMailbox mailbox = actors.remove(actorId); |
|||
if (Objects.nonNull(mailbox)) { |
|||
mailbox.destroy(null); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void stop() { |
|||
dispatchers |
|||
.values() |
|||
.forEach( |
|||
actorDispatcher -> { |
|||
actorDispatcher.executor().shutdown(); |
|||
try { |
|||
boolean terminated = |
|||
actorDispatcher |
|||
.executor() |
|||
.awaitTermination(3, TimeUnit.SECONDS); |
|||
if (!terminated) { |
|||
log.warn( |
|||
"[{}] Failed to stop dispatcher", |
|||
actorDispatcher.dispatcherId()); |
|||
} |
|||
} catch (InterruptedException e) { |
|||
log.warn( |
|||
"[{}] Failed to stop dispatcher", |
|||
actorDispatcher.dispatcherId(), |
|||
e); |
|||
} |
|||
}); |
|||
if (Objects.nonNull(scheduler)) { |
|||
scheduler.shutdownNow(); |
|||
} |
|||
actors.clear(); |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
package com.thing.common.actor.system; |
|||
|
|||
import lombok.Getter; |
|||
import lombok.ToString; |
|||
|
|||
@Getter |
|||
@ToString |
|||
public class InitFailureStrategy { |
|||
|
|||
private final boolean stop; |
|||
private final long retryDelay; |
|||
|
|||
private InitFailureStrategy(boolean stop, long retryDelay) { |
|||
this.stop = stop; |
|||
this.retryDelay = retryDelay; |
|||
} |
|||
|
|||
public static InitFailureStrategy retryImmediately() { |
|||
return retryWithDelay(0); |
|||
} |
|||
|
|||
public static InitFailureStrategy retryWithDelay(long ms) { |
|||
return new InitFailureStrategy(false, ms); |
|||
} |
|||
|
|||
public static InitFailureStrategy stop() { |
|||
return new InitFailureStrategy(true, 0); |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
package com.thing.common.actor.system; |
|||
|
|||
import lombok.Getter; |
|||
import lombok.ToString; |
|||
|
|||
@Getter |
|||
@ToString |
|||
public class ProcessFailureStrategy { |
|||
|
|||
private final boolean stop; |
|||
|
|||
private ProcessFailureStrategy(boolean stop) { |
|||
this.stop = stop; |
|||
} |
|||
|
|||
public static ProcessFailureStrategy stop() { |
|||
return new ProcessFailureStrategy(true); |
|||
} |
|||
|
|||
public static ProcessFailureStrategy resume() { |
|||
return new ProcessFailureStrategy(false); |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
<parent> |
|||
<groupId>com.thing</groupId> |
|||
<artifactId>modules</artifactId> |
|||
<version>5.1</version> |
|||
</parent> |
|||
|
|||
<groupId>com.thing.modules</groupId> |
|||
<artifactId>actor-biz</artifactId> |
|||
<packaging>jar</packaging> |
|||
<name>ThingBI Server Modules Actor Biz</name> |
|||
|
|||
<dependencies> |
|||
<dependency> |
|||
<groupId>com.thing.common</groupId> |
|||
<artifactId>actor</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>com.thing.modules</groupId> |
|||
<artifactId>thing</artifactId> |
|||
</dependency> |
|||
</dependencies> |
|||
</project> |
|||
@ -0,0 +1,50 @@ |
|||
package com.thing.common.actor.biz.actors.company; |
|||
|
|||
import com.thing.common.actor.Actor; |
|||
import com.thing.common.actor.ActorBizContext; |
|||
import com.thing.common.actor.ActorId; |
|||
import com.thing.common.actor.ActorMsg; |
|||
import com.thing.common.actor.biz.base.BizContextAwareActor; |
|||
import com.thing.common.actor.system.AbstractActorCreator; |
|||
|
|||
import java.util.Optional; |
|||
|
|||
/** |
|||
* @author siyang |
|||
* @date 2024/8/8 08:46 |
|||
* @description 公司actor,处理公司范畴下的业务 |
|||
*/ |
|||
public class CompanyActor extends BizContextAwareActor { |
|||
|
|||
public CompanyActor(ActorBizContext actorBizContext) { |
|||
super(actorBizContext); |
|||
} |
|||
|
|||
@Override |
|||
protected boolean doProcess(ActorMsg msg) { |
|||
return false; |
|||
} |
|||
|
|||
public static class ActorCreator extends AbstractActorCreator { |
|||
|
|||
private final Long companyId; |
|||
|
|||
private final CompanyActorId companyActorId; |
|||
|
|||
public ActorCreator(ActorBizContext actorBizContext, Long companyId) { |
|||
super(actorBizContext); |
|||
this.companyId = companyId; |
|||
this.companyActorId = new CompanyActorId(companyId); |
|||
} |
|||
|
|||
@Override |
|||
public ActorId createActorId() { |
|||
return Optional.ofNullable(companyActorId).orElse(new CompanyActorId(companyId)); |
|||
} |
|||
|
|||
@Override |
|||
public Actor createActor() { |
|||
return new CompanyActor(actorBizContext); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
package com.thing.common.actor.biz.actors.company; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnore; |
|||
import com.thing.common.actor.enumeration.ActorBizType; |
|||
import com.thing.common.actor.system.AbstractActorId; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Getter; |
|||
|
|||
import java.io.Serializable; |
|||
|
|||
/** |
|||
* @author siyang |
|||
* @date 2024/8/8 08:42 |
|||
* @description 公司actor唯一标识 |
|||
*/ |
|||
@Getter |
|||
@AllArgsConstructor |
|||
public class CompanyActorId extends AbstractActorId { |
|||
/** 公司顶级actorId,也是rootActor的id */ |
|||
@JsonIgnore public static final CompanyActorId ROOT_ID = new CompanyActorId(-1L); |
|||
|
|||
/** 非公司顶级actorId */ |
|||
@JsonIgnore public static final CompanyActorId NON_COMPANY_ID = new CompanyActorId(-2L); |
|||
|
|||
private final Long id; |
|||
|
|||
@Override |
|||
public Serializable id() { |
|||
return id; |
|||
} |
|||
|
|||
@Override |
|||
public ActorBizType bizType() { |
|||
return ActorBizType.COMPANY; |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
package com.thing.common.actor.biz.actors.company; |
|||
|
|||
import com.thing.common.actor.ActorMsg; |
|||
import com.thing.common.actor.enumeration.ActorMsgType; |
|||
|
|||
/** |
|||
* @author siyang |
|||
* @date 2024/8/5 11:04 |
|||
* @description |
|||
*/ |
|||
public record CompanyChangedMsg(Long id, Boolean add, Boolean remove) implements ActorMsg { |
|||
@Override |
|||
public ActorMsgType getMsgType() { |
|||
return ActorMsgType.COMPANY_CHANGED_MSG; |
|||
} |
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
package com.thing.common.actor.biz.actors.company; |
|||
|
|||
import com.thing.common.actor.Actor; |
|||
import com.thing.common.actor.ActorBizContext; |
|||
import com.thing.common.actor.ActorId; |
|||
import com.thing.common.actor.ActorMsg; |
|||
import com.thing.common.actor.biz.base.BizContextAwareActor; |
|||
import com.thing.common.actor.system.AbstractActorCreator; |
|||
|
|||
/** |
|||
* @author siyang |
|||
* @date 2024/8/8 08:47 |
|||
* @description 非公司actor,业务与公司无关 |
|||
*/ |
|||
public class NonCompanyActor extends BizContextAwareActor { |
|||
|
|||
public NonCompanyActor(ActorBizContext actorBizContext) { |
|||
super(actorBizContext); |
|||
} |
|||
|
|||
@Override |
|||
protected boolean doProcess(ActorMsg msg) { |
|||
return false; |
|||
} |
|||
|
|||
public static class ActorCreator extends AbstractActorCreator { |
|||
|
|||
public ActorCreator(ActorBizContext actorBizContext) { |
|||
super(actorBizContext); |
|||
} |
|||
|
|||
@Override |
|||
public ActorId createActorId() { |
|||
return CompanyActorId.NON_COMPANY_ID; |
|||
} |
|||
|
|||
@Override |
|||
public Actor createActor() { |
|||
return new NonCompanyActor(actorBizContext); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
package com.thing.common.actor.biz.actors.root; |
|||
|
|||
import lombok.Data; |
|||
import org.springframework.boot.context.properties.ConfigurationProperties; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
/** |
|||
* @author siyang |
|||
* @date 2024/8/2 11:36 |
|||
* @description actor系统配置 |
|||
*/ |
|||
@Data |
|||
@Component |
|||
@ConfigurationProperties(prefix = "actors.system") |
|||
public class ActorSysProperties { |
|||
public static final String ROOT_DISPATCHER = "root-dispatcher"; |
|||
public static final String DEVICE_DISPATCHER = "device-dispatcher"; |
|||
public static final String COMPANY_DISPATCHER = "company-dispatcher"; |
|||
public static final String NON_COMPANY_DISPATCHER = "non-company-dispatcher"; |
|||
|
|||
private int throughput = 5; |
|||
private int maxActorInitAttempts = 10; |
|||
private int schedulerPoolSize = 1; |
|||
private int rootDispatcherSize = 1; |
|||
private int tenantDispatcherSize = 2; |
|||
private int nonTenantDispatcherSize = 2; |
|||
private int deviceDispatcherSize = 4; |
|||
} |
|||
@ -0,0 +1,95 @@ |
|||
package com.thing.common.actor.biz.actors.root; |
|||
|
|||
import com.thing.common.actor.*; |
|||
import com.thing.common.actor.biz.actors.company.CompanyActor; |
|||
import com.thing.common.actor.biz.actors.company.CompanyActorId; |
|||
import com.thing.common.actor.biz.actors.company.CompanyChangedMsg; |
|||
import com.thing.common.actor.biz.actors.company.NonCompanyActor; |
|||
import com.thing.common.actor.biz.base.BizContextAwareActor; |
|||
import com.thing.common.actor.system.AbstractActorCreator; |
|||
import com.thing.sys.tenant.entity.SysTenantDetailEntity; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @author siyang |
|||
* @date 2024/8/1 16:59 |
|||
* @description 顶级actor |
|||
*/ |
|||
@Slf4j |
|||
public class RootActor extends BizContextAwareActor { |
|||
|
|||
public RootActor(ActorBizContext actorBizContext) { |
|||
super(actorBizContext); |
|||
} |
|||
|
|||
@Override |
|||
protected boolean doProcess(ActorMsg msg) { |
|||
switch (msg.getMsgType()) { |
|||
case SYSTEM_INIT_MSG -> systemInit(); |
|||
case COMPANY_CHANGED_MSG -> changeCompany((CompanyChangedMsg) msg); |
|||
default -> { |
|||
return false; |
|||
} |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
private void systemInit() { |
|||
initCompanyActors(); |
|||
initNonCompanyActors(); |
|||
} |
|||
|
|||
private void changeCompany(CompanyChangedMsg msg) { |
|||
if (msg.add()) { |
|||
getOrCreateCompanyActor(msg.id()); |
|||
} |
|||
if (msg.remove()) { |
|||
actorSysContext.stop(new CompanyActorId(msg.id())); |
|||
} |
|||
} |
|||
|
|||
private void initCompanyActors() { |
|||
List<SysTenantDetailEntity> list = defaultBizCtx().getSysTenantDetailService().list(); |
|||
list.forEach(tenant -> getOrCreateCompanyActor(tenant.getId())); |
|||
} |
|||
|
|||
private void initNonCompanyActors() { |
|||
getOrCreateNonCompanyActor(); |
|||
} |
|||
|
|||
private void getOrCreateCompanyActor(Long tenantId) { |
|||
actorSysContext.getOrCreateChildActor( |
|||
new CompanyActorId(tenantId), |
|||
() -> ActorSysProperties.COMPANY_DISPATCHER, |
|||
() -> new CompanyActor.ActorCreator(actorBizContext, tenantId), |
|||
() -> true); |
|||
} |
|||
|
|||
private void getOrCreateNonCompanyActor() { |
|||
actorSysContext.getOrCreateChildActor( |
|||
CompanyActorId.NON_COMPANY_ID, |
|||
() -> ActorSysProperties.NON_COMPANY_DISPATCHER, |
|||
() -> new NonCompanyActor.ActorCreator(actorBizContext), |
|||
() -> true); |
|||
} |
|||
|
|||
public static class ActorCreator extends AbstractActorCreator { |
|||
|
|||
public ActorCreator(ActorBizContext actorBizContext) { |
|||
super(actorBizContext); |
|||
} |
|||
|
|||
@Override |
|||
public ActorId createActorId() { |
|||
return CompanyActorId.ROOT_ID; |
|||
} |
|||
|
|||
@Override |
|||
public Actor createActor() { |
|||
return new RootActor(actorBizContext); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
package com.thing.common.actor.biz.actors.root; |
|||
|
|||
import com.thing.common.actor.ActorMsg; |
|||
import com.thing.common.actor.enumeration.ActorMsgType; |
|||
|
|||
/** |
|||
* @author siyang |
|||
* @date 2024/8/1 17:01 |
|||
* @description 顶级actor初始化消息 |
|||
*/ |
|||
public class RootInitMsg implements ActorMsg { |
|||
@Override |
|||
public ActorMsgType getMsgType() { |
|||
return ActorMsgType.SYSTEM_INIT_MSG; |
|||
} |
|||
} |
|||
@ -0,0 +1,58 @@ |
|||
package com.thing.common.actor.biz.base; |
|||
|
|||
import com.thing.common.actor.ActorBizContext; |
|||
import com.thing.common.actor.ActorMsg; |
|||
import com.thing.common.actor.system.AbstractActor; |
|||
import com.thing.common.actor.system.ProcessFailureStrategy; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
|
|||
/** |
|||
* @author siyang |
|||
* @date 2024/8/1 16:48 |
|||
* @description 提供业务上下文能力的Actor,所有涉及业务处理的actor都应继承该类 |
|||
*/ |
|||
@Slf4j |
|||
public abstract class BizContextAwareActor extends AbstractActor { |
|||
protected final ActorBizContext actorBizContext; |
|||
|
|||
public BizContextAwareActor(ActorBizContext actorBizContext) { |
|||
this.actorBizContext = actorBizContext; |
|||
} |
|||
|
|||
public DefaultActorBizContext defaultBizCtx() { |
|||
return (DefaultActorBizContext) actorBizContext; |
|||
} |
|||
|
|||
/** |
|||
* 消息处理核心方法,由子类实现 |
|||
* |
|||
* @param msg 消息内容 |
|||
* @return 是否处理成功 |
|||
*/ |
|||
protected abstract boolean doProcess(ActorMsg msg); |
|||
|
|||
@Override |
|||
public void process(ActorMsg msg) { |
|||
if (log.isDebugEnabled()) { |
|||
log.debug("Processing msg: {}", msg); |
|||
} |
|||
if (!doProcess(msg)) { |
|||
log.warn("Unprocessed message: {}!", msg); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public ProcessFailureStrategy onProcessFailure(ActorMsg msg, Throwable t) { |
|||
log.debug("[{}] Processing failure for msg {}", getActorRef().getActorId(), msg, t); |
|||
return doProcessFailure(t); |
|||
} |
|||
|
|||
protected ProcessFailureStrategy doProcessFailure(Throwable t) { |
|||
if (t instanceof Error) { |
|||
return ProcessFailureStrategy.stop(); |
|||
} else { |
|||
return ProcessFailureStrategy.resume(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
package com.thing.common.actor.biz.base; |
|||
|
|||
import com.thing.common.actor.ActorBizContext; |
|||
import com.thing.common.actor.ActorRef; |
|||
import com.thing.common.actor.ActorSystem; |
|||
import com.thing.sys.tenant.service.SysTenantDetailService; |
|||
|
|||
import lombok.Data; |
|||
import lombok.RequiredArgsConstructor; |
|||
|
|||
import org.springframework.stereotype.Component; |
|||
|
|||
/** |
|||
* @author siyang |
|||
* @date 2024/8/7 13:20 |
|||
* @description actor业务上下文 |
|||
*/ |
|||
@Data |
|||
@Component |
|||
@RequiredArgsConstructor |
|||
public class DefaultActorBizContext implements ActorBizContext { |
|||
/* --------------------- actor系统工具 ---------------------*/ |
|||
private ActorRef rootActor; |
|||
private ActorSystem actorSystem; |
|||
|
|||
/* ---------------------- 业务service ----------------------*/ |
|||
private final SysTenantDetailService sysTenantDetailService; |
|||
|
|||
@Override |
|||
public ActorRef getRootActor() { |
|||
return rootActor; |
|||
} |
|||
|
|||
@Override |
|||
public ActorSystem getActorSystem() { |
|||
return actorSystem; |
|||
} |
|||
} |
|||
@ -0,0 +1,100 @@ |
|||
package com.thing.common.actor.biz.lifecycle; |
|||
|
|||
import static com.thing.common.actor.biz.actors.root.ActorSysProperties.*; |
|||
|
|||
import com.thing.common.actor.ActorRef; |
|||
import com.thing.common.actor.ActorSystem; |
|||
import com.thing.common.actor.biz.actors.root.ActorSysProperties; |
|||
import com.thing.common.actor.biz.actors.root.RootActor; |
|||
import com.thing.common.actor.biz.actors.root.RootInitMsg; |
|||
import com.thing.common.actor.biz.base.DefaultActorBizContext; |
|||
import com.thing.common.actor.system.ActorSystemSettings; |
|||
import com.thing.common.actor.system.DefaultActorSystem; |
|||
import com.thing.common.util.thread.ThingExecutors; |
|||
import com.thing.common.util.thread.ThingThreadFactory; |
|||
|
|||
import jakarta.annotation.Nonnull; |
|||
import jakarta.annotation.PostConstruct; |
|||
import jakarta.annotation.PreDestroy; |
|||
|
|||
import lombok.RequiredArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
|
|||
import org.springframework.context.ApplicationListener; |
|||
import org.springframework.context.event.ContextRefreshedEvent; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
import java.util.concurrent.ExecutorService; |
|||
import java.util.concurrent.Executors; |
|||
|
|||
/** |
|||
* @author siyang |
|||
* @date 2024/8/2 11:28 |
|||
* @description 顶级actor服务 |
|||
*/ |
|||
@Slf4j |
|||
@Service |
|||
@RequiredArgsConstructor |
|||
public class ActorLifecycle implements ApplicationListener<ContextRefreshedEvent> { |
|||
|
|||
private final ActorSysProperties actorSysProperties; |
|||
private final DefaultActorBizContext actorBizContext; |
|||
|
|||
private ActorSystem system; |
|||
private ActorRef rootActor; |
|||
|
|||
@PostConstruct |
|||
public void initActorSystem() { |
|||
log.info("Initializing actor actorSystem."); |
|||
ActorSystemSettings settings = |
|||
new ActorSystemSettings( |
|||
actorSysProperties.getThroughput(), |
|||
actorSysProperties.getSchedulerPoolSize(), |
|||
actorSysProperties.getMaxActorInitAttempts()); |
|||
system = new DefaultActorSystem(settings); |
|||
|
|||
createDispatcher(ROOT_DISPATCHER, actorSysProperties.getRootDispatcherSize()); |
|||
createDispatcher(DEVICE_DISPATCHER, actorSysProperties.getDeviceDispatcherSize()); |
|||
createDispatcher(COMPANY_DISPATCHER, actorSysProperties.getTenantDispatcherSize()); |
|||
createDispatcher(NON_COMPANY_DISPATCHER, actorSysProperties.getNonTenantDispatcherSize()); |
|||
|
|||
rootActor = |
|||
system.createRootActor( |
|||
ROOT_DISPATCHER, new RootActor.ActorCreator(actorBizContext)); |
|||
|
|||
actorBizContext.setActorSystem(system); |
|||
actorBizContext.setRootActor(rootActor); |
|||
log.info("Actor actorSystem initialized."); |
|||
} |
|||
|
|||
@PreDestroy |
|||
public void stopActorSystem() { |
|||
if (system != null) { |
|||
log.info("Stopping actor system."); |
|||
system.stop(); |
|||
log.info("Actor system stopped."); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void onApplicationEvent(@Nonnull ContextRefreshedEvent event) { |
|||
log.info("Application ready. Sending application init message to actor system"); |
|||
rootActor.tellImportant(new RootInitMsg()); |
|||
} |
|||
|
|||
private void createDispatcher(String dispatcherName, int poolSize) { |
|||
system.createDispatcher(dispatcherName, initDispatcherExecutor(dispatcherName, poolSize)); |
|||
} |
|||
|
|||
private ExecutorService initDispatcherExecutor(String dispatcherName, int poolSize) { |
|||
if (poolSize == 0) { |
|||
int cores = Runtime.getRuntime().availableProcessors(); |
|||
poolSize = Math.max(1, cores / 2); |
|||
} |
|||
if (poolSize == 1) { |
|||
return Executors.newSingleThreadExecutor(ThingThreadFactory.forName(dispatcherName)); |
|||
} else { |
|||
return ThingExecutors.newWorkStealingPool(poolSize, dispatcherName); |
|||
} |
|||
} |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue