【问题标题】:Spring boot server port range settingSpring Boot 服务器端口范围设置
【发布时间】:2020-06-06 19:16:37
【问题描述】:

是否可以为spring boot应用程序的application.yml文件中的server.port设置一个可接受的范围。

我已将 server.port=0 设置为自动分配的端口,而不是硬编码的端口。

我们的网络操作人员希望限制此端口分配的可用范围。

有什么想法吗?

【问题讨论】:

    标签: spring-boot


    【解决方案1】:

    在 user1289300 和 Dave Syer 之后,我使用答案制定了一个解决方案。它作为从 server 部分的 application.yml 文件中读取的配置提供。我提供了一个端口范围最小值和最大值可供选择。 再次感谢

    @Configuration
    @ConfigurationProperties("server")
    public class EmbeddedServletConfiguration{
    
    /*
        Added EmbeddedServletContainer as Tomcat currently. Need to change in future if  EmbeddedServletContainer get changed
     */
    private final int MIN_PORT = 1100;
    private final int MAX_PORT = 65535;
    /**
     * this is the read port from the applcation.yml file
     */
    private int port;
    /**
     * this is the min port number that can be selected and is filled in from the application yml fil if it exists
     */
    private int maxPort = MIN_PORT;
    
    /**
     * this is the max port number that can be selected and is filled
     */
    private int minPort = MAX_PORT;
    
    /**
     * Added EmbeddedServletContainer as Tomcat currently. Need to change in future if  EmbeddedServletContainer get changed
     *
     * @return the container factory
     */
    @Bean
    public EmbeddedServletContainerFactory servletContainer() {
        return new TomcatEmbeddedServletContainerFactory();
    }
    
    @Bean
    public EmbeddedServletContainerCustomizer containerCustomizer() {
            return new EmbeddedServletContainerCustomizer() {
            @Override
            public void customize(ConfigurableEmbeddedServletContainer container) {
                // this only applies if someone has requested automatic port assignment
                if (port == 0) {
                    // make sure the ports are correct and min > max
                    validatePorts();
                    int port = SocketUtils.findAvailableTcpPort(minPort, maxPort);
                    container.setPort(port);
                }
               container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/404"));
               container.addErrorPages(new ErrorPage(HttpStatus.FORBIDDEN, "/403"));
    
            }
        };
    }
    
    /**
     * validate the port choices
     * - the ports must be sensible numbers and within the alowable range and we fix them if not
     * - the max port must be greater than the min port and we set it if not
     */
     private void validatePorts() {
         if (minPort < MIN_PORT || minPort > MAX_PORT - 1) {
             minPort = MIN_PORT;
         }
    
         if (maxPort < MIN_PORT + 1 || maxPort > MAX_PORT) {
             maxPort = MAX_PORT;
         }
    
         if (minPort > maxPort) {
             maxPort = minPort + 1;
         }
     }
    
    }
    

    【讨论】:

      【解决方案2】:

      只需实现EmbeddedServletContainerCustomizer http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-web-applications.html#boot-features-programmatic-embedded-container-customization

      当然,您可以对下面的public static boolean available(int port) 进行改进,以检查端口的可用性,因为某些端口虽然可用,但有时会被拒绝,例如端口 1024,取决于操作系统,也可以从某些属性文件中读取范围,但不能使用 Spring,因为范围在加载上下文之前设置,但这应该不是问题,我将所有内容放在一个文件中以显示不让它看起来漂亮的方法

      @Configuration
      @ComponentScan
      @EnableAutoConfiguration
      
      
      public class DemoApplication {
      
          private static final int MIN_PORT = 1100; // to by set according to your
          private static final int MAX_PORT = 9000; // needs or uploaded from
          public static int myPort; // properties whatever suits you
      
          public static void main(String[] args) {
      
          int availablePort = MIN_PORT;
          for (availablePort=MIN_PORT; availablePort < MAX_PORT; availablePort++) {
              if (available(availablePort)) {
      
                  break;
              }
          }
          if (availablePort == MIN_PORT && !available(availablePort)) {
              throw new IllegalArgumentException("Cant start container for port: " + myPort);
      
          }
          DemoApplication.myPort = availablePort;
      
          SpringApplication.run(DemoApplication.class, args);
      }
      
          public static boolean available(int port) {
              System.out.println("TRY PORT " + port);
              // if you have some range for denied ports you can also check it
              // here just add proper checking and return 
              // false if port checked within that range
              ServerSocket ss = null;
              DatagramSocket ds = null;
              try {
                  ss = new ServerSocket(port);
                  ss.setReuseAddress(true);
                  ds = new DatagramSocket(port);
                  ds.setReuseAddress(true);
                  return true;
              } catch (IOException e) {
              } finally {
                  if (ds != null) {
                      ds.close();
                  }
      
                  if (ss != null) {
                      try {
                          ss.close();
                      } catch (IOException e) {
                          /* should not be thrown */
                      }
                  }
              }
      
              return false;
          }
      
      }
      

      这是最重要的部分:

      @Component
      class CustomizationBean implements EmbeddedServletContainerCustomizer {
      
          @Override
          public void customize(ConfigurableEmbeddedServletContainer container) {
      
              container.setPort(DemoApplication.myPort);
      
          }
      
      }
      

      【讨论】:

      • Spring 有一个 SocketUtils,如果您喜欢这个总体思路,它可以完成大部分工作。
      • 谢谢,好东西我不知道,但是没有任何方法可以考虑排除范围,只有最大值和最小值,是否应该在 SocketUtils 中添加一个或者应该在外部完成?
      【解决方案3】:

      最简单的配置方法是在application.properties 中使用以下内容。 这里我提到了8084作为最小范围,8100作为最大范围。

      server.port=${random.int[8084,8100]}
      

      【讨论】:

        【解决方案4】:

        spring boot 项目存在挑战,我们暂时无法将此功能添加到spring boot,如果您有任何解决方案,请贡献。

        Spring boot server port range support Pull Request

        【讨论】:

        • 好的,Spring Boot 的人说 SocketUtils.findAvailableTcpPort 太脆弱,无法在生产中使用,这使得这里给出的所有解决方案都无效。
        【解决方案5】:

        我们在 Spring Boot 1.5.9 中使用 EmbeddedServletContainerCustomizer 完成了这项工作,如下所示:

        @Bean
        public EmbeddedServletContainerCustomizer containerCustomizer() {
            return (container -> {
                try {
        
                    // use defaults if we can't talk to config server
                    Integer minPort = env.getProperty("minPort")!=null ? Integer.parseInt(env.getProperty("minPort")) : 7500;
                    Integer maxPort = env.getProperty("maxPort")!=null ? Integer.parseInt(env.getProperty("maxPort")) : 9500;
                    int port = SocketUtils.findAvailableTcpPort(minPort,maxPort);
                    System.getProperties().put("server.port", port);
                    container.setPort(port);
                } catch (Exception e) {
                    log.error("Error occured while reading the min & max port form properties : " + e);
                    throw new ProductServiceException(e);
                }
        
            });
        }
        

        然而,这在 Spring Boot 2.0.0.M7 中似乎是不可能的,我们正在寻找一种替代方法。

        【讨论】:

          【解决方案6】:

          使用此解决方案,应用程序可以选择自己的端口。我不明白为什么它会得到“-1”,因为它运行完美。

          import org.slf4j.Logger;
          import org.slf4j.LoggerFactory;
          import org.springframework.beans.factory.annotation.Value;
          import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
          import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
          import org.springframework.context.annotation.Configuration;
          import org.springframework.util.SocketUtils;    
          
          @Configuration
          
             class PortRangeCustomizerBean implements EmbeddedServletContainerCustomizer 
          {
          
          private final Logger logger = LoggerFactory.getLogger(this.getClass());
          
          @Value("${spring.port.range.min}")
          private int MIN_PORT;
          
          @Value("${spring.port.range.max}")
          private int MAX_PORT;
          
          @Override
          public void customize(ConfigurableEmbeddedServletContainer container) {
              int port = SocketUtils.findAvailableTcpPort(MIN_PORT, MAX_PORT);
              logger.info("Started with PORT:\t " + port);
              container.setPort(port);
          }
          

          }

          【讨论】:

          • 请解释您的解决方案。
          • 使用这个解决方案,应用程序选择自己的端口。我不明白为什么它会得到“-1”,因为它运行完美。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2023-03-06
          • 2019-07-12
          • 2019-02-18
          • 2019-04-17
          • 1970-01-01
          • 2016-04-24
          • 2021-02-05
          相关资源
          最近更新 更多