격벽 패턴: 세마포 대 threadPool
17235 단어 javamicroservicesprogramming
두 가지 구현이 다른 이유는 무엇입니까?
이 기사에서는 Spring과 Resilence4j를 언급할 것입니다.
이 기사를 읽고 있다면 벌크헤드 패턴, 해결하려는 문제 및 가장 일반적인 구현인 세마포어 기반 및 스레드 풀 기반에 대해 이미 알고 있을 것입니다.
적어도 나에게는 언제 세마포어 구현을 사용해야 하는지, 언제 threadPool 구현을 사용해야 하는지 깨닫는 것이 쉽지 않았습니다.
나는 Semaphore가 어떻게 작동하는지 알고 있으며 ThreadPool 패턴도 이해하고 있으므로 짧은 대답과 가장 분명한 것은 비동기 호출 수를 제한하기 위해 threadPool을 사용하고 동기 호출을 제한하기 위해 세마포어를 사용하는 것입니다.
그렇다면 어려운 부분은 왜였을까? 그 이유는 다음과 같은 질문이었습니다.
@Async와 @Bulkhead가 결합되었습니다.
@Bulkhead(name = "Service3", fallbackMethod = "futureFallback")
@Async
public CompletableFuture<String> doSomeWork() {
System.out.println("Excecuting service 3 - " + Thread.currentThread().getName());
Util.mockExternalServiceHttpCall(DELAY);
return CompletableFuture.completedFuture("ok");
}
Complete Code .
예, 두 주석을 함께 사용할 수 있습니다. 벌크헤드 세마포어 구성에 따라 제한된 수의 비동기 호출을 생성할 수 있습니다.
그러나 알아두면 유용한 몇 가지 의미가 있습니다.
SimpleAsyncTaskExecutor.
@Async 주석으로 메서드에 주석을 추가하면 Spring은 TaskExecutor 인터페이스의 다른 구현을 사용할 수 있습니다.
기본적으로 프레임워크는 SimpleAsyncTaslExecutor 을 사용합니다.
이 구현은 주석이 달린 메서드가 호출될 때마다 새 스레드를 생성합니다. 이 스레드는 재사용되지 않습니다.
이 접근 방식의 문제점은 세마포 카운터가 0(격벽이 가득 찼음)인 경우에도 새 스레드를 생성한다는 것입니다.
다음 스택 추적에서 볼 수 있듯이 프레임워크는 먼저 스레드를 생성한 다음 실행을 계속할 수 있는 사용 가능한 권한이 있는지 확인하는 격벽 패턴을 호출합니다. 세마포어 카운터가 cero이면 벌크헤드는 메서드 실행을 거부합니다.
Thread [_simpleAsyncTask1] (Suspended (breakpoint at line 18 in Service3))
Service3.doSomeWork() line: 18
Service3$$FastClassBySpringCGLIB$$6085f5a4.invoke(int, Object, Object[]) line: not available
MethodProxy.invoke(Object, Object[]) line: 218
CglibAopProxy$CglibMethodInvocation.invokeJoinpoint() line: 793
CglibAopProxy$CglibMethodInvocation(ReflectiveMethodInvocation).proceed() line: 163
CglibAopProxy$CglibMethodInvocation.proceed() line: 763
MethodInvocationProceedingJoinPoint.proceed() line: 89
BulkheadAspect.lambda$handleJoinPointCompletableFuture$0(ProceedingJoinPoint) line: 225
1457434357.get() line: not available
Bulkhead.lambda$decorateCompletionStage$1(Bulkhead, Supplier) line: 100
1251257755.get() line: not available
SemaphoreBulkhead(Bulkhead).executeCompletionStage(Supplier<CompletionStage<T>>) line: 557
BulkheadAspect.handleJoinPointCompletableFuture(ProceedingJoinPoint, Bulkhead) line: 223
BulkheadAspect.proceed(ProceedingJoinPoint, String, Bulkhead, Class<?>) line: 162
BulkheadAspect.lambda$bulkheadAroundAdvice$5eb13a26$1(ProceedingJoinPoint, String, Bulkhead, Class) line: 129
1746723773.apply() line: not available
1746723773(CheckedFunction0<R>).lambda$andThen$ca02ab3$1(CheckedFunction1) line: 265
1151489454.apply() line: not available
BulkheadAspect.executeFallBack(ProceedingJoinPoint, String, Method, CheckedFunction0<Object>) line: 139
==> here
BulkheadAspect.bulkheadAroundAdvice(ProceedingJoinPoint, Bulkhead) line: 128
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]
NativeMethodAccessorImpl.invoke(Object, Object[]) line: 62
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43
Method.invoke(Object, Object...) line: 566
AspectJAroundAdvice(AbstractAspectJAdvice).invokeAdviceMethodWithGivenArgs(Object[]) line: 634
AspectJAroundAdvice(AbstractAspectJAdvice).invokeAdviceMethod(JoinPoint, JoinPointMatch, Object, Throwable) line: 624
AspectJAroundAdvice.invoke(MethodInvocation) line: 72
CglibAopProxy$CglibMethodInvocation(ReflectiveMethodInvocation).proceed() line: 175
CglibAopProxy$CglibMethodInvocation.proceed() line: 763
ExposeInvocationInterceptor.invoke(MethodInvocation) line: 97
CglibAopProxy$CglibMethodInvocation(ReflectiveMethodInvocation).proceed() line: 186
CglibAopProxy$CglibMethodInvocation.proceed() line: 763
AnnotationAsyncExecutionInterceptor(AsyncExecutionInterceptor).lambda$invoke$0(MethodInvocation, Method) line: 115
1466446116.call() line: not available
AsyncExecutionAspectSupport.lambda$doSubmit$3(Callable) line: 278
409592088.get() line: not available
CompletableFuture$AsyncSupply<T>.run() line: 1700
==> here
SimpleAsyncTaskExecutor$ConcurrencyThrottlingRunnable.run() line: 286
Thread.run() line: 829
@Aync 및 @Bulkhead 주석이 달린 메서드를 60초 동안 호출하는 부하 테스트를 실행한 후 프로파일링tool 그림에서 애플리케이션이 생성된 514개의 스레드 중 34개만 사용했음을 확인할 수 있습니다. 이것은 분명히 자원 낭비를 나타냅니다.
ThreadPoolTaskExecutor
또 다른 옵션은 TreadPoolTaskExecutor 구현을 사용하는 것입니다.
이 구현을 사용하여 동일한 테스트를 실행한 후 생성된 스레드 수가 많이 감소했습니다(41).
그러나 이 접근 방식의 문제점은 불필요한 중복성을 사용하고 있다는 것입니다. 제 생각에는 스레드 풀과 세마포어를 함께 사용하는 것은 실질적인 이점이 없습니다.
결론.
비동기 호출을 제한하려면 @Async와 Bulkhead 세마포어의 조합 대신 Bulkhead threadPool 구현을 사용하십시오.
Reference
이 문제에 관하여(격벽 패턴: 세마포 대 threadPool), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/gabrielaramburu/bulkhead-pattern-semaphore-vs-threadpool-226p텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)