通常狀況下每一個spring boot工程啓動都有固定的端口,可是固定端口不利用服務的動態擴容,若是在一臺服務器上須要對同一個服務進行多實例部署,很容java
易出現端口衝突,那麼怎麼解決這個問題呢?
mysql
random隨機端口
在spring boot中,能夠經過${random}來生成隨機數字,咱們能夠在配置文件中,這麼設置端口:web
server.port=${random.int(2000,8000)}
經過random.int方法,指定生成2000~8000的隨機端口。這樣每次啓動的端口都不同。spring
屢次啓動,發現每次的端口都不一致說明配置成功。
sql
注意事項:
這裏須要注意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運行時的全局變量。app
工具類:dom
@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環境變量jvm
/**
* 開始命令
*/
@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中。
總結
一、爲何要設置隨機端?主要是爲了解決動態擴容時出現端口衝突的問題。
二、怎麼獲取一個有效的隨機端口號
三、spring boot下實現隨機端口的三種方式。關於方式三的自定義屬性源的實現方式能夠多多品味,實踐一下,更好的體會屬性文件的加載順序。
以爲有用,記得點贊關注。
本文分享自微信公衆號 - 跟着老萬學java(douzhe_2019)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。