Spring Boot 多端口监听实战:如何解决同一服务器部署多系统的难题

2026年01月26日/ 浏览 9

作为后端开发,你是不是也遇到过这样的窘境:手里就一台测试服务器,却要同时部署 3 个相似的 Spring Boot 项目 —— 一个给前端联调,一个做功能测试,一个留着改 bug。要是申请新服务器,流程走下来至少得 3 天,可产品经理催着下周就要上线;要是改端口重启项目,每次切换场景都得改配置、等重启,半天时间全耗在这上面了?

我上周就刚踩过这个坑。团队接了个电商小程序的后端开发需求,前端同事要联调支付接口,测试同事要测订单流程,我还得修复之前遗留的库存 bug。三台服务器申请不下来,只能在现有服务器上折腾,一开始每次切换都改 application.yml 里的 server.port,重启项目至少要 2 分钟,一天下来光等重启就浪费了快 1 小时。后来才发现,Spring Boot 早就支持多端口监听了,几行配置就能搞定,效率直接拉满。今天就把这个实战方案拆解开,帮你彻底摆脱 “端口不够用、重启耗时间” 的麻烦。

为什么会遇到 “多端口” 需求?这 3 个场景你肯定经历过

在聊具体实现之前,咱们先说说为啥多端口监听会成为后端开发的 “刚需”。不是咱们没事找事折腾配置,而是实际开发中这些场景根本绕不开:

第一个场景就是 “多环境并行测试”。就像我上周遇到的情况,一个项目要同时支撑联调、测试、bug 修复三个场景,每个场景都需要独立的运行环境 —— 前端联调不能影响测试数据,测试过程中发现的 bug 也不能阻塞联调进度。如果只用一个端口,每次切换场景都得清空数据、重启项目,不仅效率低,还容易因为数据混乱导致测试结果不准。

第二个场景是 “微服务拆分后的局部部署”。现在大部分项目都用微服务架构,比如用户服务、订单服务、支付服务分开部署。但在开发或测试阶段,有时候不需要启动所有微服务,只需要局部调整某一个服务。比如我之前改支付服务的加密逻辑时,只需要启动支付服务,但它又要调用订单服务的接口。这时候给支付服务配置多个端口,一个用于本地调试,一个用于对接其他服务,就不用反复修改注册中心的配置了。

第三个场景是 “旧系统迁移过渡”。很多公司会遇到 “新系统上线、旧系统暂不下线” 的情况,比如把传统 SSM 项目迁移到 Spring Boot,上线初期需要两个系统同时运行,接收相同的请求进行对比,确保数据一致。这时候如果两个系统部署在同一台服务器,就必须用不同的端口区分,总不能为了过渡专门申请新服务器吧?

这些场景的核心需求其实很简单:在有限的服务器资源下,让一个 Spring Boot 项目或多个项目能独立运行、互不干扰。而多端口监听,就是解决这个需求最直接、成本最低的方案,不用申请新硬件,不用复杂的容器编排,几行配置就能落地。

3 种多端口监听实现方案,从简单到灵活,新手也能看懂

接下来进入核心部分,给大家拆解开 3 种 Spring Boot 多端口监听的实现方式,分别适合不同的场景,你可以根据自己的需求选。这里用的是 Spring Boot 2.7.x 版本(目前企业中最常用的稳定版),JDK 11,大家可以对照着自己的项目版本调整。

方案 1:最基础的 yml 配置 ——3 行代码搞定,适合固定端口需求

如果你的端口数量固定,比如就需要 8080、8081、8082 三个端口,那直接在 application.yml 里配置就行,不用写一行 Java 代码,新手也能秒会。

首先打开项目的

src/main/resources/application.yml 文件,原来的配置可能只有一个端口: server: port: 8080

现在咱们把它改成 “ports”(注意是复数),配置多个端口:

server: ports: 8080,8081,8082 # 用英文逗号分隔多个端口 servlet: context-path: /demo # 项目上下文路径,多个端口共用

