[Day - 22] - 今晚我想来个Spring Async非同步的感觉

Abrstract

叮咚!今晚我想来点可不可的.....,不对,是Async,这是在Spring 2.2.4 所提倡非同步注解模式(@Async),在这之前呢,大部分使用者都是采用Thread,Runnable及Callable这三种方法进去建立执行绪达到非同步效果,这样的写法到产出许多笨重且难以维护的程序码,此时Spring @Async进行以提倡新的写法,可将所有非同步方法包装在一个服务(@Service)内,透过每个方法回传非同步物件(AsyncResult)提供於客户端进行获取值使用,每个方法都透过Future介面实现,在java.util.concurrent包中。Future介面是Java执行绪Future模式的实现,可以来进行非同步计算,透过此种方式方便注入个式领域服务(Domain service)中进行触发对应的执行绪,可达到更简便的方式进行非同步工作效益,现在运送员小编将快速将这个套件功能送到你脑袋中。

Principle Introduction

每个异步处理程序(@Async)相当於创建一只新的子执行绪(Thread),并自己独立执行一项任务,父执行绪会循序等待每个子执行绪执行完任务,并获取相关物件值,再次进行相关逻辑运算後,才会回传最终结果,以下范例读者可进行参考,可进一步了解相关运作原理。

步骤一、配置执行绪池设定档

spring.task.pool.corePoolSize = 5
spring.task.pool.maxPoolSize = 50
spring.task.pool.keepAliveSeconds= 60
spring.task.pool.queueCapacity = 10000
spring.task.pool.threadNamePrefix = Wesiting-Thread-

步骤二、将配置组态模型,并将值注入模型中

@Configuration
@ConfigurationProperties(prefix = "spring.task.pool")
public class TaskThreadPoolConfig {
    private int corePoolSize = 5;

    private int maxPoolSize = 50;

    private int keepAliveSeconds = 60;

    private int queueCapacity =10000;

    private String threadNamePrefix = "Weisting-AsyncTask-";

    public int getCorePoolSize() {
        return corePoolSize;
    }

    public void setCorePoolSize(int corePoolSize) {
        this.corePoolSize = corePoolSize;
    }

    public int getMaxPoolSize() {
        return maxPoolSize;
    }

    public void setMaxPoolSize(int maxPoolSize) {
        this.maxPoolSize = maxPoolSize;
    }

    public int getKeepAliveSeconds() {
        return keepAliveSeconds;
    }

    public void setKeepAliveSeconds(int keepAliveSeconds) {
        this.keepAliveSeconds = keepAliveSeconds;
    }

    public int getQueueCapacity() {
        return queueCapacity;
    }

    public void setQueueCapacity(int queueCapacity) {
        this.queueCapacity = queueCapacity;
    }

    public String getThreadNamePrefix() {
        return threadNamePrefix;
    }

    public void setThreadNamePrefix(String threadNamePrefix) {
        this.threadNamePrefix = threadNamePrefix;
    }
}

步骤三、配置非同步服务,将每个方法配置Future介面,并回传AsyncResult封装物件。

Service
public class AsyncCaller {
    Logger logger = LoggerFactory.getLogger(AsyncCaller.class);
    @Async
    public Future<SeaFood> asyncSeaFoodWithReturnTypeFromTaiwan(String id) {
        System.out.println("Execute taiwan's async method asynchronously - "
                + Thread.currentThread().getName());
        try {
            Thread.sleep(5000);
            SeaFood seaFood = SEA_FOOD_CACHE_TAIWAN.asMap().getOrDefault(id,null);
            return new AsyncResult<SeaFood>(seaFood);
        } catch (InterruptedException e) {
            logger.error("InterruptedException : {}",e.toString() );
        }

        return null;
    }


    @Async
    public Future<SeaFood> asyncSeaFoodWithReturnTypeFromChina(String id) {
        System.out.println("Execute china's async method asynchronously - "
                + Thread.currentThread().getName());
        try {
            Thread.sleep(5000);
            SeaFood seaFood = SEA_FOOD_CACHE_CHINESE.asMap().getOrDefault(id,null);
            return new AsyncResult<SeaFood>(seaFood);
        } catch (InterruptedException e) {
            logger.error("InterruptedException : {}",e.toString() );
        }

        return null;
    }
}

