一文详解 springboot 项目启动时异步执行初始化逻辑

这篇具有很好参考价值的文章主要介绍了一文详解 springboot 项目启动时异步执行初始化逻辑。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

你知道的越多,你不知道的越多
点赞再看,养成习惯
如果您有疑问或者见解,欢迎指教:
企鹅:869192208

前言

前面的工作中,为了提高地区数据的响应时间,需要加载全国区划数据到 redis 中缓存起来,这个过程希望在项目时启动。
由于初始化全国区划到 redis 中这个过程是比较耗时的,所以我们可以考虑使用异步执行的方式去实现。

代码实现
定义异步处理工具类
import com.xymy.common.config.ThreadPoolExecutorUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;

/**
 * <h2>异步处理工具类<h2>
 *
 * @author xymy
 * @create 2023-02-07 16:45
 */
@Slf4j
public enum RunMerger {
    SM;

    public void asyncHandle(String threadName, Runnable ...runs) {
        this.asyncHandle(null, threadName,runs);
    }

    public void asyncHandle(ExecutorService seedPool, String threadName, Runnable ... runs) {
        if (seedPool == null) {
            seedPool = ThreadPoolExecutorUtil.getSeedPool(threadName, runs.length);
        }
        try {
            for (Runnable run : runs) {
                CompletableFuture.runAsync(() -> {
                        run.run();
                    },seedPool).exceptionally(ex -> {
                        log.error("处理异常", ex);
                    return null;
                });
            }
        } finally {
            ThreadPoolExecutorUtil.releaseSeedPool(seedPool);
        }
    }

    public void syncHandle(Runnable ...runs) {
        Arrays.stream(runs).forEach(Runnable::run);
    }

    public void asyncHandle(Runnable ...runs) {
        this.asyncHandle(null, runs);
    }

}
实现 java 线程池
  • 新建 ThreadPoolExecutorUtil 类
import cn.hutool.core.thread.ThreadFactoryBuilder;
import com.xymy.common.utils.DistrKeyGenerator;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.*;

/**
 * <h2>线程池<h2>
 *
 * @author xymy
 * @create 2021-11-22 14:24
 */
@Slf4j
public class ThreadPoolExecutorUtil {
    private static Map<String, ExecutorService> executors = new ConcurrentHashMap<>();

    public static ExecutorService getSeedPool(int poolSize) {
        DistrKeyGenerator keyGenerator = new DistrKeyGenerator();
        return getSeedPool(keyGenerator.generateKey() +"-", poolSize);
    }

    public static ExecutorService getSeedPool(String poolName, int poolSize) { // 需手动释放
        if (StringUtils.isBlank(poolName)) {
            DistrKeyGenerator keyGenerator = new DistrKeyGenerator();
            poolName = keyGenerator.generateKey() +"-";
        }
        ExecutorService executorService = executors.get(poolName);
        if (null == executorService) {
            synchronized (ThreadPoolExecutorUtil.class) {
                executorService = executors.get(poolName);
                if (null == executorService) {
                    executorService = init(poolName,poolSize);
                    executors.put(poolName, executorService);
                }
            }
        }
        return executorService;
    }

    public static void releaseSeedPool(ExecutorService executorService) {
        Set<Map.Entry<String, ExecutorService>> entries = executors.entrySet();
        for (Map.Entry<String, ExecutorService> entry : entries) {
            ExecutorService value = entry.getValue();
            if (value == executorService) {
                executors.remove(entry.getKey());
            }
        }
        if (executorService != null) {
            executorService.shutdown();
        }
    }

    public static void releaseSeedPool(String poolName) { // 释放
        ExecutorService executorService = executors.remove(poolName);
        if (executorService != null) {
            executorService.shutdown();
        }
    }

    private ThreadPoolExecutorUtil(){}


