Closed catlvsuger closed 3 months ago
启动代码:public static void main(String[] args) throws UnknownHostException { String[] newArgs; //减少更新停机时间,切换时间1s左右,带来问题就是端口写死 int defaultPort = 8080; int backupPort = 9999; String ip = InetAddress.getLocalHost().getHostAddress();
if (!isPortInUse(defaultPort)) {
newArgs = setNewArgs(args, defaultPort);
ConfigurableApplicationContext application = SpringApplication.run(JeecgSystemApplication.class, newArgs);
Environment env = application.getEnvironment();
// String port = env.getProperty("server.port"); String path = oConvertUtils.getString(env.getProperty("server.servlet.context-path")); logInfo(defaultPort, path, ip); }else { log.info("Port " + defaultPort + " is in use. Trying backup port " + backupPort); newArgs = setNewArgs(args, backupPort); log.info("Start new instance asynchronously "); ConfigurableApplicationContext application = SpringApplication.run(JeecgSystemApplication.class, newArgs); Environment env = application.getEnvironment(); String path = oConvertUtils.getString(env.getProperty("server.servlet.context-path")); logInfo(backupPort, path, ip); try { waitForHealthyInstance(backupPort, path); log.info("New instance on port " + backupPort + " is healthy. Proceeding with traffic switch."); // Shut down old instance String command = String.format("lsof -i :%d | grep LISTEN | awk '{print $2}' | xargs kill -9", defaultPort); try { Runtime.getRuntime().exec(new String[] { "sh", "-c", command }).waitFor(); } catch (IOException | InterruptedException e) { log.error("Error shutting down old instance", e); } // Wait for default port to be free and restart new instance on default port waitForPortRelease(defaultPort); // WebSocket.closeAllSessions(); restartOnDefaultPort(application, defaultPort);
} catch (InterruptedException e) {
log.error("Error during new instance startup", e);
}
}
}
private static void logInfo(int backupPort, String path, String ip) {
log.info("\n----------------------------------------------------------\n\t" +
"Application Jeecg-Boot is running! Access URLs:\n\t" +
"Local: \t\thttp://localhost:" + backupPort + path + "/\n\t" +
"External: \thttp://" + ip + ":" + backupPort + path + "/\n\t" +
"Swagger文档: \thttp://" + ip + ":" + backupPort + path + "/doc.html\n" +
"----------------------------------------------------------");
}
private static String[] setNewArgs(String[] args, int port) {
String[] newArgs = new String[args.length + 1];
System.arraycopy(args, 0, newArgs, 0, args.length);
newArgs[newArgs.length - 1] = "--server.port=" + port;
return newArgs;
}
private static void waitForHealthyInstance(int port, String path) throws InterruptedException {
log.info("Waiting for new instance to become healthy...");
int retries = 10;
int delay = 5000; // 5 seconds
while (retries > 0) {
try {
String urlStr = "http://localhost:" + port + path + "/sys/api/health";
log.info("url:{}.", urlStr);
URL url = new URL(urlStr); // Modify the health endpoint as needed
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(3000);
connection.connect();
int responseCode = connection.getResponseCode();
if (responseCode == 200) {
log.info("New instance on port " + port + " is healthy.");
return;
}
} catch (IOException e) {
log.warn("Health check failed. Retrying...");
}
Thread.sleep(delay);
retries--;
}
throw new IllegalStateException("New instance did not become healthy within the expected time.");
}
private static void waitForPortRelease(int port) throws InterruptedException {
log.info("Waiting for port " + port + " to be released...");
while (isPortInUse(port)) {
Thread.sleep(100); // Check every 1 second
}
log.info("Port " + port + " is now free.");
}
private static void restartOnDefaultPort(ConfigurableApplicationContext application, int defaultPort) {
log.info("Restarting application on default port " + defaultPort);
// 获取当前运行的ServletWebServerApplicationContext
ServletWebServerApplicationContext servletAppContext = (ServletWebServerApplicationContext) application;
// 停止当前的Web服务器
WebServer webServer = servletAppContext.getWebServer();
if (webServer != null) {
webServer.stop();
WebSocket.closeAllSessions();
}
// 重新配置端口
ServletWebServerFactory webServerFactory = getWebServerFactory(servletAppContext);
((TomcatServletWebServerFactory) webServerFactory).setPort(defaultPort);
WebServer newWebServer = webServerFactory.getWebServer(invokeSelfInitialize((ServletWebServerApplicationContext) application));
newWebServer.start();
log.info("Restarting application on default port " + defaultPort + " is successful.");
}
private static ServletContextInitializer invokeSelfInitialize(ServletWebServerApplicationContext context) {
try {
Method method = ServletWebServerApplicationContext.class.getDeclaredMethod("getSelfInitializer");
method.setAccessible(true);
return (ServletContextInitializer) method.invoke(context);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
private static boolean isPortInUse(int port) {
try (ServerSocket serverSocket = new ServerSocket(port)) {
return false;
} catch (IOException e) {
return true;
}
}
private static ServletWebServerFactory getWebServerFactory(ConfigurableApplicationContext context) {
String[] beanNames = context.getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
return context.getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}
先正常启动个服务8080,再启动个新的服务9999,新服务启动成功前把旧服务停掉,新的服务切换回8080,此时webSocket连接失败,webSocket请求 会进入 DispatcherServlet 在 ResourceHttpRequestHandler 返回 Resource not found, DispatcherServlet:1131 - Completed 404 NOT_FOUND tomcat日志 Processing ErrorPage[errorCode=0, location=/error] 回到 shiroRealm 的 doGetAuthenticationInfo,抛出异常 ShiroConfig 已经加了webSocket过滤,正常应该不会进去Shiro,在第二次启动时重新加载 filterChainDefinitionMap 也没有用...
更新了还是一样,前端重连是成功的,进到 WebsocketFilter校验token通过后,filterChain.doFilter(servletRequest, servletResponse);走后续的 过滤器就出问题了
版本号:3.10
问题描述:通过切换端口不停机更新后端,比如默认端口8080,更新时判断8080是否被占用,被占用使用替代端口启动,启动后,再停止原8080服务,将新的服务切换到8080,websokect连接,会进入ShiroRealm验证,请求地址会变成htp://localhost:8080/jeecg-boot/error ,导致连接失败,第一次启动没问题,重启切换端口就会出现该问题
错误截图:
友情提示: