Spring Boot随机端口怎么动态扩容?

简介: 在Spring Boot中,可以通过`${random.int(2000,8000)}`在配置文件中设置随机端口,确保每次启动时端口不同。此外,还可以通过检测机制确保生成的随机端口未被占用,避免端口冲突。具体实现包括使用`System.setProperty`设置有效随机端口、自定义属性源以及直接设置`server.port=0`让Spring Boot自动选择空闲端口。推荐使用`server.port=0`以简化配置并避免冲突。

random随机端口

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

代码解读

复制代码

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

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

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

注意事项:

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

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

通过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中。

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

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

扩展:server.port=-1

设置为-1是为了完全关闭HTTP端点,但仍创建一个WebApplicationContext, 主要是在单元测试时使用。

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

代码解读

复制代码

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

执行结果为:-1

总结

  1. 为什么要设置随机端?主要是为了解决动态扩容时出现端口冲突的问题。
  2. 怎么获取一个有效的随机端口号
  3. spring boot下实现随机端口的三种方式。关于方式三的自定义属性源的实现方式可以多多品味,实践一下,更好的体会属性文件的加载顺序。
  4. 补充通过server.port=0,设置一个有效的随机端口,推荐做法


作者:程序媛阿菲

链接:https://juejin.cn/post/6844904164263395335

来源:稀土掘金

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

目录
打赏
0
8
8
0
185
分享
相关文章
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服务器能够顺利启动和运行。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
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-优雅草卓伊凡解决方案
85 7
如何将Spring Boot应用程序运行到自定义端口
如何将Spring Boot应用程序运行到自定义端口
178 0
|
5月前
|
【SpringBoot】技能一之修改端口与banner样式
【SpringBoot】技能一之修改端口与banner样式
56 5
springBoot如何设置yml文件,设置端口号
springBoot如何设置yml文件,设置端口号
SpringBoot配置-配置文件分类,server.port修改端口,自定义修改配置内容
SpringBoot配置-配置文件分类,server.port修改端口,自定义修改配置内容
微服务——SpringBoot使用归纳——Spring Boot集成MyBatis——基于 xml 的整合
本教程介绍了基于XML的MyBatis整合方式。首先在`application.yml`中配置XML路径,如`classpath:mapper/*.xml`,然后创建`UserMapper.xml`文件定义SQL映射,包括`resultMap`和查询语句。通过设置`namespace`关联Mapper接口,实现如`getUserByName`的方法。Controller层调用Service完成测试,访问`/getUserByName/{name}`即可返回用户信息。为简化Mapper扫描,推荐在Spring Boot启动类用`@MapperScan`注解指定包路径避免逐个添加`@Mapper`
26 0
微服务——SpringBoot使用归纳——Spring Boot集成Thymeleaf模板引擎——Thymeleaf 介绍
本课介绍Spring Boot集成Thymeleaf模板引擎。Thymeleaf是一款现代服务器端Java模板引擎,支持Web和独立环境,可实现自然模板开发,便于团队协作。与传统JSP不同,Thymeleaf模板可以直接在浏览器中打开,方便前端人员查看静态原型。通过在HTML标签中添加扩展属性(如`th:text`),Thymeleaf能够在服务运行时动态替换内容,展示数据库中的数据,同时兼容静态页面展示,为开发带来灵活性和便利性。
28 0
微服务——SpringBoot使用归纳——Spring Boot中的项目属性配置——少量配置信息的情形
本课主要讲解Spring Boot项目中的属性配置方法。在实际开发中,测试与生产环境的配置往往不同,因此不应将配置信息硬编码在代码中,而应使用配置文件管理,如`application.yml`。例如,在微服务架构下,可通过配置文件设置调用其他服务的地址(如订单服务端口8002),并利用`@Value`注解在代码中读取这些配置值。这种方式使项目更灵活,便于后续修改和维护。
14 0
微服务——SpringBoot使用归纳——Spring Boot使用slf4j进行日志记录—— application.yml 中对日志的配置
在 Spring Boot 项目中,`application.yml` 文件用于配置日志。通过 `logging.config` 指定日志配置文件(如 `logback.xml`),实现日志详细设置。`logging.level` 可定义包的日志输出级别,例如将 `com.itcodai.course03.dao` 包设为 `trace` 级别,便于开发时查看 SQL 操作。日志级别从高到低为 ERROR、WARN、INFO、DEBUG,生产环境建议调整为较高级别以减少日志量。本课程采用 yml 格式,因其层次清晰,但需注意格式要求。
27 0

热门文章

最新文章