    private static ExecutorService init(String poolName, int poolSize) {
        ThreadFactoryBuilder threadFactoryBuilder = new ThreadFactoryBuilder();
        threadFactoryBuilder.setUncaughtExceptionHandler((t, e) -> log.error( "线程[{}]异常:", t.getName(), e));
        threadFactoryBuilder.setNamePrefix("apply-pool-" + poolName);
        threadFactoryBuilder.setDaemon(false);
        return new ThreadPoolExecutor(poolSize,
                poolSize,0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(poolSize*10),
                threadFactoryBuilder.build(),
                new ThreadPoolExecutor.CallerRunsPolicy());
    }

}
  • 新建 DistrKeyGenerator 类,用来生成主键
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>32.1.1-jre</version>
</dependency>
import com.google.common.base.Preconditions;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

/**
 * 主键生成
 * sharding-jdbc核心源码
 * @author xymy
 * @create 2019-07-24 09:37
 */
@Slf4j
@Component
public class DistrKeyGenerator {

    public static final long EPOCH;
    // 自增序列长度
    private static final long SEQUENCE_BITS = 12L;
    // workId的长度(单位是位时长度)
    private static final long WORKER_ID_BITS = 10L;

    private static final long SEQUENCE_MASK = (1 << SEQUENCE_BITS) - 1;

    private static final long WORKER_ID_LEFT_SHIFT_BITS = SEQUENCE_BITS;

    private static final long TIMESTAMP_LEFT_SHIFT_BITS = WORKER_ID_LEFT_SHIFT_BITS + WORKER_ID_BITS;
    // 位运算计算workerId的最大值(workerId占10位,那么1向左移10位就是workerId的最大值)
    private static final long WORKER_ID_MAX_VALUE = 1L << WORKER_ID_BITS;


    private long workerId = 0;
    // EPOCH就是起始时间,从2016-11-01 00:00:00开始的毫秒数
    static {
        Calendar calendar = Calendar.getInstance();
        calendar.set(2016, Calendar.NOVEMBER, 1);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        EPOCH = calendar.getTimeInMillis();
    }

    private long sequence;

    private long lastTime;

    public DistrKeyGenerator() {

    }

    public DistrKeyGenerator(long workerId) {
        Preconditions.checkArgument(workerId >= 0L && workerId < WORKER_ID_MAX_VALUE);
        this.workerId = workerId;
    }

    /**
     * Generate key.
     * 生成id
     * @return key type is @{@link Long}.
     */
    public synchronized long generateKey() {
        long currentMillis = getCurrentMillis();
        // 每次取分布式唯一ID的时间不能少于上一次取时的时间
        Preconditions.checkState(lastTime <= currentMillis, "Clock is moving backwards, last time is %d milliseconds, current time is %d milliseconds", lastTime, currentMillis);
        // 如果同一毫秒范围内,那么自增,否则从0开始
        if (lastTime == currentMillis) {
            // 如果自增后的sequence值超过4096,那么等待直到下一个毫秒
            if (0L == (sequence = ++sequence & SEQUENCE_MASK)) {
                currentMillis = waitUntilNextTime(currentMillis);
            }
        } else {
            sequence = 0;
        }
        // 更新lastTime的值,即最后一次获取分布式唯一ID的时间
        lastTime = currentMillis;
        if (log.isDebugEnabled()) {
            log.debug("{}-{}-{}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date(lastTime)), workerId, sequence);
        }
        // 从这里可知分布式唯一ID的组成部分;
        return ((currentMillis - EPOCH) << TIMESTAMP_LEFT_SHIFT_BITS) | (workerId << WORKER_ID_LEFT_SHIFT_BITS) | sequence;
    }
    // 获取下一毫秒的方法:死循环获取当前毫秒与lastTime比较,直到大于lastTime的值;
    private long waitUntilNextTime(final long lastTime) {
        long time = getCurrentMillis();
        while (time <= lastTime) {
            time = getCurrentMillis();
        }
        return time;
    }

    /**
     * Get current millis.
     *
     * @return current millis
     */
    private long getCurrentMillis() {
        return System.currentTimeMillis();
    }

}

新建 AppInit 实现 ApplicationRunner 接口完成启动项目时异步数据初始化
@Component
@Slf4j
public class AppInit implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) {

        RunMerger.SM.asyncHandle(() -> {
            log.info("项目启动完成,开始初始化全国区划数据...");
            //省略数据初始化的代码,此处为耗时的操作
            String countryCode = "xxxxx";
            xxService.initAreaInfoList(countryCode);
            log.info("项目启动完成,完成初始化全国区划数据...");
        });
    }
}

