SPRING @Async를 활용한 multi thread 구현 - 2 - AsyncConfigurer 생성

2017. 7. 11. 11:11framework/spring

 

SPRING @Async를 활용한 multi thread 구현 - 2 - AsyncConfigurer 생성

 

 

Spring 에서 비동기 처리를 하기 위해서 AsyncConfigurer@Asynk를 사용하려고 한다.

 

 

 개발 환경은 Java 8 , Tomcat 8 , Spring 4.1 이다.

 

 

본 포스팅은  

 

 

SPRING @Async를 활용한 multi thread 구현 - 1 - 개요

SPRING @Async를 활용한 multi thread 구현 - 2 - AsyncConfigurer 생성

SPRING @Async를 활용한 multi thread 구현 - 3 - @Async 사용 및 Task 추가

SPRING @Async를 활용한 multi thread 구현 - 4 - ExceptionHandler 생성

SPRING @Async를 활용한 multi thread 구현 - 5 - 구현

 

 



 

이번 시간에는 AsyncConfigurer 을 활용하여 Executor 를 생성하는 방법까지이다.

 

 

 

 

 

먼저 class 를 생성해야 한다.

1
2
3
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
cs

1# : @Configuration 를 활용하여 bean 객체 등록

2# : @EnableAsync 를 활용해서 비동기 프로세서를 사용하겠다고 선언

3# : AsyncConfigurer 를 상속받아서 AsyncConfig 구현

 

Executor 에 사용할 변수들 상수화

1
2
3
4
5
6
7
8
9
10
11
    /** 샘플 기본 Thread 수 */
    private static int TASK_SAMPLE_CORE_POOL_SIZE = 2;
    /** 샘플 최대 Thread 수 */
    private static int TASK_SAMPLE_MAX_POOL_SIZE = 5;
    /** 샘플 QUEUE 수 */
    private static int TASK_SAMPLE_QUEUE_CAPACITY = 0;
    /** 샘플 Thread Bean Name */
    private static String EXECUTOR_SAMPLE_BEAN_NAME = "executorSample";
    /** 샘플 Thread */
    @Resource(name = "executorSample")
    private ThreadPoolTaskExecutor executorSample;
cs

 

AsyncConfigurer 를 상속받아 클래스를 만들게 되면 @Override 해야 하는 함수가 2개 존재한다.

첫번째로는 getAsyncExecutor 함수이다.

기본적으로 Executor 를 하나를 만들어야 한다.

필자는 ThreadPoolTaskExecutor 를 만들었다.

원래는 1#의 @Bean 은 없다.

다중으로 만들기 위해서 추가해 주었다.

 

1
2
3
4
5
6
7
8
9
10
11
    @Bean(name = "executorSample")
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(TASK_SAMPLE_CORE_POOL_SIZE);
        executor.setMaxPoolSize(TASK_SAMPLE_MAX_POOL_SIZE);
        executor.setQueueCapacity(TASK_SAMPLE_QUEUE_CAPACITY);
        executor.setBeanName(EXECUTOR_SAMPLE_BEAN_NAME);
        executor.initialize();
        return executor;
    }
cs

1# : Bean Name 추가

5# : pool size 지정

6# : 최대 pool size 지정

7# : queue size 지정

8# : bean name 지정

 

또 한가지 함수는 getAsyncUncaughtExceptionHandler 함수이다.

ExceptionHandler을 연결하는 함수인데 일단 생략하다.

뒤에서 다룰 예정이다.

1
2
3
4
5
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        // TODO Auto-generated method stub
        return null;
    }
cs

 

기본 설정은 끝났다.

 

여기에 추가로 함수 2개를 더 만들었다.

이 함수의 역할은 task를 생성하기 전에 pool이 모두 찼는지를 체크하는 함수이다.

필요할 수 도 있지만 필요 없을 수 도 있다.

생략 가능하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
    /**
     * 샘플 Thread 등록 가능 여부
     *
     * @return 실행중인 task 개수가 최대 개수(max + queue)보다 크거나 같으면 false
     */
    public boolean isSampleTaskExecute() {
        boolean rtn = true;
 
        System.out.println("EXECUTOR_SAMPLE.getActiveCount() : " + executorSample.getActiveCount());
 
        // 실행중인 task 개수가 최대 개수(max + queue)보다 크거나 같으면 false
        if (executorSample.getActiveCount() >= (TASK_SAMPLE_MAX_POOL_SIZE + TASK_SAMPLE_QUEUE_CAPACITY)) {
            rtn = false;
        }
 
        return rtn;
    }
 
    /**
     * 샘플 Thread 등록 가능 여부
     *
     * @param createCnt : 생성 개수
     * @return 실행중인 task 개수 + 실행할 개수가 최대 개수(max + queue)보다 크면 false
     */
    public boolean isSampleTaskExecute(int createCnt) {
        boolean rtn = true;
 
        // 실행중인 task 개수 + 실행할 개수가 최대 개수(max + queue)보다 크거나 같으면 false
        if ((executorSample.getActiveCount() + createCnt) > (TASK_SAMPLE_MAX_POOL_SIZE + TASK_SAMPLE_QUEUE_CAPACITY)) {
            rtn = false;
        }
 
        return rtn;
    }