步骤四、注入非同步服务,并触发後,并等待任务完成後进行获取相关物件值,以便进行回传。此段写法与各位开发者早期Future-Callable写法相同,可进行参考。

    @Autowired
    AsyncCaller asyncCaller;

    public SeaFood testAsyncAnnotationForMethodsWithReturnSeaFood(LocationEnum locationEnum,String id)
            throws InterruptedException, ExecutionException {
        System.out.println("Invoking an asynchronous method. "
                + Thread.currentThread().getName());

        Future<SeaFood> future = null;
        if (locationEnum == LocationEnum.TAIWAN)
            future = asyncCaller.asyncSeaFoodWithReturnTypeFromTaiwan(id);
        else
            future = asyncCaller.asyncSeaFoodWithReturnTypeFromChina(id);

        while (true) {
            if (future.isDone()) {
               return future.get();
            }
            System.out.println("Continue doing something else. Waiting... ");
            Thread.sleep(1000);
        }
    }

根据触发Find Taiwan product API後,会跟上续结果一样等待五秒後才接收到回传结果。

image

//Log
Invoking an asynchronous method. http-nio-8080-exec-1
Continue doing something else. Waiting... 
Execute taiwan's async method asynchronously - Wesiting-Thread-1
Continue doing something else. Waiting... 
Continue doing something else. Waiting... 
Continue doing something else. Waiting... 
Continue doing something else. Waiting... 
Continue doing something else. Waiting... 

透过以上范例与原理,小编在这边相当推荐给大家做使用,非常简便又快速,是种不错的好方法。

其运作方式皆是透过我们先前所叙的CGLIB切面式编程框架进行代理,由下图每个Bean类型我们可得知,此监测GUI为运送员小编抽空开发,最後一天会开源给大家做参考引用,先行提供给大家参考。
image

Structure

如图一所示,在预设的情况下会自动透过代理程序创建一个AsyncAnnotationBeanPostProcessor处理器,无论是何种处理器都会间接时间BeanFactoryAware此接口,并在实例化的方法(setBeanFactory)中,除了设置BeanFactory之外,也创建一个Async注解模式的处理器(AsyncAnnotationAdvisor),透过图二我们可进一步的了解其构造。

image
图一、AsyncAnnotationBeanPostProcessor启动关系流程图

由下图可得知,在AsyncAnnotationAdvisor进行建构子初始化时,会建立执行绪与异常拦截器(AnnotationAsyncExecutionInterceptor),及根据@Async注解位置建立切入点(AnnotationMatchingPointcut)触发元件,可以看到非同步触发拦截器(AnnotationAsyncExecutionInterceptor)间接实现了方法拦截器(MethodInterceptor)接口,而方法拦截器(MethodInterceptor)是AOP切入点处理器,处理器中最终被触发的是invoke方法,即可进行触发开发者所配置类别中的Future方法,并回传AsyncResult结果物件值,即可达到使用者预期效果。

image
图二、Async核心运作流程图

Sample Source

spring-sample-async

Reference url

深入理解Spring系列之十五:@Async实现原理

Spring Boot(5) @Async非同步执行绪池详解


<<:  找LeetCode上简单的题目来撑过30天啦(DAY22)

>>:  [Day-28] cin深入练习(二)

[Day24]solidity合约内容讲解2

hi~我们今天要讨论有关solidity合约内容,今天的内容会延续昨天的!所以如果还没看昨天的建议...

大数据平台:分散式计算

Spark 支援批次资料、查询分析、资料流、机器学习及图处理(Graph Processing),...

Day 28:专案07 - 天气小助理02 | LINE Notify

图片来源:https://3c.ltn.com.tw/news/45392 现在已经是人手一机的时...

Day17 X 初探快取 & HTTP Caching

今天即将进入 Caching & Networking 章节的第一天,快取,是一个非常重要...

实习是进入职场前的探索

现在不管是学校课程规划或是同学主动想要了解职场,对於实习其实是一个可以看清自己的能力跟业界之间的差距...