SpringBoot - 动态端口切换黑魔法
文章目录
- 关键技术点
- 核心原理
- Code

关键技术点
利用 Spring Boot 内嵌 Servlet 容器 和 动态端口切换 的方式实现平滑更新的方案,关键技术点如下:
- Servlet 容器重新绑定端口:Spring Boot 使用
ServletWebServerFactory动态设置新端口。 - 零停机切换:通过先启动备用服务、释放主端口,再切换新服务到主端口,实现服务的无缝切换。
- 端口检测和进程终止:使用
ServerSocket和系统命令来检测和操作端口。
这种设计允许服务在不完全停止的情况下切换到更新的版本,从而极大地缩短了不可用时间,实现了接近于零停机的效果。
核心原理
-
内嵌 Tomcat 容器动态启动:
- 使用
TomcatServletWebServerFactory实现容器的动态创建和启动。 - 动态绑定
DispatcherServlet通过ServletContextInitializer集合完成 Servlet 注册。
- 使用
-
端口检查和动态切换:
- 通过
ServerSocket判断端口是否占用。 - 如果占用,则先用备用端口启动新服务,再通过关闭老服务释放主端口,最后切换新服务到主端口。
- 通过
-
运行时自动处理:
- 利用
Runtime.exec执行系统命令,释放端口并终止旧进程。 - 在极短时间内完成新旧服务切换,避免长时间的停机。
- 利用
Code
package com.artisan;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServer;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.boot.web.servlet.ServletContextInitializerBeans;
import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.ConfigurableApplicationContext;import java.io.IOException;
import java.net.ServerSocket;
import java.util.Collections;@SpringBootApplication()
public class BootMainApplication {public static void main(String[] args) {// 默认端口设置int defaultPort = 8080;// 备选端口设置int alternativePort = 9090;// 检查默认端口是否已被占用boolean isPortOccupied = isPortInUse(defaultPort);// 动态端口分配int portToUse = isPortOccupied ? alternativePort : defaultPort;// 创建Spring Boot应用实例SpringApplication app = new SpringApplication(WebMainApplication2.class);// 设置端口配置app.setDefaultProperties(Collections.singletonMap("server.port", portToUse));// 运行应用并获取上下文ConfigurableApplicationContext context = app.run(args);// 如果默认端口被占用,则尝试切换回默认端口if (isPortOccupied) {switchToDefaultPort(context, defaultPort, portToUse);}}/*** 切换到默认端口** 当默认端口被其他进程占用时,此方法尝试释放该端口,并启动一个新的Web服务器实例绑定到默认端口* 同时,它会停止当前的Web服务器实例** @param context 当前应用上下文,用于访问Web服务器工厂和停止当前Web服务器* @param defaultPort 默认端口号,希望切换到的目标端口* @param currentPort 当前Web服务器正在使用的端口号*/private static void switchToDefaultPort(ConfigurableApplicationContext context, int defaultPort, int currentPort) {try {// 释放默认端口terminateProcessUsingPort(defaultPort);// 等待端口释放while (isPortInUse(defaultPort)) {Thread.sleep(100);}// 启动新容器绑定默认端口ServletWebServerFactory webServerFactory = getWebServerFactory(context);((TomcatServletWebServerFactory) webServerFactory).setPort(defaultPort);WebServer newServer = webServerFactory.getWebServer(getServletContextInitializers(context));newServer.start();// 停止当前容器((ServletWebServerApplicationContext) context).getWebServer().stop();} catch (Exception e) {e.printStackTrace();}}/*** 检查指定的端口是否正在使用** @param port 要检查的端口号* @return 如果端口正在使用,则返回true;否则返回false*/private static boolean isPortInUse(int port) {try (ServerSocket serverSocket = new ServerSocket(port)) {// 如果能够成功创建ServerSocket实例,说明端口可用,返回falsereturn false;} catch (IOException e) {// 如果创建ServerSocket实例时抛出IOException,说明端口已被占用,返回truereturn true;}}/*** 终止使用指定端口的进程** @param port 需要释放的端口号* @throws IOException 如果执行命令发生错误* @throws InterruptedException 如果线程被中断*/private static void terminateProcessUsingPort(int port) throws IOException, InterruptedException {// 构建终止使用指定端口的进程的命令String command = String.format("lsof -i :%d | grep LISTEN | awk '{print $2}' | xargs kill -9", port);// 执行命令并等待命令执行完成Runtime.getRuntime().exec(new String[]{"sh", "-c", command}).waitFor();}/*** 获取ServletContextInitializer实例* 该方法用于将Spring应用上下文中的所有ServletContextInitializerBeans实例* 转换为ServletContextInitializer接口的实现,以便在应用启动时初始化ServletContext** @param context Spring的应用上下文,用于获取BeanFactory* @return 返回一个实现了ServletContextInitializer接口的实例*/private static ServletContextInitializer getServletContextInitializers(ConfigurableApplicationContext context) {// 使用ApplicationContext中的BeanFactory创建ServletContextInitializerBeans实例// 这里将ServletContextInitializerBeans作为ServletContextInitializer的实现类返回// ServletContextInitializerBeans将会负责收集应用上下文中所有ServletContextInitializer的实现// 并在应用启动时依次调用它们的onStartup方法来初始化ServletContextreturn (ServletContextInitializer) new ServletContextInitializerBeans(context.getBeanFactory());}/*** 获取Servlet Web服务器工厂** @param context 可配置的应用上下文,用于获取Bean工厂* @return ServletWebServerFactory实例,用于配置和创建Web服务器*/private static ServletWebServerFactory getWebServerFactory(ConfigurableApplicationContext context) {// 从应用上下文中获取Bean工厂,并从中获取ServletWebServerFactory实例return context.getBeanFactory().getBean(ServletWebServerFactory.class);}
}
测试
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController()
@RequestMapping("port/")
public class TestPortController {@GetMapping("test")public String test() {return "artisan-old";}
}
启动后,访问 http://localhost:8080/port/test
修改TestPortController 的返回值, 打个jar包, 启动新的jar包,
重新访问 http://localhost:8080/port/test ,观察返回结果是否是修改后的返回值
参考:https://mp.weixin.qq.com/s/_rt1NP_LPfzatb0EYXry9Q

相关文章:
SpringBoot - 动态端口切换黑魔法
文章目录 关键技术点核心原理Code 关键技术点 利用 Spring Boot 内嵌 Servlet 容器 和 动态端口切换 的方式实现平滑更新的方案,关键技术点如下: Servlet 容器重新绑定端口:Spring Boot 使用 ServletWebServerFactory 动态设置新端口。零停…...
Java爬虫技术:挖掘淘宝数据的利器
在当今大数据时代,网络爬虫技术已经成为获取网络数据的重要手段。Java作为一种强大且灵活的编程语言,非常适合开发复杂的网络爬虫系统。本文将详细介绍Java爬虫能够爬取的淘宝数据类型,并提供具体的代码示例,帮助您快速入门并掌握…...
Chromium for Android 浏览器的编译和安装
Chromium for Android 浏览器的编译和安装 Chromium for Android 浏览器的编译和安装环境要求和配置Chromium for Android源码下载安装 depot_tools获取代码转换现有的Linux检出安装额外的构建依赖运行钩子 Chromium for Android源码编译设置编译环境 编译 ChromiumChromium fo…...
实景视频与模型叠加融合?
[视频GIS系列]无人机视频与与实景模型进行实时融合_无人机视频融合-CSDN博客文章浏览阅读1.5k次,点赞28次,收藏14次。将无人机视频与实景模型进行实时融合是一个涉及多个技术领域的复杂过程,主要包括无人机视频采集、实景模型构建、视频与模型…...
Scala的隐式类
package hfd //隐式类 //任务:给之前的BaseUser添加新的功能,但是不要直接去改代码 //思路:把BaseUser通过隐式转换,改成一个新类型,而这个新类型中有这新的方法 //implicit class一个隐式转换函数类 //作用࿱…...
常见软件设计模式介绍:三层架构、MVC、SSM、EDD、DDD
三层架构(View Service Dao) 三层架构是指:视图层 view(表现层),服务层 service(业务逻辑层),持久层 Dao(数据访问层) 表现层:直接跟前…...
Springboot技术栈常见问题及搭建步骤
一. SpringBoot介绍 1.1. 引言 为了使用SSM框架去开发, 准备SSM框架的模板配置 为了使Spring整合第三方框架, 单独的去编写xml文件 导致SSM项目后期xml文件特别多, 维护xml文件的成本是很高的 SSM工程部署也是很麻烦, 依赖第三方的容器 SSM开发方式很是笨重 1.2 SpringBoot …...
session 共享服务器
1.安装 kryo-3.0.3.jar asm-5.2.jar objenesis-2.6.jar reflectasm-1.11.9.jar minlog-1.3.1.jar kryo-serializers-0.45.jar msm-kryo-serializer-2.3.2.jar memcached-session-manager-tc9-2.3.2.jar spymemcached-2.12.3.jar memcached-session-manager-2.3.2.jar …...
vue2:v-for实现的el-radio-group选中时显示角标,并自定义选中按钮的字体颜色和背景色
项目中需要实现一组预定义查询,每一个查询按钮在选中时右上角显示一个角标,展示当前查询返回的数据条目。 1、text-color="#3785FF" fill="#E6EAF1" 处理选中时的字体颜色和背景色,如上图,分别为蓝色和浅灰色。 2、badge中:value="selectedRadio…...
【Linux】-学习笔记10
第八章、Linux下的火墙管理及优化 1.什么是防火墙 从功能角度来讲 防火墙是位于内部网和外部网之间的屏障,它按照系统管理员预先定义好的规则来控制数据包的进出 从功能实现角度来讲 火墙是系统内核上的一个模块netfilter(数据包过滤机制) …...
鸿蒙NEXT开发案例:九宫格随机
【引言】 在鸿蒙NEXT开发中,九宫格抽奖是一个常见且有趣的应用场景。通过九宫格抽奖,用户可以随机获得不同奖品,增加互动性和趣味性。本文将介绍如何使用鸿蒙开发框架实现九宫格抽奖功能,并通过代码解析展示实现细节。 【环境准…...
深度解析:RTC电路上的32.768KHz时钟的频偏及测试
1、什么是RTC RTC是Real-Time Clock(实时时钟)的缩写,通常在电子产品中,是用时钟电路(外部采用时钟芯片,比如AiP8563)或时钟模块(SOC内部包含了时钟模块,只需要外接32.768KHz晶振)来…...
Scala的泛型
需求:定义一个名为getMiddleEle 的方法用它来获取当前的列表的中间位置的值中间位置的下标 长度/2目标:getMiddleEle(List(1,2,3,4,5)) > 5/2 2 > 下标为2的元素是:3 getMiddleEle(List(1,2,3,4)) > 4/2 2 > 下标为2的元素是:3格式如下: 定义一个函数的格式:def…...
OpenGL ES详解——glUniform1i方法是否能用于设置纹理单元
glUniform1i 方法确实可以用于设置纹理单元(texture unit)。在OpenGL中,纹理单元是图形硬件的一部分,它允许你同时绑定多个纹理,并在着色器程序中通过uniform变量来选择使用哪个纹理。 通常,纹理单元通过整…...
探索 Janus-1.3B:一个统一的 Any-to-Any 多模态理解与生成模型
随着多模态技术的不断发展,越来越多的模型被提出以解决跨文本与图像等多种数据类型的任务。Janus-1.3B 是由 DeepSeek 推出的一个革命性的模型,它通过解耦视觉编码并采用统一的 Transformer 架构,带来了一个高度灵活的 any-to-any 多模态框架…...
论文信息搜集
系列博客目录 文章目录 系列博客目录1.秩典型相关分析及其在视觉搜索重排序中的应用《Rank canonical correlation analysis and its application in visual search reranking》2.利用边信息的规范秩估计在多维谐波恢复中的应用《Canonical Rank Estimation Using Side Informa…...
实操给自助触摸一体机接入大模型语音交互
本文以CSK6 大模型开发板串口触摸屏为例,实操讲解触摸一体机怎样快速增加大模型语音交互功能,使用户能够通过语音在一体机上查询信息、获取智能回答及实现更多互动功能等。 在本文方案中通过CSK6大模型语音开发板采集用户语音,将语音数据传输…...
图表的放大和刷新功能
正常图表渲染显示: // 漏斗ading动画 let myChartone; // 获取配置项 let optionone; // 获取漏斗的数据 let order; let pay_order; let pay_order_num; let pay_order_num_num; let optiones; // 漏斗渲染 function polt(data) {// 从名为data的对象中获取ordata属…...
SQLServer利用QQ邮箱做SMTP服务器发邮件
环境 Microsoft SQL Server 2019 (RTM) - 15.0.2000.5 (X64) SQL Server Management Studio 15.0.18384.0 SQL Server 管理对象 (SMO) 16.100.46367.54 Microsoft .NET Framework 4.0.30319.42000 操作系统 Windows Server2019 ———————————————— 前言…...
flutter 多文本,其中文本下划线往下移动
变态需求 flutter中再满足多行文本,文本内有多个样式,并且多个样式可触发事件的情况,将其中的一部分文本的下划线往下移 方式一: 实现 使用RichText组件,主要是看中里面的WidgetSpan可以穿child为一个widget 实现源…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...
转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...
JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...
10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...
Web中间件--tomcat学习
Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机,它可以执行Java字节码。Java虚拟机是Java平台的一部分,Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...
C# 表达式和运算符(求值顺序)
求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如,已知表达式3*52,依照子表达式的求值顺序,有两种可能的结果,如图9-3所示。 如果乘法先执行,结果是17。如果5…...
R 语言科研绘图第 55 期 --- 网络图-聚类
在发表科研论文的过程中,科研绘图是必不可少的,一张好看的图形会是文章很大的加分项。 为了便于使用,本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中,获取方式: R 语言科研绘图模板 --- sciRplothttps://mp.…...
基于PHP的连锁酒店管理系统
有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发,数据库mysql,前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...
