Spring boot随机端口你都不会还怎么动态扩容
作者:斗者_2013 发布时间:2021-09-29 10:10:14
一般情况下每个spring boot工程启动都有固定的端口,但是固定端口不利用服务的动态扩容,如果在一台服务器上需要对同一个服务进行多实例部署,很容易出现端口冲突,那么怎么解决这个问题呢?
random随机端口
在spring boot中,可以通过${random}来生成随机数字,我们可以在配置文件中,这么设置端口:
server.port=${random.int(2000,8000)}
通过random.int方法,指定生成2000~8000的随机端口。这样每次启动的端口都不一样。
多次启动,发现每次的端口都不一致说明配置成功。
注意事项:
这里需要注意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运行时的全局变量。
工具类:
@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中。
总结
1、为什么要设置随机端?主要是为了解决动态扩容时出现端口冲突的问题。
2、怎么获取一个有效的随机端口号
3、spring boot下实现随机端口的三种方式。关于方式三的自定义属性源的实现方式可以多多品味,实践一下,更好的体会属性文件的加载顺序。
来源:https://blog.csdn.net/w1014074794/article/details/106184883


猜你喜欢
- 1. 介绍这个项目让你可以去读取并解析一个PDF文件,并将其内部结构展示出来. PDF文件的格式标准文档可以从Adobe那儿获取到. 这个项
- 前言在实际工作中,重试机制是一个很常见的场景,比如:发送消息失败,下载网络文件失败等…,因为这些错误可能是网络波动造成
- 一、返回BufferedImage由于spring mvc不支持返回BufferedImage ,所以增加图片转换器@Configurati
- Handler是什么?Handler 是一个可以实现多线程间切换的类,通过 Handler 可以轻松地将一个任务切换到 Handler 所在
- 1.通过用FTP进行上传文件,首先要实现建立FTP连接,一般建立FTP连接,需要知道FTP配置有关的信息。一般要在Bean中建立一个Serv
- 现在越来越多的软件都开始使用沉浸式状态栏了,下面总结一下沉浸式状态栏的两种使用方法注意!沉浸式状态栏只支持安卓4.4及以上的版本状态栏:4.
- 入住博客园4年多了,一直都是看别人的博客,学习别人的知识,为各个默默无私贡献自己技术总结的朋友们顶一个;这几天突然觉得是时候加入该队列中,贡
- 一、介绍pair是将2个数据组合成一组数据,当需要这样的需求时就可以使用pair。当然你也可以自定义一个结构体struct。不过大家都是为了
- 在Java解析XML文件的过程中,有时需要获取符合某些特定条件的节点,以下是实现代码。import javax.xml.xpath.XPat
- int n;int &m = n;在C++中,多了一个C语言没有的引用声明符&,如上,m就是n的引用,简单的说m就是n的别名
- 在项目中引入springcloud中的gateway时报以下错误Description:Parameter 0 of method modi
- 系列文章已完成,目录如下:jdk-logging log4j logback日志系统实现机制原理介绍commons-lo
- 本文介绍了详解Maven * Nexus的安装与使用,分享给大家,具体如下:1.安装1.1 安装docker并加速yum update &am
- 本文实例为大家分享了java实现简单扫雷游戏的具体代码,供大家参考,具体内容如下package com.test.swing;import
- 主界面xml文件<RelativeLayout xmlns:android="http://schemas.android.
- 本文实例讲述了Android使用Eclipse 打开时“发现了以元素'd:skin'”开头的无效内容。此处不应含有子元素的解
- checkbox控件时导致Activity启动默认不显示输入法。网上很多资料说要放一个空的Linearlayout,完全是在误导大众,正确的
- Lambda表达式的进化之路为什么要使用Lambda表达式可以简洁代码,提高代码的可读性可以避免匿名内部类定义过多导致逻辑紊乱在原先实现接口
- 本文实例为大家分享了Android实现3D云标签效果的具体代码,供大家参考,具体内容如下一、自定义Viewpublic class TagC
- 一、JdbcTemplateSpring 框架对 JDBC 进行封装,使用 JdbcTemplate 方便实现对数据库操作二、实战2.1 引