【发布时间】:2020-06-06 19:16:37
【问题描述】:
是否可以为spring boot应用程序的application.yml文件中的server.port设置一个可接受的范围。
我已将 server.port=0 设置为自动分配的端口,而不是硬编码的端口。
我们的网络操作人员希望限制此端口分配的可用范围。
有什么想法吗?
【问题讨论】:
标签: spring-boot
是否可以为spring boot应用程序的application.yml文件中的server.port设置一个可接受的范围。
我已将 server.port=0 设置为自动分配的端口,而不是硬编码的端口。
我们的网络操作人员希望限制此端口分配的可用范围。
有什么想法吗?
【问题讨论】:
标签: spring-boot
在 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;
}
}
}
【讨论】:
只需实现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);
}
}
【讨论】:
SocketUtils,如果您喜欢这个总体思路,它可以完成大部分工作。
最简单的配置方法是在application.properties 中使用以下内容。
这里我提到了8084作为最小范围,8100作为最大范围。
server.port=${random.int[8084,8100]}
【讨论】:
spring boot 项目存在挑战,我们暂时无法将此功能添加到spring boot,如果您有任何解决方案,请贡献。
【讨论】:
我们在 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 中似乎是不可能的,我们正在寻找一种替代方法。
【讨论】:
使用此解决方案,应用程序可以选择自己的端口。我不明白为什么它会得到“-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);
}
}
【讨论】: