Spring boot随机端口你都不会,怎么动态扩容?

简介: 一般情况下每个spring boot工程启动都有固定的端口,但是固定端口不利用服务的动态扩容,如果在一台服务器上需要对同一个服务进行多实例部署,很容易出现端口冲突,那么怎么解决这个问题呢?

random随机端口


在spring boot中,可以通过${random}来生成随机数字,我们可以在配置文件中,这么设置端口:

server.port=${random.int(2000,8000)}


通过random.int方法,指定生成2000~8000的随机端口。这样每次启动的端口都不一样。


多次启动,发现每次的端口都不一致说明配置成功。

46.png

45.png

注意事项:

这里需要注意spring boot项目启动属性文件的加载顺序,spring boot的属性是由里向外加载,所以最外层的最后被加载,会覆盖里层的属性。

所以如果主动在启动命令中使用–server.port配置了项目的端口号,那么属性文件中配置的随机端口属性就不会生效。


注意:除Spring boot外,我还整理了最新Java架构项目实战教程+大厂面试题库,有兴趣的可以 点击此处 免费获取,没基础勿进哦


通过System.setProperty设置有效随机端口


上面的方法虽然暂时达到了想要的效果,但是有个问题:如果生成的这个随机端口已经被使用了,那么项目启动就会出现端口冲突。

那么,我们能否通过一个检测机制,让生成的随机端口一定是一个没有被占用的有效的随机端口呢?


有效端口检测原理:

通过建立socket连接,Socket socket = new Socket(Address,port);#address代表主机的IP地址,port代表端口号

如果对该主机的特定端口号能建立一个socket,则说明该主机的该端口在使用。

Socket socket = new Socket(Address,port);#address代表主机的IP地址,port代表端口号

如果对该主机的特定端口号能建立一个socket,则说明该主机的该端口在使用。


实现思路:

通过在项目启动前,获取有效的随机端口并通过System.setProperty将变量设置到系统的全局变量中,这样项目启动时就可以从全局变量中获取到server.port变量的值。

这里的system,系统指的是 JRE (runtime)system,即设置jvm运行时的全局变量。


工具类:

@Slf4j
public class NetUtils {
    /**
     * 测试本机端口是否被使用
     * @param port
     * @return
     */
    public static boolean isLocalPortUsing(int port){
        boolean flag = true;
        try {
            //如果该端口还在使用则返回true,否则返回false,127.0.0.1代表本机
            flag = isPortUsing("127.0.0.1", port);
        } catch (Exception e) {
        }
        return flag;
    }
    /***
     * 测试主机Host的port端口是否被使用
     * @param host
     * @param port
     * @throws UnknownHostException
     */
    public static boolean isPortUsing(String host,int port)  {
        boolean flag = false;
        try {
            InetAddress Address = InetAddress.getByName(host);
            Socket socket = new Socket(Address,port);  //建立一个Socket连接
            flag = true;
        } catch (IOException e) {
           //log.info(e.getMessage(),e);
        }
        return flag;
    }
    //start--end是所要检测的端口范围
    static int start=0;
    static int end=1024;
    /**
     * 由于本机上安装了mysql,采用3306端口去验证
     * @param args
     */
    public static void main(String args[]){
            int testPost =3306;
            if(isLocalPortUsing(testPost)){
                System.out.println("端口 "+testPost+" 已被使用");
            }else{
                System.out.println("端口 "+testPost+"未使用");
            }
    }
}
public class ServerPortUtils {
    /**
     * 获取可用端口
     * @return
     */
    public static int getAvailablePort(){
         int max = 65535;
         int min = 2000;
         Random random = new Random();
         int port = random.nextInt(max)%(max-min +1) + min;
         boolean using = NetUtils.isLocalPortUsing(port);
         if(using){
             return  getAvailablePort();
         }else{
             return  port;
         }
    }
}


项目启动前设置server.port环境变量

/**
 * 开始命令
 */
@Slf4j
public class StartCommand {
    public StartCommand(String[] args){
         Boolean isServerPort = false;
         String serverPort = "";
         if(args != null){
              for (String arg:args){
                    if(StringUtils.hasText(arg) &&
                            arg.startsWith("--server.port")
                    ){
                        isServerPort = true;
                        serverPort = arg;
                        break;
                    }
              }
         }
         //没有指定端口,则随机生成一个可用的端口
        if(!isServerPort){
              int port = ServerPortUtils.getAvailablePort();
              log.info("current server.port=" + port);
              System.setProperty("server.port",String.valueOf(port));
        }else{//指定了端口,则以指定的端口为准
            log.info("current server.port=" + serverPort.split("=")[1]);
            System.setProperty("server.port",serverPort.split("=")[1]);
        }
    }
}


启动类调用方法:

@SpringBootApplication
@EnableUserClient
@RestController
public class DemoApplication {
    @Autowired
    Environment environment;
    public static void main(String[] args) {
        new StartCommand(args);
        SpringApplication.run(DemoApplication.class, args);
    }
}


通过自定义PropertiesPropertySource属性源实现


public class MyEnvironmentPostProcessor implements EnvironmentPostProcessor {
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        //MapPropertySource
        Properties properties = new Properties();
        properties.put("server.port", ServerPortUtils.getAvailablePort());
        System.out.println(properties.get("server.port"));
        PropertiesPropertySource source = new PropertiesPropertySource("myCustom", properties);
        environment.getPropertySources().addLast(source);
        //environment.getPropertySources().addAfter();
    }
}


