1. 背景
接手一个新项目,本地启动的过程中发现启动很慢,启动一次大概20几分钟,很疑惑,下面开始排查过程
2. 分析
jps
找到当前的java进程
jstack -l pid
查看对应进程状态
每次项目启动都会在这个地方锁住
在堆栈中发现有一段AsyncExportAutoConfiguration.setApplicationContext
,基本可以断定是Springboot
的一个自动配置类,并且继承了ApplicationContextAware
,用于加载某些配置。于是进入源码查看,终于发现问题。
该项目使用了elastic-job
,启动时都需要对声明的任务进行注册(有兴趣可以了解下该框架,这里不做拓展)
JobScheduler
的构造器中需要创建JobInstance
实例
JobInstance
的默认构造器中需要获取一些系统信息
- 问题就出在
InetAddress.getAddressesFromNameService
时会有一段时间的阻塞
3. 解决
修改hosts文件,路径/etc/hosts
,配置localhost->hostname
,::1->hostname
的映射,保存及时生效。hostname可以在命令行使用hostname
直接获取
除自己配置/etc/hosts
外,这里提供一个host文件检查器,启动前检查是否包含所需映射,仅供参考
@Slf4j
@Component
@Profile("local")
public class HostMappingChecker implements ApplicationContextAware {
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.info("Host Mapping Checker Start...");
String fileName = null;
// 判断系统
String osName = System.getProperty("os.name");
if ("linux".equalsIgnoreCase(osName) || "Mac OS X".equalsIgnoreCase(osName)) {
fileName = "/etc/hosts";
} else {
fileName = System.getenv("windir") + "\\system32\\drivers\\etc\\hosts";
}
try {
// 输入流
StringBuilder sourceFileStr = new StringBuilder();
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(fileName), StandardCharsets.UTF_8))) {
int readChar;
while ((readChar = bufferedReader.read()) != -1) {
sourceFileStr.append((char) readChar);
}
}
// 输出流
try (BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName), StandardCharsets.UTF_8));) {
// 写入信息
String hostName = InetAddress.getLocalHost().getHostName();
String ipv4Address = InetAddress.getLocalHost().getHostAddress();
String ipv6Address = "::1";
String localAddress = "127.0.0.1";
if (!containHostMapping(sourceFileStr.toString(), localAddress, hostName)) {
sourceFileStr.append("\n").append(localAddress).append(" ").append(hostName);
}
if (!containHostMapping(sourceFileStr.toString(), ipv4Address, hostName)) {
sourceFileStr.append("\n").append(ipv4Address).append(" ").append(hostName);
}
if (!containHostMapping(sourceFileStr.toString(), ipv6Address, hostName)) {
sourceFileStr.append("\n").append(ipv6Address).append(" ").append(hostName);
}
bufferedWriter.write(sourceFileStr.toString());
bufferedWriter.flush();// 清空缓冲区
}
} catch (Exception ex) {
log.error("Check Local Host Mapping Failed, ex: {}", Throwables.getStackTraceAsString(ex));
}
log.info("Host Mapping Checker End...");
}
/**
* 判断是否存在该映射
*
* @param source
* @param appendKey
* @param appendValue
* @return
*/
private static boolean containHostMapping(String source, String appendKey, String appendValue) {
// 最多允许10个空格
for (int i = 0; i < 10; i++) {
StringBuilder blank = new StringBuilder();
for (int j = 0; j <= i; j++) {
blank.append(" ");
}
if (source.contains(appendKey + blank + appendValue)) {
return true;
}
StringBuilder tab = new StringBuilder();
for (int j = 0; j <= i; j++) {
tab.append("\t");
}
if (source.contains(appendKey + tab + appendValue)) {
return true;
}
}
return false;
}
}