cs

 

설명은 주석으로 대체

 

 

 

 

근데 필자는 Executor 를 다중으로 생성하려고 한다.

 

다중으로 생성할 필요가 없다면 끝났다.

 

 

 

 

 

Executor 에 사용할 변수들 상수화

마찬가지로 새로운 Executor에 사용할 변수를 만든다.

1
2
3
4
5
6
7
8
9
10
11
    /** 기타 기본 Thread 수 */
    private static int TASK_ETC_CORE_POOL_SIZE = 5;
    /** 기타 최대 Thread 수 */
    private static int TASK_ETC_MAX_POOL_SIZE = 10;
    /** 기타 QUEUE 수 */
    private static int TASK_ETC_QUEUE_CAPACITY = 0;
    /** 기타 Thread Bean Name */
    private static String EXECUTOR_ETC_BEAN_NAME = "executorEtc";
    /** 기타 Thread */
    @Resource(name = "executorEtc")
    private ThreadPoolTaskExecutor executorEtc;
cs

 

그리고 새로운 Executor 를 생성하는 함수를 만든다.

1
2
3
4
5
6
7
8
9
10
11
    @Bean(name = "executorEtc")
    @Qualifier
    public Executor taskExecutorEtc() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(TASK_ETC_CORE_POOL_SIZE);
        executor.setMaxPoolSize(TASK_ETC_MAX_POOL_SIZE);
        executor.setQueueCapacity(TASK_ETC_QUEUE_CAPACITY);
        executor.setBeanName(EXECUTOR_ETC_BEAN_NAME);
        executor.initialize();
        return executor;
    }
cs

1# : Bean Name 추가

2# : @Qualifier 를 사용해서 타입 충돌을 방지한다.

5# : pool size 지정

6# : 최대 pool size 지정

7# : queue size 지정

8# : bean name 지정

 

마지막으로 생략 가능한 pool 체크 함수도 똑같이 만든다.

물론 명명규칙은 다르게 ~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
    /**
     * 기타 Thread 등록 가능 여부
     *
     * @return 실행중인 task 개수가 최대 개수(max + queue)보다 크거나 같으면 false
     */
    public boolean isEtcTaskExecute() {
        boolean rtn = true;
 
        // 실행중인 task 개수가 최대 개수(max + queue)보다 크거나 같으면 false
        if (executorEtc.getActiveCount() >= (TASK_ETC_MAX_POOL_SIZE + TASK_ETC_QUEUE_CAPACITY)) {
            rtn = false;
        }
 
        return rtn;
    }
 
    /**
     * 기타 Thread 등록 가능 여부
     *
     * @param createCnt : 생성 개수
     * @return 실행중인 task 개수 + 실행할 개수가 최대 개수(max + queue)보다 크면 false
     */
    public boolean isEtcTaskExecute(int createCnt) {
        boolean rtn = true;
 
        // 실행중인 task 개수 + 실행할 개수가 최대 개수(max + queue)보다 크거나 같으면 false
        if ((executorEtc.getActiveCount() + createCnt) > (TASK_ETC_MAX_POOL_SIZE + TASK_ETC_QUEUE_CAPACITY)) {
            rtn = false;
        }
 
        return rtn;
    }
cs

 

설명은 주석으로 대체

 

 

이것으로 @Async를 사용하기 위한 모든 설정이 끝났다.

 

다음 포스팅은 @Async를 사용해서 task 를 추가하는 방법에 대해 설명한다.

 



 

전체소스

AsyncConfig.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import java.util.concurrent.Executor;
 
