【spring boot 启动慢排查】

思路:

1. 考虑各种配置链接耗时

比如数据库连接、redis连接、mq连接等这些链接地址可能因为网段不通导致连接超时,影响启动时长。

下面是我的一次排查记录
查看控制台日志

在这里插入图片描述
发现耗时2分钟左右,找到耗时的类DruidDataSource,根据日志找追踪源码,994行打印,耗时应该在994行上面的代码。
在这里插入图片描述
找到源码打印的地方,往上逐一排查可能耗时的逻辑,可以把这个初始化方法打断点挨着找,
我是找到循环的地方,可能有多次循环或者链接等待超时,如下图
在这里插入图片描述
继续排查:先说第2个地方可能线程阻塞耗时,进去上面两个方法发现内部没有太多的耗时操作,断点过程也发现这里CountDownLatch数量2,没有影响耗时。
在这里插入图片描述
继续排查:第1个地方,断点发现这里处理初始化连接数量,由于我的项目是多数据源,前面的数据源初始化连接数量都设置initialSize=1,很快就创建完成,最后一个数据源初始化连接设置initialSize=100,这里需要一直创建出设置的数量,耗时巨大。

2. 检查spring bean 加载耗时

准备了两个类,在Spring boot项目启动的时候记录bean的加载耗时时间。

/**
 * @author : Eric
 *
 */
@Component
public class LaunchTimeManager implements ApplicationListener<ContextRefreshedEvent> {

    private Map<String, LaunchTime> launchTimeMap = new ConcurrentHashMap<>();

    public void beanStart(String beanName, Long startTime) {
        if (Objects.nonNull(launchTimeMap.get(beanName))) {
            System.out.printf("------------%s Bean重复启动 !!!-------------\n", beanName);
            return;
        }
        LaunchTime launchTime = new LaunchTime();
        launchTime.setBeanName(beanName);
        launchTime.setStartTime(startTime);
        launchTimeMap.putIfAbsent(beanName, launchTime);
    }

    public void beanEnd(String beanName, Long endTime) {
        if (Objects.isNull(launchTimeMap.get(beanName))) {
            System.out.printf("------------%s Bean还未启动 !!!-------------\n", beanName);
            return;
        }
        launchTimeMap.computeIfPresent(beanName, (k, v) -> {
            v.setEndTime(endTime);
            return v;
        });
    }

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        System.out.println("Spring容器启动完成");

        launchTimeMap.values().stream()
                .sorted((e1, e2) -> Long.valueOf(e2.getCostTime() - e1.getCostTime()).intValue())
                .limit(20)
                .forEach(e -> {
                    System.out.printf("启动耗时, beanName:%s, 耗时:%s \n", e.getBeanName(), e.getCostTime());
                });
    }

    /**
     * Bean启动时间
     */
    public static class LaunchTime {
        private String beanName;

        private Long startTime;

        private Long endTime;

        public String getBeanName() {
            return beanName;
        }

        public void setBeanName(String beanName) {
            this.beanName = beanName;
        }

        public Long getStartTime() {
            return startTime;
        }

        public void setStartTime(Long startTime) {
            this.startTime = startTime;
        }

        public Long getEndTime() {
            return endTime;
        }

        public void setEndTime(Long endTime) {
            this.endTime = endTime;
        }

        public Long getCostTime() {
            return (endTime == null ? 0L : endTime) - (startTime == null ? 0L : startTime) ;
        }
    }

}
/**
 * @author : Eric
 *
 */
@Component
public class LaunchTimeBeanPostProcessor implements BeanPostProcessor, InstantiationAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor {
    @Autowired
    private LaunchTimeManager launchInfoManager;

    @Override
    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
    }

    @Override
    public void resetBeanDefinition(String beanName) {
        MergedBeanDefinitionPostProcessor.super.resetBeanDefinition(beanName);
    }

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        launchInfoManager.beanStart(beanName, System.currentTimeMillis());
        return InstantiationAwareBeanPostProcessor.super.postProcessBeforeInstantiation(beanClass, beanName);
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        launchInfoManager.beanEnd(beanName, System.currentTimeMillis());
        return InstantiationAwareBeanPostProcessor.super.postProcessAfterInstantiation(bean, beanName);
    }

    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        return InstantiationAwareBeanPostProcessor.super.postProcessProperties(pvs, bean, beanName);
    }

    @Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
        return InstantiationAwareBeanPostProcessor.super.postProcessPropertyValues(pvs, pds, bean, beanName);
    }

}

最后结合自己的项目情况,逐步排查,相信大家也能找到对应的问题所在

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Spring Boot是一个用于快速构建独立的、基于Spring框架的Java应用程序的开发框架。它简化了Spring应用程序的配置和部署过程,提供了一种约定大于配置的方式来开发应用程序。 Spring Boot启动过程可以分为以下几个步骤: 1. 创建Spring Boot应用程序:首先,需要创建一个基于Spring BootJava项目。可以使用Spring Initializr来生成一个基本的项目结构,也可以手动创建一个Maven或Gradle项目,并添加相应的依赖。 2. 配置应用程序:在创建好的项目中,可以通过配置文件(application.properties或application.yml)来配置应用程序的属性,如端口号、数据库连接等。 3. 编写应用程序代码:根据业务需求,编写相应的Java类和方法。可以使用Spring Boot提供的注解和组件来简化开发过程,如@Controller、@Service、@Repository等。 4. 启动应用程序:在应用程序的入口类中,使用@SpringBootApplication注解标记该类为Spring Boot应用程序的入口点。然后,通过调用SpringApplication类的静态run()方法来启动应用程序。 5. 运行应用程序:启动应用程序后,Spring Boot会自动扫描并加载所有的组件和配置,并根据配置进行初始化。应用程序会监听指定的端口,等待客户端请求。 6. 处理请求:当有客户端请求到达时,Spring Boot会根据请求的URL和方法映射,调用相应的控制器方法进行处理。控制器方法可以返回数据或者跳转到指定的页面。 7. 关闭应用程序:当需要关闭应用程序时,可以通过调用SpringApplication类的静态exit()方法来优雅地关闭应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值