| Scheduler
- 스프링에서 일정 주기마다 특정 작업을 수행할 수 있도록 하는 기능
- 주기 설정
fixedDelay | 작업 완료시점을 기준으로 일정시간 지연 후 반복 |
fixedRate | 작업 시작지점을 기준으로 일정시간 지연 후 방복 |
cron | 정규식 표현 |
- Cron 표현식
(1) 사용법
// 초/분/시/일/월/요일/연도(생략가능)
0 0 0 * * * <-- 매일 00시에
0/10 * * * * <-- 10초마다
0 0/5 * * * <-- 5분마다
0 0 14 * * * <-- 매일 오후 2시
0 0 2 ? 7 MON-WEB <-- 매년 7월 월-수 2시 00분 00초
(2) 범위
초 0 - 59
분 0 - 59
시 0 - 23
일 1 - 31
월 1 ‒ 12 || JAN - DEC
요일 0 - 6 || SUN - SAT
연도 1970 - 2099
(3) 특수기호 의미
* : 모든 수
? : 조건 없음(날짜와 요일에만 사용)
- : 범위(기간) 지정
, : 특정 여러 시간 지정
/ : 시작 시간과 반복 간격
L : 지정할 수 있는 범위의 마지막 값 (날짜와 요일에만 사용)
| 기본 사용법
1. @SpringBootApplication에 @EnableScheduling을 추가한다.
@SpringBootApplication
@EnableScheduling
public class BaedanguemApplication {
public static void main(String[] args) {
SpringApplication.run(BaedanguemApplication.class, args);
}
}
2. 스케줄러를 통해 제어해야하는 기능에 @Scheduled를 추가한다.
@Component
public class TestScheduler{
@Scheduled(cron = "0 0 0 * * *")
public void test(){
// 원하는 기능 삽입
}
}
3. 스케줄러의 주기를 별도로 관리하고 싶을 경우
(1) application.yml에 cron 표현식을 작성하여 사용할 수도 있다.
// application.yml
scheduler:
scrap:
yahoo: "0 0 0 * * *"
(2) SpEL을 통해 @Scheduled에 속성값을 넣어주기
@Component
public class TestScheduler{
@Scheduled(cron = "${scheduler.scrap.yahoo}")
public void test(){
// 원하는 기능 삽입
}
}
| 쓰레드 개념
- 멀티 쓰레드란? 한 프로세스가 여러 스레드로 동시에 여러 작업을 수행하는 것을 말한다.
* 하나의 프로세스 안에 여러 개의 쓰레드가 공유 자원을 두고 소통한다.
* 동기화 이슈 : 작업 사이에 실행 시기를 맞추는 걸 동기화라고 한다.
여러 쓰레드가 동일 자원에 접근할 때 동기화 이슈, 곧, 작업이 꼬이는 이슈가 발생한다.
* 동기화 이슈 해결 방안 : Mutual Exclusion (상호 배제)
* 뮤텍스와 세마포어 : 뮤텍스는 하나하나씩 공유 자원에 접근하게 하는 거고,
세마포어는 접근 갯수를 정하는 걸 말한다.
* 교착상태(Deadlock) : 쓰레드들이 서로 상대방의 작업을 기다리면서 무한 대기 상태에 빠지는 것
* 조건 : 상호 배제, 점유 대기, 비선점, 순환대기
* 기아상태(Starvation) : 특정 프로세스의 우선순위가 낮아 원하는 자원을 할당 받지 못하는 상태
- 쓰레드의 다섯가지 상태
(1) new : 탄생
(2) ready : 준비 (=runnable)
(3) running : 실행
(4) blocked/waiting : 일시정지(봉쇄/대기)
(5) exit : 종료 (=terminated)
- 자바에서 쓰레드를 일시정지 시키기
// 쓰레드 일시정지
Thread.sleep(1000); // 1초간 쓰레드 일시정지 후 깨어남
// 쓰레드 wait()와 다르다
Thread.wait(1000); // 1초간 쓰레드 대기하기 <- 멀티쓰레드 환경에서 쓰레드간 통신 위해 사용
// ㄴ notify() 또는 notifyAll()를 호출할 때까지
// 계속 대기하고 있는다.
- IntertuptedException : 인터럽트를 받은 스레드가 blocking 될 수 있는 메소드를 실행할 때 발생
- 자바에서 쓰레드를 종료시켜야할 때
* 자바에서 쓰레드를 사용할 땐, stop() 을 사용하지 않는다 -- deprecated되었다.
* 이유는, 종료시 자원들이 불완전한 상태로 남겨지지 때문이다.
* 대신 interrupt() 메소드를 통해 intteruptedException을 발생시켜 쓰레드를 종료한다.
try {
Thread.sleep(3000); // 3 seconds
} catch (InterruptedException e) { // 인터럽트를 받는 스레드가 blocking 될 수 있는 메소드를 실행할 때 발생
e.printStackTrace();
Thread.currentThread().interrupt();
}
| 스프링의 Scheduler 쓰레드 동작방식
- @Scheduled가 붙은 여러개의 메소드들이 있다고 가정할 때, 스프링을 실행하면
(1) main 쓰레드가 작동하고
(2) Scheduling 쓰레드가 별도로 하나 작동한다.
- 쓰레드풀을 통해 멀티 쓰레드 환경을 만들지 않았다면, 다수의 @Scheduled 메소드들은 하나의 쓰레드 안에서 작동한다.
- 아래와 같이 메인 함수와, 스케줄러 함수들을 작동시켰을 때에 결과를 보면,
여러 개의 스케줄링 메소드가 하나의 쓰레드에서 실행이되면서,
의도한 스케줄링 시간에 맞지 않게 작동이 되는 걸 볼 수 있다.
@SpringBootApplication
@EnableJpaRepositories("com.example.petbutler.persist")
@EnableTransactionManagement
@EnableScheduling
public class PetbutlerApplication {
public static void main(String[] args) {
SpringApplication.run(PetbutlerApplication.class, args);
System.out.println(Thread.currentThread().getName() + "-> MAIN");
}
}
@Configuration
public class SchedulerConfig {
// 1초 후 10초 동안 일시정지
@Scheduled(fixedDelay = 1000)
public void test1(){
try {
System.out.println(Thread.currentThread().getName() + "-> 테스트1");
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
Thread.interrupted();
}
}
// 1초 후 1초 동안 일시정지
@Scheduled(fixedDelay = 1000)
public void test2(){
try {
System.out.println(Thread.currentThread().getName() + "-> 테스트2");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
Thread.interrupted();
}
}
}
>> 결과
- 테스트1이 실행된 후 10초가 지난 후에야 테스트2가 작동하며, 이것이 반복된다.
scheduling-1-> 테스트2
2022-11-11 13:28:05.641 DEBUG 23980 --- [ main] ySourcesPropertyResolver$DefaultResolver : Found key 'spring.liveBeansView.mbeanDomain' in PropertySource 'systemProperties' with value of type String
2022-11-11 13:28:05.643 INFO 23980 --- [ main] c.e.petbutler.PetbutlerApplication : Started PetbutlerApplication in 17.004 seconds (JVM running for 20.012)
2022-11-11 13:28:05.645 DEBUG 23980 --- [ main] o.s.b.a.ApplicationAvailabilityBean : Application availability state LivenessState changed to CORRECT
2022-11-11 13:28:05.647 DEBUG 23980 --- [ main] o.s.b.a.ApplicationAvailabilityBean : Application availability state ReadinessState changed to ACCEPTING_TRAFFIC
main-> MAIN
scheduling-1-> 테스트1
scheduling-1-> 테스트2
scheduling-1-> 테스트1
scheduling-1-> 테스트2
scheduling-1-> 테스트1
scheduling-1-> 테스트2
scheduling-1-> 테스트1
scheduling-1-> 테스트2
scheduling-1-> 테스트1
| Thread Pool 개념
- 여러 개의 쓰레드를 유지/관리하기 위해서 사용한다.
- 미리 설정해준 크기만큼 쓰레드들을 만들어 두고, 해당 쓰레드들을 계속해 재사용할 수 있게 한다.
- Thread Pool의 적정 사이즈는?
* 너무 적은, 너무 많은 Thread Pool을 만들지 않는 것이 필요하다.
* 정확한 공식이 있는 것은 아니나 일반적으로 아래와 같이 지정할 경우 최적화하여 사용할 수 있다.
- CPU 처리가 많은 경우 : CPU 코어 갯수 N + 1 *연산작업 - I/O 작업이 많은 경우 : CPU 코어 갯수 N x 2 *I/O작업(=입출력작업)은 블로킹이 많다. |
* rf. I/O 란? I/O란 하드웨어 장치를 통한 입출력 정보를 컴퓨터에서 구동 중인 어플리케이션에서 컨트롤하고자 할 때 사용하는 일종의 인터페이스
| 스프링 스케줄러에서 ThreadPool를 사용하기
1. SchedulingConfigurer를 구현한 설정 클래스를 만든다.
2. configureTask(ScheduluedTaskRegistrar taskRegistrar) 메소드를 Override한다.
3. 메소드 안에서 ThreadPoolTaskScheduler 인스턴스를 생성한다.
4. ThreadPoolTaskScheduler에 쓰레드 갯수를 setting 하고 initialize(초기화)한다.
* Runtime.getRuntime().availableProcessors() 를 통해 CPU의 코어 갯수를 알아낼 수 있다.
5. ThreadPoolTaskScheduler를 매개변수인 taskRegistrar에 넣는다.
// 쓰레드 풀 설정
@Configuration
public class SchedulerConfig implements SchedulingConfigurer {
// 쓰레드 풀 갯수 설정 예시
// - CPU 처리가 많은 경우 CPU 코어 갯수 N + 1
// - I/O 작업이 많은 경우 CPU 코어 갯수 N x 2
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
ThreadPoolTaskScheduler threadPool = new ThreadPoolTaskScheduler();
int n = Runtime.getRuntime().availableProcessors(); // core 갯수
threadPool.setPoolSize(n + 1); // 쓰레드 풀 갯수 설정
threadPool.initialize(); // 쓰레드 풀 초기화
taskRegistrar.setTaskScheduler(threadPool); // 스케줄러에서 쓰레드풀 사용
}
}
[참고 및 출처]
부트캠프 수업내용 정리
쓰레드 관련
https://darrengwon.tistory.com/763
https://gold-dragon.tistory.com/263
https://velog.io/@jsj3282/Thread%EC%9D%98-%EC%83%81%ED%83%9C#1-%EC%93%B0%EB%A0%88%EB%93%9C%EB%9E%80
interrupt https://cornswrold.tistory.com/190
I/O는 어떻게 처리될까 https://chelseafandev.github.io/2021/07/13/os-io-system/
'Framework > 스프링 라이브러리' 카테고리의 다른 글
[Scraping] Jsoup으로 스크래핑하기 (0) | 2022.12.13 |
---|---|
[스프링 시큐러티] JWT 토큰 + 스프링 시큐러티 (0) | 2022.11.18 |
[스프링 시큐러티] 스프링 시큐러티 자료 모음 (수정중) (0) | 2022.10.29 |
[TEST] TDD 방식에 대한 자료 모음 (수정중) (0) | 2022.10.28 |
[JSON 파싱] ObjectMapper, Simple-json (0) | 2022.10.24 |