Spring 비동기 호출 에서 문맥 을 전달 하 는 방법
비동기 호출 은 동기 호출 에 비해 동기 호출 이란 프로그램 이 예 정 된 순서에 따라 한 걸음 씩 실행 되 고 모든 단 계 는 이전 단계 가 실 행 된 후에 야 실 행 될 수 있 으 며 비동기 호출 은 이전 프로그램 이 실 행 될 때 까지 기다 리 지 않 아 도 실 행 될 수 있다 는 것 을 말한다.비동기 호출 이란 프로그램 이 실 행 될 때 실 행 된 반환 값 을 기다 리 지 않 고 뒤의 코드 를 계속 실행 할 수 있 는 것 을 말한다.Google 의 응용 서비스 에서 많은 업무 논리 적 인 실행 작업 은 동기 화 되 돌아 오지 않 아 도 됩 니 다(예 를 들 어 메 일 발송,불필요 한 데이터 시트 등).비동기 적 으로 만 실행 하면 됩 니 다.
본 고 는 Spring 응용 에서 어떻게 비동기 호출 을 실현 하 는 지 를 소개 할 것 이다.비동기 호출 과정 에서 스 레 드 컨 텍스트 정보의 손실 이 발생 할 수 있 습 니 다.우 리 는 스 레 드 컨 텍스트 정보의 전달 을 어떻게 해결 해 야 합 니까?
Spring 응용 에서 비동기 실현
Spring 은 작업 스케줄 링 과 비동기 적 인 방법 수행 에 주해 지원 을 제공 합 니 다.방법 이나 클래스 에@Async 주 해 를 설정 하면 다른 방법 으로 호출 할 수 있 습 니 다.호출 자 는 호출 시 즉시 되 돌아 오고 호출 된 방법의 실제 실행 은 Spring 의 TaskExecutor 에 맡 겨 집 니 다.그래서 주 해 된 방법 이 호출 될 때 새로운 스 레 드 에서 실 행 됩 니 다.이 를 호출 하 는 방법 은 원래 스 레 드 에서 실 행 됩 니 다.그러면 차단 을 피하 고 작업 의 실시 성 을 확보 할 수 있 습 니 다.
도입 의존
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
스프링 관련 의존 도 를 도입 하면 된다.입구 류
@SpringBootApplication
@EnableAsync
public class AsyncApplication {
public static void main(String[] args) {
SpringApplication.run(AsyncApplication.class, args);
}
```
`@EnableAsync` , `@Async` 。
####
:
```java
@RestController
@Slf4j
public class TaskController {
@Autowired
private TaskService taskService;
@GetMapping("/task")
public String taskExecute() {
try {
taskService.doTaskOne();
taskService.doTaskTwo();
taskService.doTaskThree();
} catch (Exception e) {
log.error("error executing task for {}",e.getMessage());
}
return "ok";
}
}
TaskService 를 호출 하여 세 가지 비동기 방법 을 실행 합 니 다.서비스 방법
@Component
@Slf4j
//@Async
public class TaskService {
@Async
public void doTaskOne() throws Exception {
log.info(" ");
long start = System.currentTimeMillis();
Thread.sleep(1000);
long end = System.currentTimeMillis();
log.info(" , :" + (end - start) + " ");
}
@Async
public void doTaskTwo() throws Exception {
log.info(" ");
long start = System.currentTimeMillis();
Thread.sleep(1000);
long end = System.currentTimeMillis();
log.info(" , :" + (end - start) + " ");
}
@Async
public void doTaskThree() throws Exception {
log.info(" ");
long start = System.currentTimeMillis();
Thread.sleep(1000);
long end = System.currentTimeMillis();
log.info(" , :" + (end - start) + " ");
}
}
@Async 는 클래스 에 사용 할 수 있 습 니 다.이 종 류 를 표시 하 는 모든 방법 은 비동기 적 인 방법 이 고 특정한 방법 에 단독으로 사용 할 수 있 습 니 다.모든 방법 은 sleep 1000 ms 입 니 다.결과 전시
실행 결 과 는 다음 과 같 습 니 다.
TaskService 의 세 가지 방법 은 비동기 로 실 행 된 것 을 볼 수 있 습 니 다.인터페이스의 결 과 는 빠르게 되 돌아 오고 로그 정 보 는 비동기 로 출력 됩 니 다.비동기 호출,새로운 스 레 드 호출 방법 을 열 어 주 스 레 드 에 영향 을 주지 않 습 니 다.비동기 방법의 실제 집행 은 Spring 의 TaskExecutor 에 게 맡 겼 다.
Future:비동기 실행 결과 가 져 오기
위의 테스트 에서 우 리 는 주 호출 방법 이 호출 방법 이 실 행 될 때 까지 기다 리 지 않 고 현재 의 임 무 를 끝 낸 것 을 발견 할 수 있다.호출 된 세 가지 방법 이 모두 실행 되 었 는 지 알 고 싶다 면 어떻게 해 야 합 니까?다음은 비동기 리 셋 을 사용 할 수 있 습 니 다.
비동기 리 셋 은 모든 호출 된 방법 을 하나의 Future 형식의 값 으로 되 돌려 주 는 것 입 니 다.Spring 에 서 는 Future 인터페이스의 하위 클래스 를 제공 합 니 다:AsyncResult,그래서 우 리 는 AsyncResult 형식의 값 을 되 돌려 줄 수 있 습 니 다.
public class AsyncResult<V> implements ListenableFuture<V> {
private final V value;
private final ExecutionException executionException;
//...
}
AsyncResult 는 ListenableFuture 인 터 페 이 스 를 실 현 했 습 니 다.이 대상 내부 에는 두 가지 속성 이 있 습 니 다.반환 값 과 이상 정보 입 니 다.
public interface ListenableFuture<T> extends Future<T> {
void addCallback(ListenableFutureCallback<? super T> var1);
void addCallback(SuccessCallback<? super T> var1, FailureCallback var2);
}
ListenableFuture 인 터 페 이 스 는 Future 에서 계승 되 었 고 이 를 바탕 으로 리 셋 방법의 정 의 를 추가 했다.Future 인터페이스 정 의 는 다음 과 같 습 니 다.
public interface Future<V> {
//
boolean cancel(boolean mayInterruptIfRunning);
//
boolean isCancelled();
//
V get() throws InterruptedException, ExecutionException;
// , , true, , false
boolean isDone();
// get() ,
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
\#get()방법 은 실행 할 때 리 셋 결 과 를 기다 리 고 기 다 려 야 합 니 다.시간 초과 시간 을 설정 하지 않 으 면 작업 이 완 료 될 때 까지 차단 합 니 다.우 리 는 시간 초과 시간 을 설정 하면 현재 작업 이 너무 오래 실 행 된 상황 에서 현재 작업 을 중단 하고 스 레 드 를 방출 할 수 있 습 니 다.그러면 자원 을 계속 차지 하지 않 습 니 다.\#cancel(boolean)방법,인 자 는 boolean 형식의 값 입 니 다.현재 실행 중인 작업 을 중단 할 수 있 는 지 여 부 를 입력 하 는 데 사 용 됩 니 다.인자 가 true 이 고 현재 작업 이 완료 되 지 않 았 다 면 현재 작업 을 중단 할 수 있 음 을 설명 합 니 다.그러면 true 로 돌아 갑 니 다.현재 작업 이 실행 되 지 않 았 다 면 인자 가 true 든 false 든 반환 값 은 true 입 니 다.현재 작업 이 완료 되 었 다 면 인자 가 true 든 false 든 반환 값 은 false 입 니 다.현재 작업 이 완료 되 지 않 았 고 인자 가 false 라면 반환 값 도 false 입 니 다.즉:
4.567917.퀘 스 트 가 아직 실행 되 지 않 았 다 면 퀘 스 트 를 취소 하려 면 반드시 true 로 돌아 가 야 합 니 다.매개 변수 와 무관 합 니 다4.567917.만약 에 작업 이 완성 되 었 다 면 임 무 는 취소 할 수 없 기 때문에 이때 반환 값 은 모두 false 이 고 매개 변수 와 무관 합 니 다4.567917.작업 이 실행 중이 라면 작업 을 취소 할 지 여 부 는 매개 변수 가 중단 을 허용 하 는 지 여 부 를 봅 니 다(true/false)비동기 방법 반환 값 의 실현 가 져 오기
public Future<String> doTaskOne() throws Exception {
log.info(" ");
long start = System.currentTimeMillis();
Thread.sleep(1000);
long end = System.currentTimeMillis();
log.info(" , :" + (end - start) + " ");
return new AsyncResult<>(" , " + (end - start) + " ");
}
//... ,
task 방법의 반환 값 을 Future
@GetMapping("/task")
public String taskExecute() {
try {
Future<String> r1 = taskService.doTaskOne();
Future<String> r2 = taskService.doTaskTwo();
Future<String> r3 = taskService.doTaskThree();
while (true) {
if (r1.isDone() && r2.isDone() && r3.isDone()) {
log.info("execute all tasks");
break;
}
Thread.sleep(200);
}
log.info("
" + r1.get() + "
" + r2.get() + "
" + r3.get());
} catch (Exception e) {
log.error("error executing task for {}",e.getMessage());
}
return "ok";
}
비동기 방법 을 호출 한 후 순환 을 통 해 비동기 방법 이 실행 되 었 는 지 여 부 를 판단 할 수 있다.결 과 는 우리 가 예상 한 바 와 같이 future 에서 얻 은 것 은 AsyncResult 에서 돌아 온 문자열 입 니 다.
스 레 드 탱크 설정
앞 은 가장 간단 한 사용 방법 입 니 다.기본 TaskExecutor 를 사용 합 니 다.사용자 정의 Executor 를 사용 하려 면@Configuration 주석 과 결합 하여 설정 할 수 있 습 니 다.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
public class TaskPoolConfig {
@Bean("taskExecutor") // bean ,
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10); // ( )
executor.setMaxPoolSize(20); //
executor.setQueueCapacity(200); //
executor.setKeepAliveSeconds(60); // ( : )
executor.setThreadNamePrefix("taskExecutor-"); //
//
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return executor;
}
}
스 레 드 탱크 의 설정 이 매우 유연 하여 핵심 스 레 드 수,최대 스 레 드 수 등 속성 을 설정 합 니 다.그 중에서 rejection-policy 는 스 레 드 탱크 가 최대 스 레 드 수 에 이 르 렀 을 때 새로운 작업 을 어떻게 처리 합 니까?선택 가능 한 정책 은 Caller Blocks Policy,Caller Runs Policy 등 이 있 습 니 다.CALLER_RUNS:새 스 레 드 에서 작업 을 수행 하지 않 고 호출 자가 있 는 스 레 드 에서 실 행 됩 니 다.스 레 드 탱크 의 설정 이 적용 되 는 지 확인 합 니 다.TaskService 에서 현재 스 레 드 이름 을 인쇄 합 니 다.
public Future<String> doTaskOne() throws Exception {
log.info(" ");
long start = System.currentTimeMillis();
Thread.sleep(1000);
long end = System.currentTimeMillis();
log.info(" , :" + (end - start) + " ");
log.info(" {}", Thread.currentThread().getName());
return new AsyncResult<>(" , " + (end - start) + " ");
}
결 과 를 통 해 스 레 드 탱크 에 설 치 된 스 레 드 이름 접두사 가 유효 합 니 다.Spring@Async 비동기 스 레 드 사용 과정 에서 주의해 야 할 것 은 다음 과 같은 용법 이@Async 를 무효 화 할 수 있 습 니 다.
마이크로 서비스 구조 에서 의 요청 은 여러 개의 마이크로 서비스 와 관련 될 때 가 많다.또는 한 서비스 에 여러 가지 처리 방법 이 있 는데 이런 방법 은 비동기 적 인 방법 일 수 있다.요청 한 경로,사용자 의 유일한 userId 와 같은 일부 스 레 드 컨 텍스트 정 보 는 요청 에서 계속 전 달 됩 니 다.만약 어떤 처리 도 하지 않 는 다 면,우 리 는 이 정 보 를 정상적으로 얻 을 수 있 는 지 를 봅 시다.
@GetMapping("/task")
public String taskExecute() {
try {
Future<String> r1 = taskService.doTaskOne();
Future<String> r2 = taskService.doTaskTwo();
Future<String> r3 = taskService.doTaskThree();
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
log.info(" {}, {}, :{}", Thread.currentThread().getName(), request.getMethod(), request.getRequestURL().toString());
while (true) {
if (r1.isDone() && r2.isDone() && r3.isDone()) {
log.info("execute all tasks");
break;
}
Thread.sleep(200);
}
log.info("
" + r1.get() + "
" + r2.get() + "
" + r3.get());
} catch (Exception e) {
log.error("error executing task for {}", e.getMessage());
}
return "ok";
}
Spring Boot Web 에서 Request ContextHolder 를 통 해 request 를 쉽게 얻 을 수 있 습 니 다.인터페이스 방법 에서 요청 한 방법 과 요청 한 경 로 를 출력 합 니 다.
public Future<String> doTaskOne() throws Exception {
log.info(" ");
long start = System.currentTimeMillis();
Thread.sleep(1000);
long end = System.currentTimeMillis();
log.info(" , :" + (end - start) + " ");
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
log.info(" {}, {}, :{}", Thread.currentThread().getName(), request.getMethod(), request.getRequestURL().toString());
return new AsyncResult<>(" , " + (end - start) + " ");
}
동시에 TaskService 에서 요청 한 정 보 를 출력 할 수 있 는 지 검증 합 니 다.프로그램 을 실행 합 니 다.결 과 는 다음 과 같 습 니 다.TaskService 에서 모든 비동기 스 레 드 방법 으로 RequestContextHolder 의 요청 정 보 를 가 져 올 때 빈 포인터 이상 을 알 렸 습 니 다.이것 은 요청 한 상하 문 정보 가 비동기 방법의 라인 에 전달 되 지 않 았 음 을 설명 한다.RequestContextHolder 의 실현 은 현재 스 레 드 의 request 를 저장 하 는 ThreadLocal 두 개가 있 습 니 다.
// request
private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
new NamedThreadLocal<RequestAttributes>("Request attributes");
// request
private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
new NamedInheritableThreadLocal<RequestAttributes>("Request context");
다시 보기\#getRequestAttributes()방법 은 ThreadLocal 의 값 을 직접 가 져 오 는 것 과 같 습 니 다.그러면 매번 가 져 오 는 Request 는 이 요청 의 request 입 니 다.어떻게 문맥 정 보 를 비동기 스 레 드 로 전달 합 니까?Spring 의 Thread PoolTaskExecutor 는 속성 TaskDecorator 를 설정 합 니 다.TaskDecorator 는 리 셋 인터페이스 로 장식 기 모드 를 사용 합 니 다.장식 모델 은 동태 적 으로 한 대상 에 게 추가 적 인 기능 을 추가 하 는 것 으로 기능 을 증가 하 는 데 있어 장식 모델 은 서브 클래스 를 생 성 하 는 것 보다 더욱 유연 하 다.따라서 Task Decorator 는 주로 작업 호출 시 실행 컨 텍스트 를 설정 하거나 작업 수행 에 감시/통 계 를 제공 합 니 다.
public interface TaskDecorator {
Runnable decorate(Runnable runnable);
}
\#decorate 방법,주어진 Runnable 을 장식 하고 포 장 된 Runnable 을 되 돌려 실제 실행 할 수 있 도록 합 니 다.다음은 스 레 드 컨 텍스트 를 복사 한 Task Decorator 를 정의 합 니 다.
import org.springframework.core.task.TaskDecorator;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
public class ContextDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
RequestAttributes context = RequestContextHolder.currentRequestAttributes();
return () -> {
try {
RequestContextHolder.setRequestAttributes(context);
runnable.run();
} finally {
RequestContextHolder.resetRequestAttributes();
}
};
}
}
현재 스 레 드 의 context 를 지정 한 Runnable 로 장식 하고 마지막 으로 현재 스 레 드 컨 텍스트 를 초기 화 합 니 다.온라인 탱크 설정 에서 리 셋 된 Task Decorator 속성 을 추가 하 는 설정:
@Bean("taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(200);
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("taskExecutor-");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(60);
// TaskDecorator
executor.setTaskDecorator(new ContextDecorator());
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
위 설정 을 통 해 저 희 는 서 비 스 를 다시 실행 하고 인 터 페 이 스 를 방문 합 니 다.콘 솔 로그 정 보 는 다음 과 같 습 니 다.결 과 를 통 해 알 수 있 듯 이 스 레 드 의 문맥 정 보 는 성공 적 으로 전달 되 었 다.
작은 매듭
본 고 는 예제 와 결합 하여 Spring 에서 비동기 방법 을 실현 하고 비동기 방법의 반환 치 를 얻 었 다.스프링 스 레 드 탱크 를 배치 하 는 방식 도 소개 했다.마지막 으로 비동기 다 중 스 레 드 에서 스 레 드 컨 텍스트 정 보 를 전달 하 는 방법 을 소개 합 니 다.스 레 드 상하 문 전달 은 분포 식 환경 에서 자주 사용 된다.예 를 들 어 분포 식 체인 추적 에서 요청 한 TraceId,SpanId 가 필요 하 다.쉽게 말 하면 전달 해 야 할 정 보 는 서로 다른 스 레 드 에 있 을 수 있다.비동기 방법 은 우리 가 일상적인 개발 에서 다 중 스 레 드 로 업무 논 리 를 처리 하 는 것 으로 이런 업무 논 리 는 엄격 한 집행 순 서 를 필요 로 하지 않 는 다.비동기 로 문 제 를 해결 하 는 동시에 비동기 다 중 스 레 드 방식 도 사용 해 야 한다.
총결산
이상 은 이 글 의 모든 내용 입 니 다.본 고의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 참고 학습 가 치 를 가지 기 를 바 랍 니 다.여러분 의 저희 에 대한 지지 에 감 사 드 립 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
thymeleaf로 HTML 페이지를 동적으로 만듭니다 (spring + gradle)지난번에는 에서 화면에 HTML을 표시했습니다. 이번에는 화면을 동적으로 움직여보고 싶기 때문에 입력한 문자를 화면에 표시시키고 싶습니다. 초보자의 비망록이므로 이상한 점 등 있으면 지적 받을 수 있으면 기쁩니다! ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.