티스토리 뷰
다른 웹 서버로부터 요청 후 응답을 받을 때까지 기다리지 않아도 되는 서비스는 비동기 처리방식으로 구현합니다. 비동기 처리 방식을 이용하면, 사용자 입장에서는 멈춤 현상(Blocking)을 겪지 않고, 자연스럽게 웹 사용성이 증가됩니다. 이번 포스팅에서는 Springboot에서 비동기 처리 서비스를 개발하는 방법에 대해 알아보겠습니다.
Springboot Web 프로젝트 다운로드
https://start.spring.io/ 에서 디펜던시에 Spring Web을 추가하고, 스프링 부트 프로젝트를 다운로드합니다.
프로젝트의 build.gradle 파일을 열어보면, Spring Web 디펜던시가 추가된 것을 확인할 수 있습니다.
implementation 'org.springframework.boot:spring-boot-starter-web'
Springboot 비동기 처리 서비스 코드 작성하기
비동기 처리 서비스를 개발하는 예제로 Github API를 활용해 유저 정보를 가져오는 코드를 작성해보겠습니다. 먼저, User정보를 저장할 Entity 클래스를 아래와 같이 작성합니다.
package com.example.demo;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@JsonIgnoreProperties(ignoreUnknown = true)
public class User {
private String name;
private String blog;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getBlog() {
return blog;
}
public void setBlog(String blog) {
this.blog = blog;
}
@Override
public String toString() {
return "User [name=" + name + ", blog=" + blog + "]";
}
}
User 클래스는 이름과 블로그 주소를 저장하고 출력합니다. 다음으로, Github API를 호출하는 GithubLookupService 클래스를 작성합니다.
package com.example.demo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.concurrent.CompletableFuture;
@Service
public class GitHubLookupService {
private static final Logger logger = LoggerFactory.getLogger(GitHubLookupService.class);
private final RestTemplate restTemplate;
public GitHubLookupService(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}
@Async
public CompletableFuture<User> findUser(String user) throws InterruptedException {
logger.info("Looking up " + user);
String url = String.format("https://api.github.com/users/%s", user);
User results = restTemplate.getForObject(url, User.class);
// Artificial delay of 1s for demonstration purposes
Thread.sleep(1000L);
return CompletableFuture.completedFuture(results);
}
}
findUser()에서 RestTemplate를 활용하여 github API를 호출하고, 응답 정보를 User 객체로 수신받습니다. @Async는 붙은 메서드는 별도의 스레드가 생성되어 실행됩니다. 그리고, 반환 타입은 CompletableFuture <>인데, 이는 Async 메서드에서 반환할 때 사용해야 하는 타입입니다. GithubLookupService를 실행할 수 있는 main 함수를 작성해보겠습니다,
package com.example.demo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
@EnableAsync
@SpringBootApplication
public class DemoApplication implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(DemoApplication.class);
@Autowired
GitHubLookupService gitHubLookupService;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(3);
executor.setMaxPoolSize(3);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("GithubLookup-");
executor.initialize();
return executor;
}
@Override
public void run(String... args) throws Exception {
long start = System.currentTimeMillis();
CompletableFuture<User> page1 = gitHubLookupService.findUser("PivotalSoftware");
CompletableFuture<User> page2 = gitHubLookupService.findUser("CloudFoundry");
CompletableFuture<User> page3 = gitHubLookupService.findUser("Spring-Projects");
CompletableFuture.allOf(page1,page2,page3).join();
logger.info("Elapsed time: " + (System.currentTimeMillis() - start));
logger.info("--> " + page1.get());
logger.info("--> " + page2.get());
logger.info("--> " + page3.get());
}
}
@EnableAsync를 추가해야, Async 메서드가 스프링 부트의 스레드 풀을 통하여 백그라운드로 실행이 됩니다. 그리고 Executor를 반환하는 taskExecutor()를 정의하여, 스레드 풀에 대해 설정합니다. 만약 taskExecutor를 정의하지 않았다면, Springboot는 SimpleAsyncTaskExecutor를 생성하고 사용합니다. setCorePoolSize, setMaxPoolSize를 3으로 설정하여 스레드 개수를 3개로 설정합니다. github api를 요청하는 코드를 CommandLineRunner를 상속받아 run메서드에 구현하였습니다. API 요청 전 시간을 측정하고, 3개의 요청이 완료된 후 응답을 3개의 응답을 받는데 까지 걸리는 시간을 측정합니다. join()가 응답을 기다리를 역할을 합니다.
findUser()는 @Async 어노테이션을 사용하여 비동기식으로 처리를 지정하였으므로, PivotalSoftware, CloudFoundry, Spring-Projects 유저 정보 조회 api는 응답이 오지 않아도, 먼저 요청이 이루어집니다. 만약 동기식이었다면, PivotalSoftware에 대한 유저 정보를 요청하고 응답을 받은 뒤, 다음 코드가 진행됩니다. 이러한 결과를 통해 비동기 처리는 웹서버로 요청을 먼저 전송한 이후, 응답 결과를 수신하기 전에 다른 로직을 실행할 수 있으므로 작업의 효율을 높일 수 있습니다.
--> User [name=Pivotal Software, Inc., blog=http://pivotal.io]
--> User [name=Cloud Foundry, blog=https://www.cloudfoundry.org/]
--> User [name=Spring, blog=https://spring.io/projects]
'IT 기술' 카테고리의 다른 글
Springboot에서 Spring MVC를 활용한 웹 컨텐츠 만들기 (1) | 2022.08.26 |
---|---|
Springboot에서 WebSocket으로 메세지 송수신하기 (0) | 2022.08.26 |
Springboot에서 Docker MongoDB 연동하기 (0) | 2022.08.26 |
구글 키워드 검색량 조회하는 방법 (3) | 2022.08.25 |
Springboot에서 Database의 Transaction 관리하기 (1) | 2022.08.25 |
- Total
- Today
- Yesterday
- notion
- springboot rest api 서버
- iframe 태그 찾기
- codesmell 유형
- 스프링부트실행
- SPA
- 테스팅 자동화
- 코드스멜 유형
- AWS
- TCP/UDP
- springboot build
- springboot 실행
- oracle 메모리
- OSI 7계층
- 클린코드작성법
- python
- 디자인패턴
- selenium
- token
- springboot restapi
- API Gateway 캐싱
- 클린코드작성원칙
- 멀티코어 멀티프로세서
- 디자인패턴 구조패턴
- MPA
- oracle pga sga
- C++
- notion 업무일정관리
- 스프링부트 restapi
- 스프링부트빌드
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |