1. 程式人生 > 程式設計 >Spring boot隨機埠你都不會還怎麼動態擴容

Spring boot隨機埠你都不會還怎麼動態擴容

一般情況下每個spring boot工程啟動都有固定的埠,但是固定埠不利用服務的動態擴容,如果在一臺伺服器上需要對同一個服務進行多例項部署,很容易出現埠衝突,那麼怎麼解決這個問題呢?

random隨機埠

在spring boot中,可以通過${random}來生成隨機數字,我們可以在配置檔案中,這麼設定埠:

server.port=${random.int(2000,8000)}

通過random.int方法,指定生成2000~8000的隨機埠。這樣每次啟動的埠都不一樣。

多次啟動,發現每次的埠都不一致說明配置成功。

Spring boot隨機埠你都不會還怎麼動態擴容

Spring boot隨機埠你都不會還怎麼動態擴容

注意事項:
這裡需要注意spring boot專案啟動屬性檔案的載入順序,spring boot的屬性是由裡向外載入,所以最外層的最後被載入,會覆蓋裡層的屬性。

所以如果主動在啟動命令中使用–server.port配置了專案的埠號,那麼屬性檔案中配置的隨機埠屬性就不會生效。

通過System.setProperty設定有效隨機埠

上面的方法雖然暫時達到了想要的效果,但是有個問題:如果生成的這個隨機埠已經被使用了,那麼專案啟動就會出現埠衝突。

那麼,我們能否通過一個檢測機制,讓生成的隨機埠一定是一個沒有被佔用的有效的隨機埠呢?

有效埠檢測原理:

通過建立socket連線,Socket socket = new Socket(Address,port);#address代表主機的IP地址,port代表埠號
如果對該主機的特定埠號能建立一個socket,則說明該主機的該埠在使用。

Socket socket = new Socket(Address,則說明該主機的該埠在使用。

實現思路:

通過在專案啟動前,獲取有效的隨機埠並通過System.setProperty將變數設定到系統的全域性變數中,這樣專案啟動時就可以從全域性變數中獲取到server.port變數的值。
這裡的system,系統指的是 JRE (runtime)system,即設定jvm執行時的全域性變數。

工具類:

@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環境變數

/**
 * 開始命令
 */
@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隨機埠你都不會還怎麼動態擴容

總結

1、為什麼要設定隨機端?主要是為了解決動態擴容時出現埠衝突的問題。
2、怎麼獲取一個有效的隨機埠號
3、spring boot下實現隨機埠的三種方式。關於方式三的自定義屬性源的實現方式可以多多品味,實踐一下,更好的體會屬性檔案的載入順序。

到此這篇關於Spring boot隨機埠你都不會還怎麼動態擴容的文章就介紹到這了,更多相關Spring boot隨機埠內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!