通过配置在resources/META-INF/spring.factories文件中使用全名注册

org.springframework.boot.env.EnvironmentPostProcessor=com.laowan.demo.command.MyEnvironmentPostProcessor


这样在项目启动后,就会将该属性源加载到Environment中。

44.png



server.port=0随机端口 (推荐)


43.png

通过设置server.port=0,在spring boot项目启动时,会自动去寻找一个空闲的端口,避免端口冲突。


扩展:server.port=-1


42.png

设置为-1是为了完全关闭HTTP端点,但仍创建一个WebApplicationContext,

主要是在单元测试时使用。


验证:

执行单元测试,获取server.port属性

@SpringBootTest
@Slf4j
class DemoApplicationTests {
    @Autowired
    Environment environment;
    @Test
    void getProperties() {
        System.out.println(environment.getProperty("server.port"));
    }
}


执行结果为:-1


注意:最后送大家十套2020最新Java架构实战教程+大厂面试题库,有兴趣的 点击此处 免费获取,没基础勿进哦


总结


1、为什么要设置随机端?主要是为了解决动态扩容时出现端口冲突的问题。

2、怎么获取一个有效的随机端口号

3、spring boot下实现随机端口的三种方式。关于方式三的自定义属性源的实现方式可以多多品味,实践一下,更好的体会属性文件的加载顺序。

4、补充通过server.port=0,设置一个有效的随机端口,推荐做法

目录
相关文章
|
12月前
|
XML Java 应用服务中间件
Spring Boot 两种部署到服务器的方式
本文介绍了Spring Boot项目的两种部署方式:jar包和war包。Jar包方式使用内置Tomcat,只需配置JDK 1.8及以上环境,通过`nohup java -jar`命令后台运行,并开放服务器端口即可访问。War包则需将项目打包后放入外部Tomcat的webapps目录,修改启动类继承`SpringBootServletInitializer`并调整pom.xml中的打包类型为war,最后启动Tomcat访问应用。两者各有优劣,jar包更简单便捷,而war包适合传统部署场景。需要注意的是,war包部署时,内置Tomcat的端口配置不会生效。
2841 17
Spring Boot 两种部署到服务器的方式
|
10月前
|
JavaScript 前端开发 Java
Idea启动SpringBoot程序报错:Veb server failed to start. Port 8082 was already in use;端口冲突的原理与解决方案
本文解决了Idea启动SpringBoot程序报错:Veb server failed to start. Port 8082 was already in use的问题,并通过介绍端口的使用原理和操作系统的端口管理机制,可以更有效地解决端口冲突问题,并确保Web服务器能够顺利启动和运行。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
11月前
|
网络协议 Java Shell
java spring 项目若依框架启动失败,启动不了服务提示端口8080占用escription: Web server failed to start. Port 8080 was already in use. Action: Identify and stop the process that’s listening on port 8080 or configure this application to listen on another port-优雅草卓伊凡解决方案
java spring 项目若依框架启动失败,启动不了服务提示端口8080占用escription: Web server failed to start. Port 8080 was already in use. Action: Identify and stop the process that’s listening on port 8080 or configure this application to listen on another port-优雅草卓伊凡解决方案
747 7
|
10月前
|
Java 数据库 微服务
微服务——SpringBoot使用归纳——Spring Boot中的项目属性配置——指定项目配置文件
在实际项目中,开发环境和生产环境的配置往往不同。为简化配置切换,可通过创建 `application-dev.yml` 和 `application-pro.yml` 分别管理开发与生产环境配置,如设置不同端口(8001/8002)。在 `application.yml` 中使用 `spring.profiles.active` 指定加载的配置文件,实现环境快速切换。本节还介绍了通过配置类读取参数的方法,适用于微服务场景,提升代码可维护性。课程源码可从 [Gitee](https://gitee.com/eson15/springboot_study) 下载。
418 0
|
12月前
|
Java 测试技术 Spring
Spring Boot随机端口怎么动态扩容?
在Spring Boot中,可以通过`${random.int(2000,8000)}`在配置文件中设置随机端口,确保每次启动时端口不同。此外,还可以通过检测机制确保生成的随机端口未被占用,避免端口冲突。具体实现包括使用`System.setProperty`设置有效随机端口、自定义属性源以及直接设置`server.port=0`让Spring Boot自动选择空闲端口。推荐使用`server.port=0`以简化配置并避免冲突。
269 8
|
SQL JSON Java
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和PageHelper进行分页操作,并且集成Swagger2来生成API文档,同时定义了统一的数据返回格式和请求模块。
619 1
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
|
存储 运维 安全
Spring运维之boot项目多环境(yaml 多文件 proerties)及分组管理与开发控制
通过以上措施,可以保证Spring Boot项目的配置管理在专业水准上,并且易于维护和管理,符合搜索引擎收录标准。
847 2
|
Java Spring
【SpringBoot】技能一之修改端口与banner样式
【SpringBoot】技能一之修改端口与banner样式
249 5
|
安全 Java 应用服务中间件
如何将Spring Boot应用程序运行到自定义端口
如何将Spring Boot应用程序运行到自定义端口
1226 0
|
6月前
|
Java Spring 容器
SpringBoot自动配置的原理是什么?
Spring Boot自动配置核心在于@EnableAutoConfiguration注解,它通过@Import导入配置选择器,加载META-INF/spring.factories中定义的自动配置类。这些类根据@Conditional系列注解判断是否生效。但Spring Boot 3.0后已弃用spring.factories,改用新格式的.imports文件进行配置。
1079 0