PersoRead
字数
801 字
阅读时间
4 分钟
1 随机服务运行端口
通用模块进行如下配置,并确保配置文件中没有硬编码端口设置,如果有手动端口设置,则不进行随机端口分配
属性源冲突
原因分析
属性源名称冲突:
- 当您使用相同的属性源名称(例如
"randomPort"
)为多个不同的属性添加端口时,第二次添加时会覆盖第一次添加的属性源。这意味着server.port
被设置后,dubbo.protocol.port
使用相同的属性源名称会替换掉server.port
的设置。
- 当您使用相同的属性源名称(例如
属性源的优先级:
- Spring 环境中的属性源具有优先级,越靠前的属性源优先级越高。因此,确保每个属性源具有唯一名称并正确添加到属性源列表的开头,可以避免覆盖问题。
解决方案
要避免属性源名称冲突,确保每个 EnvironmentPostProcessor
实例为不同的属性使用唯一的属性源名称。您已在代码中实现了这一点,通过将 portProperty
添加到属性源名称中,如 "randomPort-server.port"
和 "randomPort-dubbo.protocol.port"
。这种方式确保每个属性有一个独立的属性源,不会相互覆盖。
java
package com.mole.persoread.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertiesPropertySource;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.Properties;
import java.util.Random;
@Slf4j
public abstract class RandomPortEnvironmentPostProcessor implements EnvironmentPostProcessor {
// 最大尝试次数
private static final int MAX_ATTEMPTS = 100;
// 配置属性名称
protected final String portProperty;
// 最小端口号
protected final int minPort;
// 最大端口号
protected final int maxPort;
/**
* 构造函数
*
* @param portProperty 配置属性名称
* @param minPort 最小端口号
* @param maxPort 最大端口号
*/
protected RandomPortEnvironmentPostProcessor(String portProperty, int minPort, int maxPort) {
this.portProperty = portProperty;
this.minPort = minPort;
this.maxPort = maxPort;
}
/**
* 后置处理环境
*
* @param environment 环境
* @param application 应用
*/
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
log.info("{} is being executed", this.getClass().getSimpleName());
if (isPortAlreadySet(environment)) {
return;
}
Properties props = new Properties();
int port = findRandomAvailablePort();
if (port != -1) {
props.put(portProperty, port);
log.info("Props updated: {}", props);
String propertySourceName = "randomPort-" + portProperty;
environment.getPropertySources().addFirst(new PropertiesPropertySource(propertySourceName, props));
log.info("environment: {}", environment.getProperty(portProperty));
log.info("Random port {} assigned to {}", port, portProperty);
} else {
throw new IllegalStateException("No available ports found in the range " + minPort + "-" + maxPort);
}
}
/**
* 检查端口是否已经设置
*
* @param environment 环境
* @return 如果端口已设置且不为 -1,则返回 true,否则返回 false
*/ protected boolean isPortAlreadySet(ConfigurableEnvironment environment) {
String portValue = environment.getProperty(portProperty);
if (portValue != null && !"-1".equals(portValue)) {
log.info("Port {} is already set for {}", portValue, portProperty);
return true;
}
return false;
}
/**
* 查找指定范围内的可用随机端口
*
* @return 可用端口号,如果未找到则返回-1
*/ private int findRandomAvailablePort() {
Random random = new Random();
for (int i = 0; i < MAX_ATTEMPTS; i++) {
int port = random.nextInt((maxPort - minPort) + 1) + minPort;
if (isPortAvailable(port)) {
return port;
}
}
return -1;
}
/**
* 检查端口是否可用
*
* @param port 端口号
* @return 是否可用
*/
private boolean isPortAvailable(int port) {
try (ServerSocket socket = new ServerSocket(port)) {
socket.setReuseAddress(true);
return true;
} catch (IOException e) {
log.warn("Port {} is not available", port);
return false;
}
}
}
java
package com.mole.persoread.config;
public class RandomServerPortEnvironmentPostProcessor extends RandomPortEnvironmentPostProcessor {
public RandomServerPortEnvironmentPostProcessor() {
super("server.port", 8000, 9000);
}
}
java
package com.mole.persoread.config;
public class RandomDubboPortEnvironmentPostProcessor extends RandomPortEnvironmentPostProcessor {
public RandomDubboPortEnvironmentPostProcessor() {
super("dubbo.protocol.port", 20880, 21000);
}
}
src/main/resources/META-INF/spring.factories
org.springframework.boot.env.EnvironmentPostProcessor=com.mole.persoread.config.RandomServerPortEnvironmentPostProcessor,com.mole.persoread.config.RandomDubboPortEnvironmentPostProcessor