然后启动项目,看控制台输出:

Tomcat started on port(s): 8080 (http) 8081 (http) 8082 (http) Started DemoApplication in 2.345 seconds (JVM running for 2.876)

这时候你用 Postman 测一下,访问

http://localhost:8080/demo/hello、

http://localhost:8081/demo/hello、

http://localhost:8082/demo/hello,都能拿到相同的响应结果,说明多端口已经生效了。

这个方案的优点是简单,不用改代码,直接配配置文件就行;缺点是端口固定死了,如果需要新增或删除端口,必须改配置文件再重启项目,适合端口数量长期不变的场景,比如固定的多环境测试。

方案 2:编程式配置 —— 灵活控制端口,适合动态需求

如果你的端口需要根据环境变量动态调整,或者需要在代码里判断是否启用某个端口,那方案 1 就不够用了,这时候得用编程式配置,通过自定义

EmbeddedServletContainerCustomizer 来实现。

第一步,创建一个配置类,比如 MultiPortConfig.java,代码如下:

import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.server.WebServerFactoryCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MultiPortConfig { @Bean public WebServerFactoryCustomizer<TomcatServletWebServerFactory> webServerFactoryCustomizer() { // 这里可以从环境变量、配置中心获取端口,比如从application.yml的custom.ports读取 String ports = "8080,8081,8082,8083"; // 实际项目中建议放配置文件 String[] portArray = ports.split(","); return factory -> { // 遍历端口数组,添加多个连接器 for (String port : portArray) { int portNum = Integer.parseInt(port); factory.addAdditionalTomcatConnectors( // 创建HTTP连接器 new org.apache.catalina.connector.Connector( org.apache.coyote.http11.Http11NioProtocol.class.getName() ) {{ setPort(portNum); // 设置端口号 }} ); } }; } }

第二步,在 application.yml 里配置自定义的端口(也可以不用,直接在代码里写死,但推荐放配置文件,方便修改):

custom: ports: 8080,8081,8082,8083

然后修改配置类里的 ports 获取方式,从配置文件读取:

import org.springframework.beans.factory.annotation.Value; // 在配置类中注入配置 @Value("${custom.ports}") private String ports;

启动项目后,控制台会显示 4 个端口都已启动。这个方案的优点是灵活,比如你可以根据不同的环境(dev、test、prod)配置不同的端口,甚至可以在代码里加判断逻辑,比如生产环境只启用 8080 端口,测试环境启用多个端口。

这里有个小坑要注意:如果用的是 Spring Boot 2.x 版本,

WebServerFactoryCustomizer 的泛型要指定为

TomcatServletWebServerFactory;如果是 Spring Boot 1.x 版本,对应的类是

EmbeddedServletContainerCustomizer,别搞错了。

方案 3:多服务实例配置 —— 不同端口对应不同功能,适合复杂场景

有时候咱们不仅需要多端口,还需要不同端口对应不同的功能,比如 8080 端口处理普通接口,8081 端口处理文件上传,8082 端口处理定时任务。这时候可以用 Spring Boot 的多服务实例配置,给每个端口绑定不同的 ServletContext。

第一步,创建多个配置类,分别对应不同的端口和功能。比如创建 FileUploadConfig.java(处理文件上传,端口 8081):

import org.springframework.boot.autoconfigure.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.DispatcherServlet; @Configuration public class FileUploadConfig { @Bean public ServletRegistrationBean<DispatcherServlet> fileUploadServletRegistration(DispatcherServlet dispatcherServlet) { ServletRegistrationBean<DispatcherServlet> registrationBean = new ServletRegistrationBean<>(dispatcherServlet); registrationBean.addUrlMappings("/upload/*"); // 该端口只处理/upload路径的请求 registrationBean.setPort(8081); // 绑定8081端口 registrationBean.setName("fileUploadServlet"); // 给servlet起个名字,避免冲突 return registrationBean; } }

再创建 NormalApiConfig.java(处理普通接口,端口 8080):

@Configuration public class NormalApiConfig { @Bean public ServletRegistrationBean<DispatcherServlet> normalApiServletRegistration(DispatcherServlet dispatcherServlet) { ServletRegistrationBean<DispatcherServlet> registrationBean = new ServletRegistrationBean<>(dispatcherServlet); registrationBean.addUrlMappings("/api/*"); // 处理/api路径的请求 registrationBean.setPort(8080); // 绑定8080端口 registrationBean.setName("normalApiServlet"); return registrationBean; } }

第二步,在 application.yml 里关闭默认的端口配置,避免冲突:

server: port: -1 # 设为-1表示关闭默认端口

然后启动项目,这时候访问

http://localhost:8080/api/hello会走普通接口逻辑,访问

http://localhost:8081/upload/file会走文件上传逻辑,两个端口互不干扰。

这个方案适合功能拆分比较细的场景,比如把接口、文件、定时任务分开处理,方便后续维护和扩展。但缺点是配置相对复杂,需要创建多个 ServletRegistrationBean,新手可能需要多调试几次。

避坑指南:这 4 个问题一定要注意,不然会踩大雷

我在测试多端口监听的时候,踩过几个坑,这里跟大家分享一下,帮你少走弯路:

第一个坑是 “端口冲突”。如果配置的端口已经被其他程序占用,项目启动会报错 “Address already in use”。解决方法很简单:用 netstat 命令查看端口占用情况(Windows 用 netstat -ano | findstr "端口号",Linux 用 netstat -tuln | grep "端口号"),找到占用端口的进程,要么杀掉进程,要么换个未被占用的端口。

第二个坑是 “Spring Security 拦截多端口”。如果项目集成了 Spring Security,默认会拦截所有端口的请求,导致某些端口访问被拒绝。解决方法是在 SecurityConfig 里配置允许所有端口的请求,或者指定需要拦截的端口:

@Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/**").permitAll() // 允许所有请求,实际项目中要按需求调整 .and().csrf().disable(); }

第三个坑是 “日志输出混乱”。多端口同时运行时,日志会混在一起,分不清哪个请求来自哪个端口。解决方法是在日志配置里添加端口信息,比如在 logback.xml 里配置:

<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg - port:${server.port}%n</pattern>

这样每条日志都会显示对应的端口号,方便排查问题。

第四个坑是 “生产环境端口暴露”。测试环境可以用多个端口,但生产环境要注意安全,不能把所有端口都暴露出去。建议生产环境只启用必要的端口,并且通过 Nginx 反向代理,对外只暴露 80 或 443 端口,内部端口不对外网开放,降低安全风险。

总结:什么时候用多端口监听?这 3 个场景优先选

最后咱们总结一下,Spring Boot 多端口监听不是万能的,但在这 3 个场景下,它绝对是性价比最高的方案:

开发 / 测试阶段的多环境并行:不用申请多台服务器,一个项目多端口运行,支撑联调、测试、bug 修复同时进行,效率提升至少 50%;微服务局部调试:不需要启动所有微服务,只启动需要修改的服务,用多端口对接其他服务,减少资源占用;旧系统迁移过渡:新老系统同一台服务器运行,不同端口区分,避免数据混乱,平稳过渡。

如果你现在正被 “端口不够用、服务器申请难” 的问题困扰,建议你先试试方案 1 的 yml 配置,3 分钟就能搞定;如果需要动态调整端口,再用方案 2 的编程式配置;如果功能拆分细,就用方案 3 的多服务实例配置。

最后想跟大家说,后端开发的核心不是 “会用多少框架”,而是 “能解决多少实际问题”。一个小小的多端口配置,看似简单,却能帮咱们节省大量的时间和资源。你在项目中还遇到过哪些 “小需求大麻烦” 的情况?欢迎在评论区留言,咱们一起讨论解决方案;如果这篇文章对你有帮助,也别忘了点赞收藏,下次遇到端口问题直接翻出来用!

picture loss