feat: add retry configuration and optimization running lifecycle
This commit is contained in:
parent
d9d3bb2a5b
commit
e5788822ed
@ -8,13 +8,13 @@ package cn.wzpmc.plugins.configuration;
|
|||||||
* @since 2024/7/31 上午3:42
|
* @since 2024/7/31 上午3:42
|
||||||
*/
|
*/
|
||||||
public interface IConfiguration {
|
public interface IConfiguration {
|
||||||
/**
|
|
||||||
* @return WebSocket连接URL
|
|
||||||
* @author wzp
|
|
||||||
* @since 2024/7/31 上午3:48 v0.0.1-dev
|
|
||||||
*/
|
|
||||||
String getWebsocket();
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wzp
|
||||||
|
* @since 2025/3/26 17:29 v1.0.7
|
||||||
|
* @return 网络相关配置
|
||||||
|
*/
|
||||||
|
INetworkConfiguration getNetwork();
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author wzp
|
* @author wzp
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
package cn.wzpmc.plugins.configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wzp
|
||||||
|
* @since 2025/3/26 17:26
|
||||||
|
* @version 1.0.7
|
||||||
|
*/
|
||||||
|
public interface INetworkConfiguration {
|
||||||
|
/**
|
||||||
|
* @return WebSocket连接URL
|
||||||
|
* @author wzp
|
||||||
|
* @since 2025/3/26 17:26 v1.0.7
|
||||||
|
*/
|
||||||
|
String getWebsocket();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wzp
|
||||||
|
* @since 2025/3/26 17:27 v1.0.7
|
||||||
|
* @return 是否启用连接重试
|
||||||
|
*/
|
||||||
|
Boolean isRetry();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wzp
|
||||||
|
* @since 2025/3/26 17:27 v1.0.7
|
||||||
|
* @return 重试最大次数(-1为无限)
|
||||||
|
*/
|
||||||
|
Integer getMaxRetryCount();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wzp
|
||||||
|
* @since 2025/3/26 17:28 v1.0.7
|
||||||
|
* @return 获取重试间隔(单位毫秒)
|
||||||
|
*/
|
||||||
|
Long getRetryInterval();
|
||||||
|
|
||||||
|
}
|
@ -18,6 +18,7 @@ import cn.wzpmc.utils.JsonUtils;
|
|||||||
import cn.wzpmc.utils.ReflectionUtils;
|
import cn.wzpmc.utils.ReflectionUtils;
|
||||||
import cn.wzpmc.utils.TemplateFileUtils;
|
import cn.wzpmc.utils.TemplateFileUtils;
|
||||||
import cn.wzpmc.utils.YamlUtils;
|
import cn.wzpmc.utils.YamlUtils;
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
|
||||||
@ -52,6 +53,7 @@ public class Main {
|
|||||||
}
|
}
|
||||||
log.debug("读取配置文件 {}", configurationFile.getAbsolutePath());
|
log.debug("读取配置文件 {}", configurationFile.getAbsolutePath());
|
||||||
Configuration configuration = YamlUtils.readYamlFile(configurationFile, Configuration.class);
|
Configuration configuration = YamlUtils.readYamlFile(configurationFile, Configuration.class);
|
||||||
|
JSONObject fullConfiguration = YamlUtils.readYamlFile(configurationFile, JSONObject.class);
|
||||||
Configuration defaultConfiguration = TemplateFileUtils.readDefaultConfig(classLoader, DEFAULT_CONFIGURATION_FILE_PATH, Configuration.class);
|
Configuration defaultConfiguration = TemplateFileUtils.readDefaultConfig(classLoader, DEFAULT_CONFIGURATION_FILE_PATH, Configuration.class);
|
||||||
// 配置文件自动更新 start
|
// 配置文件自动更新 start
|
||||||
boolean isChanged = false;
|
boolean isChanged = false;
|
||||||
@ -78,6 +80,12 @@ public class Main {
|
|||||||
configuration.setCommandPrefix(defaultConfiguration.getCommandPrefix());
|
configuration.setCommandPrefix(defaultConfiguration.getCommandPrefix());
|
||||||
isChanged = true;
|
isChanged = true;
|
||||||
}
|
}
|
||||||
|
if (configuration.getNetwork() == null) {
|
||||||
|
configuration.setNetwork(defaultConfiguration.getNetwork());
|
||||||
|
configuration.getNetwork().setWebsocket(fullConfiguration.getString("websocket"));
|
||||||
|
isChanged = true;
|
||||||
|
}
|
||||||
|
// end
|
||||||
if (isChanged) {
|
if (isChanged) {
|
||||||
log.warn("已自动升级配置文件,请检查config.yml是否有错误的地方,有则请修改");
|
log.warn("已自动升级配置文件,请检查config.yml是否有错误的地方,有则请修改");
|
||||||
YamlUtils.writeYamlFile(configurationFile, configuration);
|
YamlUtils.writeYamlFile(configurationFile, configuration);
|
||||||
@ -95,7 +103,7 @@ public class Main {
|
|||||||
public static URI getUriFromConfiguration(Configuration configuration) {
|
public static URI getUriFromConfiguration(Configuration configuration) {
|
||||||
URI uri;
|
URI uri;
|
||||||
try {
|
try {
|
||||||
uri = new URI(configuration.getWebsocket());
|
uri = new URI(configuration.getNetwork().getWebsocket());
|
||||||
} catch (URISyntaxException e) {
|
} catch (URISyntaxException e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -134,8 +142,8 @@ public class Main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static WebSocketConnectionHandler createConnection(MyBot myBot, URI uri) {
|
public static WebSocketConnectionHandler createConnection(MyBot myBot, URI uri) {
|
||||||
WebSocketConnectionHandler webSocketConnectionHandler = new WebSocketConnectionHandler(myBot);
|
WebSocketConnectionHandler webSocketConnectionHandler = new WebSocketConnectionHandler(myBot, uri);
|
||||||
webSocketConnectionHandler.connect(uri);
|
webSocketConnectionHandler.connect();
|
||||||
return webSocketConnectionHandler;
|
return webSocketConnectionHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,12 +12,13 @@ import lombok.Data;
|
|||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public class Configuration implements IConfiguration {
|
public class Configuration implements IConfiguration {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WebSocket连接URL
|
* 网络相关配置
|
||||||
*
|
* @since 2025/3/26 17:32 v1.0.7
|
||||||
* @since 2024/7/30 下午11:48 v0.0.1-dev
|
|
||||||
*/
|
*/
|
||||||
private String websocket;
|
private NetworkConfiguration network;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 命令前缀
|
* 命令前缀
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
package cn.wzpmc.configuration;
|
||||||
|
|
||||||
|
import cn.wzpmc.plugins.configuration.INetworkConfiguration;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wzp
|
||||||
|
* @since 2025/3/26 17:29
|
||||||
|
* @version 1.0.7
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class NetworkConfiguration implements INetworkConfiguration {
|
||||||
|
/**
|
||||||
|
* WebSocket连接URL
|
||||||
|
* @since 2025/3/26 17:31 v1.0.7
|
||||||
|
*/
|
||||||
|
private String websocket;
|
||||||
|
/**
|
||||||
|
* 是否启用连接重试
|
||||||
|
* @since 2025/3/26 17:31 v1.0.7
|
||||||
|
*/
|
||||||
|
private Boolean retry;
|
||||||
|
/**
|
||||||
|
* 重试最大次数(-1为无限)
|
||||||
|
* @since 2025/3/26 17:31 v1.0.7
|
||||||
|
*/
|
||||||
|
private Integer maxRetryCount;
|
||||||
|
/**
|
||||||
|
* 获取重试间隔(单位毫秒)
|
||||||
|
* @since 2025/3/26 17:31 v1.0.7
|
||||||
|
*/
|
||||||
|
private Long retryInterval;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean isRetry() {
|
||||||
|
return this.retry;
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,10 @@
|
|||||||
package cn.wzpmc.console;
|
package cn.wzpmc.console;
|
||||||
|
|
||||||
|
import cn.wzpmc.entities.api.ApiResponseRequired;
|
||||||
import cn.wzpmc.entities.user.bot.MyBot;
|
import cn.wzpmc.entities.user.bot.MyBot;
|
||||||
import cn.wzpmc.network.WebSocketConnectionHandler;
|
import cn.wzpmc.network.WebSocketConnectionHandler;
|
||||||
import cn.wzpmc.plugins.CommandManager;
|
import cn.wzpmc.plugins.CommandManager;
|
||||||
|
import cn.wzpmc.utils.json.action.ActionReader;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
@ -47,12 +49,16 @@ public class MyBotConsole extends SimpleTerminalConsole {
|
|||||||
@Override
|
@Override
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
this.webSocketConnectionHandler.kill();
|
this.webSocketConnectionHandler.kill();
|
||||||
|
for (ApiResponseRequired<?, ?> value : ActionReader.tasks.values()) {
|
||||||
|
value.getFuture().complete(null);
|
||||||
|
}
|
||||||
running = false;
|
running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start() {
|
public void start() {
|
||||||
this.bot.setConsole(this);
|
this.bot.setConsole(this);
|
||||||
|
if (this.bot.isShutdown()) return;
|
||||||
super.start();
|
super.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -50,6 +50,8 @@ public class MyBot extends IBot {
|
|||||||
private final Configuration configuration;
|
private final Configuration configuration;
|
||||||
private final CommandManager commandManager = new CommandManager(this);
|
private final CommandManager commandManager = new CommandManager(this);
|
||||||
private final PluginManager pluginManager = new PluginManager();
|
private final PluginManager pluginManager = new PluginManager();
|
||||||
|
@Setter
|
||||||
|
private boolean shutdown = false;
|
||||||
private final IncreasbleHashMap<Class<? extends Event>, EventHandlerMethod> events = new IncreasbleHashMap<>();
|
private final IncreasbleHashMap<Class<? extends Event>, EventHandlerMethod> events = new IncreasbleHashMap<>();
|
||||||
@Getter
|
@Getter
|
||||||
private final Ops ops;
|
private final Ops ops;
|
||||||
|
@ -37,7 +37,6 @@ public class HandshakePacketHandler extends SimpleChannelInboundHandler<FullHttp
|
|||||||
handshaker.finishHandshake(channelHandlerContext.channel(), fullHttpResponse);
|
handshaker.finishHandshake(channelHandlerContext.channel(), fullHttpResponse);
|
||||||
this.handshakeFuture.complete(true);
|
this.handshakeFuture.complete(true);
|
||||||
log.debug("握手成功");
|
log.debug("握手成功");
|
||||||
log.info("连接服务器成功!");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,8 @@ import java.util.concurrent.Executors;
|
|||||||
public class PacketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
|
public class PacketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
|
||||||
private final IBot bot;
|
private final IBot bot;
|
||||||
private final ExecutorService threadPool = Executors.newFixedThreadPool(4);
|
private final ExecutorService threadPool = Executors.newFixedThreadPool(4);
|
||||||
|
private final Runnable retryFunction;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame webSocketFrame) {
|
protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame webSocketFrame) {
|
||||||
@ -105,4 +107,10 @@ public class PacketHandler extends SimpleChannelInboundHandler<TextWebSocketFram
|
|||||||
public <REQUEST, RESPONSE> void registerResponse(UUID echo, CompletableFuture<ActionResponse<RESPONSE>> responsePromise, Action<REQUEST, RESPONSE> request) {
|
public <REQUEST, RESPONSE> void registerResponse(UUID echo, CompletableFuture<ActionResponse<RESPONSE>> responsePromise, Action<REQUEST, RESPONSE> request) {
|
||||||
ActionReader.tasks.put(echo, new ApiResponseRequired<>(responsePromise, request));
|
ActionReader.tasks.put(echo, new ApiResponseRequired<>(responsePromise, request));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||||
|
log.info("与服务器断开连接!");
|
||||||
|
retryFunction.run();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,11 @@ package cn.wzpmc.network;
|
|||||||
|
|
||||||
import cn.wzpmc.api.Action;
|
import cn.wzpmc.api.Action;
|
||||||
import cn.wzpmc.api.ActionResponse;
|
import cn.wzpmc.api.ActionResponse;
|
||||||
import cn.wzpmc.user.IBot;
|
import cn.wzpmc.console.MyBotConsole;
|
||||||
|
import cn.wzpmc.entities.api.ApiResponseRequired;
|
||||||
|
import cn.wzpmc.entities.user.bot.MyBot;
|
||||||
|
import cn.wzpmc.plugins.configuration.INetworkConfiguration;
|
||||||
|
import cn.wzpmc.utils.json.action.ActionReader;
|
||||||
import com.alibaba.fastjson2.JSON;
|
import com.alibaba.fastjson2.JSON;
|
||||||
import io.netty.bootstrap.Bootstrap;
|
import io.netty.bootstrap.Bootstrap;
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFuture;
|
||||||
@ -32,26 +36,70 @@ import java.util.concurrent.ExecutionException;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class WebSocketConnectionHandler {
|
public class WebSocketConnectionHandler {
|
||||||
private final EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
|
private final EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
|
||||||
private final IBot bot;
|
private final MyBot bot;
|
||||||
|
/**
|
||||||
|
* websocket连接地址
|
||||||
|
* @since 2025/3/26 17:56 v1.0.7
|
||||||
|
*/
|
||||||
|
private final URI websocket;
|
||||||
private ChannelFuture channelFuture;
|
private ChannelFuture channelFuture;
|
||||||
private PacketHandler packetHandler;
|
private PacketHandler packetHandler;
|
||||||
private HandshakePacketHandler handshakePacketHandler;
|
private HandshakePacketHandler handshakePacketHandler;
|
||||||
|
private int currentRetryCount = 0;
|
||||||
|
|
||||||
|
|
||||||
|
private void tryReconnect() {
|
||||||
|
INetworkConfiguration network = bot.getConfiguration().getNetwork();
|
||||||
|
if (!network.isRetry()) {
|
||||||
|
this.quit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Integer maxRetryCount = network.getMaxRetryCount();
|
||||||
|
if (maxRetryCount != 0 && currentRetryCount >= maxRetryCount) {
|
||||||
|
this.quit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.currentRetryCount++;
|
||||||
|
log.info("尝试重连第{}次", currentRetryCount);
|
||||||
|
this.connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void quit() {
|
||||||
|
for (ApiResponseRequired<?, ?> value : ActionReader.tasks.values()) {
|
||||||
|
value.getFuture().obtrudeException(new InterruptedException());
|
||||||
|
}
|
||||||
|
this.handshakePacketHandler.getHandshakeFuture().obtrudeException(new InterruptedException());
|
||||||
|
MyBotConsole console = bot.getConsole();
|
||||||
|
bot.setShutdown(true);
|
||||||
|
if (console == null) {
|
||||||
|
this.eventLoopGroup.shutdownGracefully();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 建立连接
|
* 建立连接
|
||||||
*
|
|
||||||
* @param websocket websocket连接地址
|
|
||||||
* @author wzp
|
* @author wzp
|
||||||
* @since 2024/7/30 下午11:55 v0.0.1-dev
|
* @since 2024/7/30 下午11:55 v0.0.1-dev
|
||||||
*/
|
*/
|
||||||
public void connect(URI websocket) {
|
public void connect() {
|
||||||
log.info("正在连接websocket");
|
log.info("正在连接websocket");
|
||||||
Bootstrap bootstrap = new Bootstrap();
|
Bootstrap bootstrap = new Bootstrap();
|
||||||
WebSocketClientHandshaker clientHandshaker = WebSocketClientHandshakerFactory.newHandshaker(websocket, WebSocketVersion.V13, null, false, new DefaultHttpHeaders(), 65536 * 100);
|
WebSocketClientHandshaker clientHandshaker = WebSocketClientHandshakerFactory.newHandshaker(websocket, WebSocketVersion.V13, null, false, new DefaultHttpHeaders(), 65536 * 100);
|
||||||
this.handshakePacketHandler = new HandshakePacketHandler(clientHandshaker);
|
this.handshakePacketHandler = new HandshakePacketHandler(clientHandshaker);
|
||||||
this.packetHandler = new PacketHandler(this.bot);
|
this.packetHandler = new PacketHandler(this.bot, this::tryReconnect);
|
||||||
bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class).handler(new WebSocketChannelInitializer(this.packetHandler, this.handshakePacketHandler));
|
bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class).handler(new WebSocketChannelInitializer(this.packetHandler, this.handshakePacketHandler));
|
||||||
this.channelFuture = bootstrap.connect(websocket.getHost(), websocket.getPort());
|
this.channelFuture = bootstrap.connect(websocket.getHost(), websocket.getPort());
|
||||||
|
this.channelFuture.addListener(future -> {
|
||||||
|
if (!future.isSuccess()) {
|
||||||
|
log.info("连接失败!");
|
||||||
|
this.tryReconnect();
|
||||||
|
} else {
|
||||||
|
log.info("连接成功!");
|
||||||
|
this.currentRetryCount = 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -81,6 +129,7 @@ public class WebSocketConnectionHandler {
|
|||||||
this.handshakePacketHandler.getHandshakeFuture().get();
|
this.handshakePacketHandler.getHandshakeFuture().get();
|
||||||
} catch (ExecutionException e) {
|
} catch (ExecutionException e) {
|
||||||
log.error(e);
|
log.error(e);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
CompletableFuture<ActionResponse<RESPONSE>> responsePromise = new CompletableFuture<>();
|
CompletableFuture<ActionResponse<RESPONSE>> responsePromise = new CompletableFuture<>();
|
||||||
packetHandler.registerResponse(request.getEcho(), responsePromise, request);
|
packetHandler.registerResponse(request.getEcho(), responsePromise, request);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package cn.wzpmc.utils;
|
package cn.wzpmc.utils;
|
||||||
|
|
||||||
import cn.wzpmc.configuration.Configuration;
|
import cn.wzpmc.configuration.Configuration;
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
import org.yaml.snakeyaml.DumperOptions;
|
import org.yaml.snakeyaml.DumperOptions;
|
||||||
import org.yaml.snakeyaml.Yaml;
|
import org.yaml.snakeyaml.Yaml;
|
||||||
@ -30,7 +31,8 @@ public class YamlUtils {
|
|||||||
*/
|
*/
|
||||||
public static <T> T readYamlStream(InputStream is, Class<T> clazz) {
|
public static <T> T readYamlStream(InputStream is, Class<T> clazz) {
|
||||||
Yaml yaml = new Yaml();
|
Yaml yaml = new Yaml();
|
||||||
return yaml.loadAs(is, clazz);
|
JSONObject json = yaml.loadAs(is, JSONObject.class);
|
||||||
|
return json.to(clazz);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
websocket: "<Your WebSocket connection link, e.g: ws://127.0.0.1:3001/>"
|
network:
|
||||||
|
websocket: "<Your WebSocket connection link, e.g: ws://127.0.0.1:3001/>"
|
||||||
|
retry: true
|
||||||
|
maxRetryCount: 3
|
||||||
|
retryInterval: 10000
|
||||||
commandPrefix: "/"
|
commandPrefix: "/"
|
||||||
authorization:
|
authorization:
|
||||||
enable: false
|
enable: false
|
||||||
|
Loading…
x
Reference in New Issue
Block a user