import javax.annotation.Resource;
 
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
    /** 샘플 기본 Thread 수 */
    private static int TASK_SAMPLE_CORE_POOL_SIZE = 2;
    /** 샘플 최대 Thread 수 */
    private static int TASK_SAMPLE_MAX_POOL_SIZE = 5;
    /** 샘플 QUEUE 수 */
    private static int TASK_SAMPLE_QUEUE_CAPACITY = 0;
    /** 샘플 Thread Bean Name */
    private static String EXECUTOR_SAMPLE_BEAN_NAME = "executorSample";
    /** 샘플 Thread */
    @Resource(name = "executorSample")
    private ThreadPoolTaskExecutor executorSample;
 
    /** 기타 기본 Thread 수 */
    private static int TASK_ETC_CORE_POOL_SIZE = 5;
    /** 기타 최대 Thread 수 */
    private static int TASK_ETC_MAX_POOL_SIZE = 10;
    /** 기타 QUEUE 수 */
    private static int TASK_ETC_QUEUE_CAPACITY = 0;
    /** 기타 Thread Bean Name */
    private static String EXECUTOR_ETC_BEAN_NAME = "executorEtc";
    /** 기타 Thread */
    @Resource(name = "executorEtc")
    private ThreadPoolTaskExecutor executorEtc;
 
    /**
     * 샘플 Thread 생성
     *
     * @return
     */
    @Bean(name = "executorSample")
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(TASK_SAMPLE_CORE_POOL_SIZE);
        executor.setMaxPoolSize(TASK_SAMPLE_MAX_POOL_SIZE);
        executor.setQueueCapacity(TASK_SAMPLE_QUEUE_CAPACITY);
        executor.setBeanName(EXECUTOR_SAMPLE_BEAN_NAME);
        executor.initialize();
        return executor;
    }
 
    /**
     * 샘플 Thread 등록 가능 여부
     *
     * @return 실행중인 task 개수가 최대 개수(max + queue)보다 크거나 같으면 false
     */
    public boolean isSampleTaskExecute() {
        boolean rtn = true;
 
        System.out.println("EXECUTOR_SAMPLE.getActiveCount() : " + executorSample.getActiveCount());
 
        // 실행중인 task 개수가 최대 개수(max + queue)보다 크거나 같으면 false
        if (executorSample.getActiveCount() >= (TASK_SAMPLE_MAX_POOL_SIZE + TASK_SAMPLE_QUEUE_CAPACITY)) {
            rtn = false;
        }
 
        return rtn;
    }
 
    /**
     * 샘플 Thread 등록 가능 여부
     *
     * @param createCnt : 생성 개수
     * @return 실행중인 task 개수 + 실행할 개수가 최대 개수(max + queue)보다 크면 false
     */
    public boolean isSampleTaskExecute(int createCnt) {
        boolean rtn = true;
 
        // 실행중인 task 개수 + 실행할 개수가 최대 개수(max + queue)보다 크거나 같으면 false
        if ((executorSample.getActiveCount() + createCnt) > (TASK_SAMPLE_MAX_POOL_SIZE + TASK_SAMPLE_QUEUE_CAPACITY)) {
            rtn = false;
        }
 
        return rtn;
    }
 
    /**
     * 기타 Thread 생성
     *
     * @return
     */
    @Bean(name = "executorEtc")
    @Qualifier
    public Executor taskExecutorEtc() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(TASK_ETC_CORE_POOL_SIZE);
        executor.setMaxPoolSize(TASK_ETC_MAX_POOL_SIZE);
        executor.setQueueCapacity(TASK_ETC_QUEUE_CAPACITY);
        executor.setBeanName(EXECUTOR_ETC_BEAN_NAME);
        executor.initialize();
        return executor;
    }
 
    /**
     * 기타 Thread 등록 가능 여부
     *
     * @return 실행중인 task 개수가 최대 개수(max + queue)보다 크거나 같으면 false
     */
    public boolean isEtcTaskExecute() {
        boolean rtn = true;
 
        // 실행중인 task 개수가 최대 개수(max + queue)보다 크거나 같으면 false
        if (executorEtc.getActiveCount() >= (TASK_ETC_MAX_POOL_SIZE + TASK_ETC_QUEUE_CAPACITY)) {
            rtn = false;
        }
 
        return rtn;
    }
 
    /**
     * 기타 Thread 등록 가능 여부
     *
     * @param createCnt : 생성 개수
     * @return 실행중인 task 개수 + 실행할 개수가 최대 개수(max + queue)보다 크면 false
     */
    public boolean isEtcTaskExecute(int createCnt) {
        boolean rtn = true;
 
        // 실행중인 task 개수 + 실행할 개수가 최대 개수(max + queue)보다 크거나 같으면 false
        if ((executorEtc.getActiveCount() + createCnt) > (TASK_ETC_MAX_POOL_SIZE + TASK_ETC_QUEUE_CAPACITY)) {
            rtn = false;
        }
 
        return rtn;
    }
 
    /* (non-Javadoc)
     * @see org.springframework.scheduling.annotation.AsyncConfigurer#getAsyncUncaughtExceptionHandler()
     */
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new AsyncExceptionHandler();
    }
}
cs