至此,就可以实现 springboot 项目启动时异步执行初始化逻辑。文章来源地址https://www.toymoban.com/news/detail-662514.html

到了这里,关于一文详解 springboot 项目启动时异步执行初始化逻辑的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • SpringBoot异步执行方法

    1. 源码跟踪 1.简单描述 在SpringBoot2.0.9之前需要手动自定义线程池(如下2.1), 然后指定线程池的名称 SpringBoot2.0.9以及之前的版本,使用的线程池默认是 SimpleAsyncTaskExcutor , , 之后的版本使用的是 ThreadpoolTaskExecutor 并且不需要手动的创建当前线程池(但往往我们还是会手动指定,具体原

    2024年02月07日
    浏览(26)
  • SpringBoot启动时的几种初始化操作

    1、静态代码块 static静态代码块,在类加载的时候即自动执行。 2、构造方法 在对象初始化时执行。执行顺序在static静态代码块之后。 3、通过注解@PostConstruct实现 @PostConstruct注解使用在方法上,它可以被用来标注一个 非静态的 void 方法 ,这个方法会在该类被 Spring 容器初始化

    2024年02月09日
    浏览(37)
  • springboot 异步执行方法详细介绍

            在Spring Boot中,异步执行方法是一种提高应用程序性能和响应性的技术。通过异步执行,你可以在处理耗时的业务逻辑时,不需要阻塞当前线程,从而提高应用程序的吞吐量和并发处理能力。         基本概念         在Spring中,异步执行通常是通过`@Asy

    2024年02月21日
    浏览(31)
  • 一文读懂异步和同步——async/await在运行时和普通代码的执行顺序的问题

    一、执行顺序问题 JavaScript中的await语句是异步编程中用于等待Promise对象执行结果的,它通常与async函数一起使用。在使用await时,程序的执行将暂停,直到该后面的Promise对象完成执行并返回结果。 函数代码执行顺序通常情况下按照代码所在文件从上至下

    2024年02月03日
    浏览(34)
  • 【Java】SpringBoot实现事件监听(异步执行)

            在Spring Boot中,事件监听是一种机制,通过该机制,你可以定义和触发自定义的事件,以及在应用程序中注册监听器来响应这些事件,提供了一种解耦的方式来处理应用程序中的事件。 文末有源码gitee地址!拉取可进行测试。 事件监听的主要组件包括: 事件(E

    2024年04月17日
    浏览(34)
  • SpringBoot 底层机制分析【Tomcat 启动+Spring 容器初始化+Tomcat 如何关联Spring 容器】【下】

    😀前言 本篇博文是关于SpringBoot 底层机制分析实现,希望能够帮助你更好的了解SpringBoot 😊 🏠个人主页:晨犀主页 🧑个人简介:大家好,我是晨犀,希望我的文章可以帮助到大家,您的满意是我的动力😉😉 💕欢迎大家:这里是CSDN,我总结知识的地方,欢迎来到我的博客

    2024年02月13日
    浏览(34)
  • 深入理解 Flink(八)Flink Task 部署初始化和启动详解

    核心入口: 部署 Task 链条:JobMaster -- DefaultScheduler -- SchedulingStrategy -- ExecutionVertex -- Execution -- RPC请求 -- TaskExecutor JobMaster 向 TaskExecutor 发送 submitTask() 的 RPC 请求,用来部署 StreamTask 运行。TaskExecutor 接收到 JobMaster 的部署 Task 运行的 RPC 请求的时候,就封装了一个 Task 抽象,然

    2024年01月17日
    浏览(68)
  • openstack详解(二十三)——Neutron其他配置、数据库初始化与服务启动

    今天继续给大家介绍Linux运维相关知识,本文主要内容是Neutron其他配置、数据库初始化与服务启动。 在上文openstack详解(二十二)——Neutron插件配置中,我们完成了Neutron插件的配置,今天,我们来进行Neutron的元数据代理配置、数据库初始化以及服务启动。 打开Neutron的元数

    2024年02月10日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包