From 8876292193eeb2a3f9c2214f58f294e41a8e0a42 Mon Sep 17 00:00:00 2001 From: walker-sheng Date: Wed, 3 Mar 2021 16:56:32 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=BF=E7=9C=9F=E7=BB=93=E6=9E=84=E6=8A=BD?= =?UTF-8?q?=E8=B1=A1(=E6=93=8D=E4=BD=9C=E5=88=86=E5=8F=91=E8=B0=83?= =?UTF-8?q?=E6=95=B4=EF=BC=8C=E7=94=A8=E6=88=B7=E8=AE=A2=E9=98=85/?= =?UTF-8?q?=E5=8F=96=E6=B6=88=E8=AE=A2=E9=98=85=E9=80=BB=E8=BE=91=E6=A1=86?= =?UTF-8?q?=E6=9E=B6=E6=9E=84=E5=BB=BA=EF=BC=8C=E6=B7=BB=E5=8A=A0=E5=9B=BA?= =?UTF-8?q?=E5=AE=9A=E9=A2=91=E7=8E=87=E4=BB=BB=E5=8A=A1=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BusinessExceptionAssertEnum.java | 1 + .../joylink/rtss/simulation/Simulation.java | 155 +++++++++++++++--- .../SimulationCommonController.java | 12 +- .../rtss/simulation/SimulationDelayJob.java | 6 +- .../rtss/simulation/SimulationJob.java | 2 +- .../simulation/SimulationScheduledJob.java | 36 ++-- .../SimulationSubscribeMessageService.java | 8 + .../SimulationSubscribePathChecker.java | 6 - .../websocket/DefaultSubscribeManager.java | 31 ++-- .../SimulationOperationDispatcher.java | 22 ++- 10 files changed, 202 insertions(+), 77 deletions(-) create mode 100644 src/main/java/club/joylink/rtss/simulation/SimulationSubscribeMessageService.java delete mode 100644 src/main/java/club/joylink/rtss/simulation/SimulationSubscribePathChecker.java diff --git a/src/main/java/club/joylink/rtss/exception/BusinessExceptionAssertEnum.java b/src/main/java/club/joylink/rtss/exception/BusinessExceptionAssertEnum.java index cd0a1c57f..dd283b859 100644 --- a/src/main/java/club/joylink/rtss/exception/BusinessExceptionAssertEnum.java +++ b/src/main/java/club/joylink/rtss/exception/BusinessExceptionAssertEnum.java @@ -45,6 +45,7 @@ public enum BusinessExceptionAssertEnum implements BusinessExceptionAssert { // 仿真 SIMULATION_NOT_EXIST(30001, "simulation not exist"), + SIMULATION_OPERATION_FAILED(30002, "simulation operation failed"), // LOGIN_INFO_ERROR(40003, "login info error"), diff --git a/src/main/java/club/joylink/rtss/simulation/Simulation.java b/src/main/java/club/joylink/rtss/simulation/Simulation.java index 03bb67cc8..5cc490a45 100644 --- a/src/main/java/club/joylink/rtss/simulation/Simulation.java +++ b/src/main/java/club/joylink/rtss/simulation/Simulation.java @@ -1,12 +1,11 @@ package club.joylink.rtss.simulation; import lombok.extern.slf4j.Slf4j; +import org.springframework.util.PropertyPlaceholderHelper; import org.springframework.util.StringUtils; import java.time.LocalDateTime; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -22,6 +21,8 @@ public abstract class Simulation destinationMap = new HashMap<>(); + private static final PropertyPlaceholderHelper placeholderHelper = new PropertyPlaceholderHelper("{", "}"); + private List subscribeMessageServiceList; private SimulationMessagePublisher publisher; private Map simulationMemberMap = new ConcurrentHashMap<>(); @@ -174,12 +177,29 @@ public abstract class Simulation 8) { - throw new IllegalArgumentException("speed must small or equal than 8"); + if (speed > MAX_SPEED) { + throw new IllegalArgumentException(String.format("speed must small or equal than [%s]", MAX_SPEED)); } - if (this.speed == speed) { // 速度与当前相同,返回 - return; + if (this.speed != speed) { // 速度不同,重新更改并重新按新速度运行 + this.pause(); + this.speed = speed; + this.runAsSpeed(speed); + for (SimulationScheduledJob job : this.scheduledJobMap.values()) { + job.updateRunPeriod(speed); + } + this.start(); } - this.pause(); - this.speed = speed; - this.runAsSpeed(speed); - for (SimulationScheduledJob job : this.scheduledJobMap.values()) { - job.updateRunPeriod(speed); - } - this.start(); } public void runError(Throwable throwable) { @@ -300,9 +319,13 @@ public abstract class Simulation list) { + this.subscribeMessageServiceList = list; + } + + public void addSubscribeMessageService(SimulationSubscribeMessageService subscribeMessageService) { + if (this.subscribeMessageServiceList == null) { + this.subscribeMessageServiceList = new ArrayList<>(); + } + this.subscribeMessageServiceList.add(subscribeMessageService); + } + + public String handleDestination(String destinationPattern) { + String destination = this.destinationMap.get(destinationPattern); + if (destination != null) { + return destination; + } + Properties properties = new Properties(); + properties.put("id", this.id); + String dest = placeholderHelper.replacePlaceholders(destinationPattern, properties); + this.destinationMap.put(destinationPattern, dest); + return dest; + } + + public boolean isAcceptedSubscribe(String destination) { + if (this.subscribeMessageServiceList != null) { + for (SimulationSubscribeMessageService service : this.subscribeMessageServiceList) { + if (service.acceptedSubscribePath(destination)) { + return true; + } + } + } + return false; + } + public static String tryExtractSidFromDestination(String destination) { + Objects.requireNonNull(destination); + if (!destination.startsWith(MESSAGE_SUB_PREFIX.substring(0, MESSAGE_SUB_PREFIX.length() - 5))) { + return null; + } String[] patterns = StringUtils.tokenizeToStringArray(MESSAGE_SUB_PREFIX, PATH_SEPARATOR); String[] dests = StringUtils.tokenizeToStringArray(destination, PATH_SEPARATOR); for (int i = 0; i < patterns.length; i++) { @@ -356,4 +432,33 @@ public abstract class Simulation params) { - + return this.simulationOperationDispatcher.doDispatch(id, memberId, type, params); } @PutMapping("/{id}/member/{memberId}/playby/{userId}") diff --git a/src/main/java/club/joylink/rtss/simulation/SimulationDelayJob.java b/src/main/java/club/joylink/rtss/simulation/SimulationDelayJob.java index 726b11936..b5310cad8 100644 --- a/src/main/java/club/joylink/rtss/simulation/SimulationDelayJob.java +++ b/src/main/java/club/joylink/rtss/simulation/SimulationDelayJob.java @@ -42,11 +42,7 @@ public final class SimulationDelayJob implements Runnable { @Override public void run() { - try { - this.job.run(); - } catch (Throwable e) { - log.error("仿真延时任务执行异常", e); - } + this.job.run(); } public boolean isTimeToRun(LocalDateTime systemTime) { diff --git a/src/main/java/club/joylink/rtss/simulation/SimulationJob.java b/src/main/java/club/joylink/rtss/simulation/SimulationJob.java index b5baf179f..2daa0bae2 100644 --- a/src/main/java/club/joylink/rtss/simulation/SimulationJob.java +++ b/src/main/java/club/joylink/rtss/simulation/SimulationJob.java @@ -2,5 +2,5 @@ package club.joylink.rtss.simulation; public interface SimulationJob { - void run() throws InterruptedException; + void run(); } diff --git a/src/main/java/club/joylink/rtss/simulation/SimulationScheduledJob.java b/src/main/java/club/joylink/rtss/simulation/SimulationScheduledJob.java index c82accde5..7abf01993 100644 --- a/src/main/java/club/joylink/rtss/simulation/SimulationScheduledJob.java +++ b/src/main/java/club/joylink/rtss/simulation/SimulationScheduledJob.java @@ -16,14 +16,18 @@ public final class SimulationScheduledJob implements Runnable { /** * 原始频率,单位ms */ - private int rate; + private final int rate; + /** + * 是否固定频率 + */ + private final boolean fixed; /** * 实际运行间隔,单位ns */ long runPeriod; LocalDateTime nextRunTime; - public SimulationScheduledJob(Simulation simulation, String name, SimulationJob job, int rate) { + public SimulationScheduledJob(Simulation simulation, String name, SimulationJob job, int rate, boolean fixed) { if (null == job) { throw new IllegalArgumentException("job must not be null"); } @@ -34,28 +38,32 @@ public final class SimulationScheduledJob implements Runnable { this.name = name; this.job = job; this.rate = rate; + this.fixed = fixed; this.updateRunPeriod(1); } + public SimulationScheduledJob(Simulation simulation, String name, SimulationJob job, int rate) { + this(simulation, name, job, rate, false); + } + public static final int TIMEOUT = 10; @Override public void run() { - try { - long start = System.nanoTime(); - this.job.run(); - long used = System.nanoTime() - start; - if (used > TimeUnit.MILLISECONDS.toNanos(TIMEOUT)) { - log.warn(String.format("仿真任务[%s]执行耗时[%sns]超过[%sms],请检查并调优", - this.name, TimeUnit.NANOSECONDS.toMillis(used), TIMEOUT)); - } - } catch (Throwable e) { - simulation.runError(e); - log.error(String.format("仿真任务[%s]执行异常,仿真停止运行", this.name), e); + long start = System.nanoTime(); + this.job.run(); + long used = System.nanoTime() - start; + if (used > TimeUnit.MILLISECONDS.toNanos(TIMEOUT)) { + log.warn(String.format("仿真任务[%s]执行耗时[%sns]超过[%sms],请检查并调优", + this.name, TimeUnit.NANOSECONDS.toMillis(used), TIMEOUT)); } } public void updateRunPeriod(int speed) { - this.runPeriod = TimeUnit.MILLISECONDS.toNanos(this.rate) / speed; + if (this.fixed) { + this.runPeriod = TimeUnit.MILLISECONDS.toNanos(this.rate); + } else { + this.runPeriod = TimeUnit.MILLISECONDS.toNanos(this.rate) / speed; + } } public boolean isTimeToRun(LocalDateTime systemTime) { diff --git a/src/main/java/club/joylink/rtss/simulation/SimulationSubscribeMessageService.java b/src/main/java/club/joylink/rtss/simulation/SimulationSubscribeMessageService.java new file mode 100644 index 000000000..0f9c1d00e --- /dev/null +++ b/src/main/java/club/joylink/rtss/simulation/SimulationSubscribeMessageService.java @@ -0,0 +1,8 @@ +package club.joylink.rtss.simulation; + +public interface SimulationSubscribeMessageService { + + boolean acceptedSubscribePath(String destination); + + Object buildMessageOfSubscribe(String destination); +} diff --git a/src/main/java/club/joylink/rtss/simulation/SimulationSubscribePathChecker.java b/src/main/java/club/joylink/rtss/simulation/SimulationSubscribePathChecker.java deleted file mode 100644 index 5fc8cb4a6..000000000 --- a/src/main/java/club/joylink/rtss/simulation/SimulationSubscribePathChecker.java +++ /dev/null @@ -1,6 +0,0 @@ -package club.joylink.rtss.simulation; - -public interface SimulationSubscribePathChecker { - - boolean acceptedSubscribePath(String destination); -} diff --git a/src/main/java/club/joylink/rtss/simulation/messaging/websocket/DefaultSubscribeManager.java b/src/main/java/club/joylink/rtss/simulation/messaging/websocket/DefaultSubscribeManager.java index 8f20760d1..70c197ec2 100644 --- a/src/main/java/club/joylink/rtss/simulation/messaging/websocket/DefaultSubscribeManager.java +++ b/src/main/java/club/joylink/rtss/simulation/messaging/websocket/DefaultSubscribeManager.java @@ -50,24 +50,14 @@ public class DefaultSubscribeManager { } wsIdDestMap.put(this.buildWsSessionSubId(wsSessionId, subId), destination); String sid = Simulation.tryExtractSidFromDestination(destination); - SimulationUser simulationUser = this.tryGetSimulationUser(sid, user.getName()); - if (simulationUser == null) { + if (sid == null) { return; } - simulationUser.subscribe(wsSessionId, destination); - wsSidMap.put(wsSessionId, sid); - } - - private SimulationUser tryGetSimulationUser(String sid, String userId) { - if (sid == null) { - return null; - } Simulation simulation = this.simulationManager.queryById(sid); - if (simulation == null) { - return null; + if (simulation != null) { + simulation.handleUserSubscribe(user.getName(), wsSessionId, destination); } - SimulationUser simulationUser = simulation.querySimulationUserById(userId); - return simulationUser; + wsSidMap.put(wsSessionId, sid); } private String buildWsSessionSubId(String wsSessionId, String subId) { @@ -77,11 +67,16 @@ public class DefaultSubscribeManager { public void unsubscribe(WebsocketConfig.MyPrincipal user, String wsSessionId, String subId) { String key = this.buildWsSessionSubId(wsSessionId, subId); String destination = wsIdDestMap.remove(key); - String sid = Simulation.tryExtractSidFromDestination(destination); - SimulationUser simulationUser = this.tryGetSimulationUser(sid, user.getName()); - if (simulationUser == null) { + if (destination == null) { return; } - simulationUser.unsubscribe(wsSessionId, destination); + String sid = Simulation.tryExtractSidFromDestination(destination); + if (sid == null) { + return; + } + Simulation simulation = this.simulationManager.queryById(sid); + if (simulation != null) { + simulation.handleUserUnsubscribe(user.getName(), wsSessionId, destination); + } } } diff --git a/src/main/java/club/joylink/rtss/simulation/operation/SimulationOperationDispatcher.java b/src/main/java/club/joylink/rtss/simulation/operation/SimulationOperationDispatcher.java index 70786ec2e..55e1cccaf 100644 --- a/src/main/java/club/joylink/rtss/simulation/operation/SimulationOperationDispatcher.java +++ b/src/main/java/club/joylink/rtss/simulation/operation/SimulationOperationDispatcher.java @@ -1,14 +1,18 @@ package club.joylink.rtss.simulation.operation; +import club.joylink.rtss.exception.BusinessExceptionAssertEnum; import club.joylink.rtss.simulation.Simulation; import club.joylink.rtss.simulation.SimulationManager; import club.joylink.rtss.simulation.SimulationMember; import club.joylink.rtss.simulation.operation.converter.ConvertUtil; +import club.joylink.rtss.util.JsonUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.lang.reflect.Parameter; +import java.lang.reflect.ParameterizedType; +import java.util.List; import java.util.Map; @Slf4j @@ -29,8 +33,8 @@ public class SimulationOperationDispatcher { return handlerMethod.getMethod().invoke(handlerMethod.getBean(), args); } catch (Exception e) { log.error(String.format("仿真[%s]操作[%s]分发异常", id, operation), e); + throw BusinessExceptionAssertEnum.SIMULATION_OPERATION_FAILED.exception(e); } - return null; } private Object[] buildParameters(Simulation simulation, OperationHandlerMethod handlerMethod, Map params) { @@ -43,10 +47,20 @@ public class SimulationOperationDispatcher { continue; } Object o = params.get(parameter.getName()); + // 简单对象处理 args[i] = ConvertUtil.convert(o, parameter.getType()); - // 复杂对象处理,需要再加 -// if (args[i] == null && o != null) { -// } + // 复杂对象处理 + if (args[i] == null && o != null) { + if (List.class.isAssignableFrom(parameter.getType())) { + ParameterizedType parameterizedType = (ParameterizedType) parameter.getParameterizedType(); + Class actualClass = (Class) parameterizedType.getActualTypeArguments()[0]; + args[i] = JsonUtils.read(JsonUtils.writeValueAsString(params.get(parameter.getName())), + JsonUtils.getCollectionType(parameter.getType(), actualClass)); + } else { + args[i] = JsonUtils.read(JsonUtils.writeValueAsString(params.get(parameter.getName())), + parameter.getType()); + } + } } return args; }