본문 바로가기

TIL

TIL) pull or Merge 취소, SFTP 서버제공 사이트, CONTENT_DISPOSITION, @Async

1. pull or Merge 취소

 

실수로 pull 받아 버린 경우

 

git reset --hard ORIGIN_HEAD

 

 

 

2. SFTP 서버제공 사이트

 

이번에 프로젝트에서 SFTP 를 사용했는데, 테스트시 SFTP 원격 서버가 필요했는데, 제공하는 사이트가 있었다.

https://www.sftp.net/public-online-sftp-servers

 

Free Public SFTP Servers - SFTP.net

Do you want to quickly test your SFTP client and don't have your own SFTP server? Try one of these publicly accessible SFTP servers. Most of them only allow read-only access. If you need to upload data as well, choose an SFTP server and download/install if

www.sftp.net

 

 

 

3. CONTENT_DISPOSITION

http 응답 헤더에 CONTENT_DISPOSITION 옵션값은 사용자가 응답받은 데이터를 어떻게 표시할 것인지를 정의한다.

 

기본값은 inline 이며 화면에 표시해준다.

attachment 는 사용자가 로컬에 다운받을 수 있게 해준다.

 

val httpHeaders = HttpHeaders().apply {
    this[HttpHeaders.CONTENT_DISPOSITION] = "attachment; filename=$newFileName"
}

 

 

 

 

4. Async

스프링에서 메서드에 @Async 에너테이션을 붙이면 그 메서드는 비동기 처리가 된다.

 

그에 앞서, 비동기 처리를 4가지 방식으로 짜 보았다.

  • 그냥 Thread 객체를 이용한 방법
  • ExecutorSerivce 를 이용한 방법
  • @Async 에너테이션을 이용한 방법
  • @Async 에너테이션 + ThreadPoolTaskExecutor <- 이 방법이 제일 좋다.
@Service
class MultiThread {

    val logger = LoggerFactory.getLogger(MultiThread::class.java)

    fun printNumber(x: String) {
        Thread {
            Thread.sleep(SplittableRandom().nextLong(3000))
            logger.info("[${Thread.currentThread().name}] $x")
        }.start()
    }

    fun printNumber2(x: String) {
        val executorService: ExecutorService = Executors.newFixedThreadPool(10)
        
        executorService.submit {
            Thread.sleep(SplittableRandom().nextLong(3000))
            logger.info("[${Thread.currentThread().name}] $x")
        }
    }

    @Async
    fun printNumber3(x: String) {
        Thread.sleep(SplittableRandom().nextLong(3000))
        logger.info("[${Thread.currentThread().name}] $x")
    }

    @Async("threadPoolTaskExecutor")
    fun printNumber4(x: String) {
        Thread.sleep(SplittableRandom().nextLong(3000))
        logger.info("[${Thread.currentThread().name}] $x")
    }
}

@Configuration
@EnableAsync
class AsyncConfig {
    @Bean(name = ["threadPoolTaskExecutor"])
    fun threadPoolTaskExecutor(): Executor =
        ThreadPoolTaskExecutor().apply {
            corePoolSize = 3
            maxPoolSize = 30
            setQueueCapacity(10)
            setThreadNamePrefix("TOMAS Executor-")
            initialize()
    }
}

'

 

4번째 방법의 장점

  • 1, 3 번 방법은 Thread 의 갯수가 관리되지 않는다. 작업이 많으면 스레드가 무한정 늘어난다.
  • 1, 2 번 방법은 동기/비동기 메서드로 바꿀 때 코드의 수정이 필요하다.

4번째 방법은 동기로 처리하고 싶다면 에너테이션을 빼고, 비동기면 붙이면 된다. 코드를 수정할 필요가 없다는 것이다.

그리고 ThreadPoolTaskExecutor 객체를 제공하는 방법으로 스레드의 갯수도 관리가 가능하다.

 

 

@Async 동작 방식 : 프록시

@Async 가 붙은 메서드의 클래스를 스프링은 AOP 를 이용하여 프록시 객체로 감싼다.

이 프록시 객체에서 비동기의 코드가 붙는다.

 

이를 RTW (RunTime Weaving) 이라 한다. Weaving 이란, Aspect 를 Method 와 엮는, 즉 프록시 객체와 실제 메서드를 엮는것을 말한다.

 

프록시로 만들어지기에, @Async 에서 제약사항이 생긴다.

  • private 메서드는 적용되지 않음. (프록시 밖에서 접근 불가능)
  • 클래스의 다른 메서드가 @Async 가 붙은 메서드를 호출하는 것은 적용되지 않음. (객체 내부에서 타는 로직은 프록시를 타지 않음)
  • 런티임시 동작이 늘어나므로, 약간의 성능저하.

 

 

@Async 리턴타입

void 가 아니면 Future 로 감싸서 리턴한다.

 

 

 

 

@Async 

비동기로 처리되는 해당 thread만 죽는다. 외부에서는 여러 스레드 중 어떤 스레드가 죽었는지 알 수 없다. 

따라서, 로그로 남기기 위해서는 핸들러를 만들어줘야 한다.

 

class AsyncConfigurerConfiguration : AsyncConfigurer {

	...
    
    override fun getAsyncUncaughtExceptionHandler(): AsyncUncaughtExceptionHandler {
        return AsyncUncaughtExceptionHandler { ex, method, param -> println(ex) }
    }
}

 

 

 

 

@Async AOP Proxy 제약사항 해결

위에서 AOP 방식을 사용함으로서 발생하는 제약사항 3가지를 봤었다.

프록시 객체 라는 중간 매체를 사용함으로 발생하는 문제였다.

 

그렇다면, @Async 코드가 붙은 작업을 비동기로 처리해주는 작업을 프록시 객체를 통해 실행이 아닌,

코드를 직접 붙여넣는다면 해당 문제는 발생하지 않는다.

 

Spring Aop 를 Proxy 모드가 아닌 AspectJ 모드로 사용하면, 컴파일타임에 바이너리파일을 필요한 부분에 Weaving 한다.

컴파일 타임에 이를 진행하는 것을 CTW (CompileTime Weaving) 이라 한다.

 

 

참고 : http://dveamer.github.io/java/SpringAsyncAspectJ.html

 

Dveamer

현실에서 살고 있지만 이상에 대한 꿈을 버리지 못한 몽상가의 홈페이지 입니다. 개인적인 기록을 주 목적으로 하며 일상과 프로그래밍 관련 글을 포스팅합니다.

dveamer.github.io

 

https://jeong-pro.tistory.com/187

 

How does @Async work? @Async를 지금까지 잘 못 쓰고 있었습니다(@Async 사용할 때 주의해야 할 것, 사용법

@Async in Spring boot 스프링 부트에서 개발자에게 비동기 처리를 손쉽게 할 수 있도록 다양한 방법을 제공하고 있다. 대세는 Reactive stack, CompletableFuture를 쓰겠으나 역시 가장 쉬운 방법으로는 @Async..

jeong-pro.tistory.com