需求来源
项目采用随机端口启动但是会出现重复等问题,直接全随机会导致端口抢占问题。
主要思路
- 启动spring的时候获取到容器端口号
- .做一下当前容器端口号是否被暂用校验。
- 写一段随机代码获取可用端口,轮训获取到可用的返回
- 端口号写入容器中。
- 如何无侵入的修改所有项目。自定义starter
随机端口获取方法以及校验法
@Data
@Slf4j
public class ServerPortUtil {
@Value("${whcloud-config.server.maxPort:59999}")
private int MAX_PORT;
@Value("${whcloud-config.server.minPort:50000}")
private int MIN_PORT;
public String getAvailablePort(String portStr) {
if (!ObjectUtils.isEmpty(portStr)) {
int port = Integer.parseInt(portStr);
if (isLocalePortUsing(port)) {
String availablePort = getAvailablePort();
return availablePort;
}else {
return portStr;
}
}
return getAvailablePort();
}
/**
* 获取可用端口
*
* @return
*/
public String getAvailablePort() {
Random random = new Random();
// 最大尝试次数为端口范围
while (true) {
// 指定范围内随机端口,随便写的算法,根据自己需要调整
int port = random.nextInt(MAX_PORT - MIN_PORT) + MIN_PORT;
boolean isUsed = isLocalePortUsing(port);
if (!isUsed) {
log.info("随机可用端口为:{}", port);
return String.valueOf(port);
}
}
}
/**
* 检查给定端口是否在用
*
* @param port 端口号
* @return true:端口已经占用,false:端口没有占用
*/
public boolean isLocalePortUsing(int port) {
try {
// 建立一个Socket连接, 如果该端口还在使用则返回true, 否则返回false, 127.0.0.1代表本机
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), port);
socket.close();
return true;
} catch (Exception e) {
// 异常说明端口连接不上,端口能使用
}
return false;
}
}
我们使用WebServerFactoryCustomizer来自定义容器实现端口
@Slf4j
public class ServerPortRandomCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
private Environment environment;
private ServerPortUtil serverPortUtil;
public ServerPortRandomCustomizer(Environment environment, ServerPortUtil serverPortUtil) {
this.environment = environment;
this.serverPortUtil = serverPortUtil;
}
@Override
public void customize(ConfigurableServletWebServerFactory factory) {
String portStr = environment.getProperty("server.port");
log.info("容器启动端口号:{}",portStr);
// 自定义修改后添加进去覆盖
String availablePort = serverPortUtil.getAvailablePort(portStr);
if (!ObjectUtils.isEmpty(availablePort)){
log.info("自定义修改端口号:{}",availablePort);
factory.setPort(Integer.parseInt(availablePort));
}
}
}
我们以starter的方式提供jar
@Configuration
@Import({ServerPortConfig.class})
public class ServerPortRandomAutoConfigure {
private static final Logger LOGGER = LoggerFactory.getLogger(ServerPortRandomAutoConfigure.class);
public ServerPortRandomAutoConfigure() {
}
@Bean
@ConditionalOnMissingBean
public ServerPortRandomCustomizer serverPortRandomCustomizer(ServerPortConfig serverPortConfig, Environment environment) {
LOGGER.info("Init ServerPortRandomCustomizer");
return new ServerPortRandomCustomizer(environment, serverPortConfig);
}
}
最后一步吧启动器放到spring.factories里面
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.wh.whcloudcommonstarter.configuration.ServerPortRandomAutoConfigure
需要使用得服务引入jar包即可
<dependency>
<groupId>com.wh</groupId>
<artifactId>whcloud-common-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>