概括

一应用多端口通俗来讲是指启动一个springboot项目同时监听多个端口。

使用场景:

1.反向代理中间件、动态网关实现。上篇文章介绍的是SpringBoot 反向代理的实现,简单接口转发可以满足,但仅仅依靠单个端口配置路由转发无法满足多域名多站点的反向代理,因为前端静态文件CSS、JS是相对路径。而多端口就能满足需求,Nginx通过多端口,实现的一个Nginx服务代理多个Web前端站点。

2.接口安全,实现一应用 提供对外端口和对内端口。

实现

pom.xml 引入

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

application.yml 配置

这里配置除了占用默认的8080端口,还会占用 9000-9070端口

server:
  followPort: 9000-9070

多Tomcat配置类

package com.terry.config;

import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.Connector;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

/**
 * Tomcat多端口占用配置
 * @author terry
 * @date 2022/7/26
 */
@Configuration
@Slf4j
public class EmbeddedTomcatConfiguration {
 
    @Value("${server.followPort}")
    private String followPort;

    /**
     * tomcat 从节点(用来处理代理请求)
     * @return
     */
    @Bean
    public ServletWebServerFactory servletContainer() {
        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
        String[] ports = followPort.split("-");
        List<Connector> result = new ArrayList<>();
        for (int i = Integer.parseInt(ports[0]); i <= Integer.parseInt(ports[1]); i++) {
            // if (!checkPortBind(i)) {
            result.add(buildConnector(i));
        }
        log.info("Tomcat Follow Port Binding {} Success!", followPort);
        tomcat.addAdditionalTomcatConnectors(result.toArray(new Connector[] {})); // 添加http
        return tomcat;
    }

    public Connector buildConnector(Integer port){
        Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
        connector.setScheme("http");
        connector.setPort(port);
        return connector;
    }

    /**
     * 验证端口占用(效率慢)
     * @param port
     * @return
     */
    public Boolean checkPortBind(Integer port){
        boolean isBind = true;
        try {
            new Socket(InetAddress.getByName("127.0.0.1"), port);
        } catch (IOException e) {
            isBind = false;
        }
        return isBind;
    }
}

启动成功,打印日志如下

2022-07-26 23:34:20.584  INFO 22856 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2022-07-26 23:34:20.816  INFO 22856 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) 9000 (http) 9001 (http) 9002 (http) 9003 (http) 9004 (http) 9005 (http) 9006 (http) 9007 (http) 9008 (http) 9009 (http) 9010 (http) 9011 (http) 9012 (http) 9013 (http) 9014 (http) 9015 (http) 9016 (http) 9017 (http) 9018 (http) 9019 (http) 9020 (http) 9021 (http) 9022 (http) 9023 (http) 9024 (http) 9025 (http) 9026 (http) 9027 (http) 9028 (http) 9029 (http) 9030 (http) 9031 (http) 9032 (http) 9033 (http) 9034 (http) 9035 (http) 9036 (http) 9037 (http) 9038 (http) 9039 (http) 9040 (http) 9041 (http) 9042 (http) 9043 (http) 9044 (http) 9045 (http) 9046 (http) 9047 (http) 9048 (http) 9049 (http) 9050 (http) 9051 (http) 9052 (http) 9053 (http) 9054 (http) 9055 (http) 9056 (http) 9057 (http) 9058 (http) 9059 (http) 9060 (http) 9061 (http) 9062 (http) 9063 (http) 9064 (http) 9065 (http) 9066 (http) 9067 (http) 9068 (http) 9069 (http) 9070 (http) with context path ''
2022-07-26 23:34:20.823  INFO 22856 --- [           main] com.terry.App                            : Started App in 1.726 seconds (JVM running for 2.577)