일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |
- 트리셋
- 스케일아웃
- 2178
- 파이널프로젝트
- 그래프탐색
- fatch
- CS
- 코테
- Spring JPA
- 해시
- springboot
- flyway
- 구현
- 운영체제
- CPU스케줄링
- findById
- 산업은행it
- 폰켓몬
- 컴퓨터구조
- 프로젝트
- 프로그래머스
- DB replication
- 백준
- 임베디드타입
- 산업은행청년인턴
- SpringBatch
- 트리맵
- 외래키제약조건위반
- BFS
- JPA
- Today
- Total
나 JAVA 봐라
Spring Boot + AWS RDS 연동 및 Read/Write 분산 처리 본문
https://yejin-code.tistory.com/13
[AWS RDS] Multi-AZ, Read Replica
프로젝트를 aws를 통해 배포하기로 했다. 그에 따라 DB도 RDS를 사용하기로 했는데, 이 후 Replication, Auto scale까지 고려하다보니 따져봐야할 것들이 많아졌다. 그렇다고 무턱대고 생성하기에는.. 추
yejin-code.tistory.com
(AWS에서 제공하는 RDS, Read Replica, DB replication에 대한 내용은 위의 링크를 참고해주세요!)
사전 작업으로 AWS RDS로 mysql를 띄운 후, read replica를 생성했다.
read/write 요청에 따른 분산 처리를 어떻게 할까 고민하면서 aws 서비스를 사용해보려 했지만 여러 이슈로 애플리케이션에서 분산 처리를 해주기로 하였다.
이번 글에서는 spring boot에서 read/write 요청에 따른 분산 처리를 해주는 방법에 대해 먼저 정리하고, 이 후 실제 어느 DB로 요청이 가는지 엔드포인트를 출력하는 것을 정리해보려고 한다.
1. 사전 작업으로 AWS RDS로 read replica까지 생성이 되었다면, application.yml 에서 DB에 대한 정보를 입력한다.
jdbc-url : DB에 접속할 수 있는 엔드포인트 (포트번호와 스키마까지 포함하기)
username : DB에 접속할 수 있는 username
password : DB에 접속할 수 있는 password
+ RDS를 통해 read replica를 생성했을 경우, read/write의 username, password가 동일하다.
2. Router 클래스 만들기
DataSourceRouter 클래스는 트랜잭션이 읽기 전용인지 여부를 확인하고 이에 따라 데이터 소스를 라우팅하여 read replica 또는 primary 로 요청을 전송한다.
+ 주석처리된 log 관련한 코드들은 로깅을 위해 잠시 사용했다.
package org.fastcampus.oruryclient.global.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.transaction.support.TransactionSynchronizationManager;
public class DataSourceRouter extends AbstractRoutingDataSource {
// private final Logger log = LoggerFactory.getLogger(getClass());
@Override
protected Object determineCurrentLookupKey() {
// @Transactionl(readOnly = true) 이면 True 이다.
boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
// if (readOnly) {
// log.info("readOnly = true, request to replica");
// }
// log.info("readOnly = false, request to source");
return readOnly ? "read" : "write";
}
}
3. DataSource 설정 클래스 만들기
DataSourceConfig 클래스는 주 데이터베이스(Primary)와 읽기 전용 데이터베이스(Read replica)에 대한 데이터 소스를 설정하고, 읽기 전용 여부에 따라 데이터 소스를 라우팅한다.
package org.fastcampus.oruryclient.global.config;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy;
import java.util.HashMap;
import javax.sql.DataSource;
import lombok.RequiredArgsConstructor;
@Configuration
@RequiredArgsConstructor
@EnableJpaRepositories
public class DataSourceConfig {
// Write replica 정보로 만든 DataSource
@Bean
@ConfigurationProperties(prefix = "spring.datasource.write")
public DataSource writeDataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
// Read replica 정보로 만든 DataSource
@Bean
@ConfigurationProperties(prefix = "spring.datasource.read")
public DataSource readDataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
// 읽기 모드인지 여부로 DataSource를 분기 처리
@Bean
@DependsOn({"writeDataSource", "readDataSource"})
public DataSource routeDataSource() {
DataSourceRouter dataSourceRouter = new DataSourceRouter();
DataSource writeDataSource = writeDataSource();
DataSource readDataSource = readDataSource();
HashMap<Object, Object> dataSourceMap = new HashMap<>();
dataSourceMap.put("write", writeDataSource);
dataSourceMap.put("read", readDataSource);
dataSourceRouter.setTargetDataSources(dataSourceMap);
dataSourceRouter.setDefaultTargetDataSource(writeDataSource);
return dataSourceRouter;
}
@Bean
@Primary
@DependsOn({"routeDataSource"})
public DataSource dataSource() {
return new LazyConnectionDataSourceProxy(routeDataSource());
}
}
이렇게 하면 분산 처리를 위한 작업은 끝이 났다.
실제로 read/write 요청에 따라서 분산 처리가 이루어지는지 확인해보기 위해 요청을 보내봤다.
아래는 review에 대한 Service 레이어다.
createReview 메소드는 write 요청/ getReviewDtosByGymId 메소드는 read 요청이다.
각자 어느 DB로 요청을 보냈는지 확인하기 위해 try-catch문을 추가하여 로그를 남기도록 했다.
@Slf4j
@RequiredArgsConstructor
@Service
public class ReviewService {
private final ReviewRepository reviewRepository;
private final DataSource lazyDataSource;
@Transactional
public void createReview(ReviewDto reviewDto) {
try (Connection connection = lazyDataSource.getConnection()) {
log.info("write url : {}", connection.getMetaData().getURL());
} catch (SQLException e) {
throw new RuntimeException(e);
}
reviewRepository.save(reviewDto.toEntity());
}
@Transactional(readOnly = true)
public List<ReviewDto> getReviewDtosByGymId(Long gymId, Long cursor, Pageable pageable) {
try (Connection connection = lazyDataSource.getConnection()) {
log.info("read url : {}", connection.getMetaData().getURL());
} catch (SQLException e) {
throw new RuntimeException(e);
}
List<Review> reviews = (cursor.equals(NumberConstants.FIRST_CURSOR))
? reviewRepository.findByGymIdOrderByIdDesc(gymId, pageable)
: reviewRepository.findByGymIdAndIdLessThanOrderByIdDesc(gymId, cursor, pageable);
return reviews.stream()
.map(ReviewDto::from).toList();
}
}
먼저 write 요청을 보내본다.
postman으로 리뷰를 생성해보았다.
리뷰가 생성되었다는 응답이 왔으니, 실제로 요청을 잘 보냈는지 로그를 확인해본다.
write url로 요청을 잘 보낸 것을 확인할 수 있다.
다음은 read 요청도 보내본다.
마찬가지로 postman으로 리뷰를 조회했다.
리뷰가 잘 조회되었다는 응답이 왔으니, 실제로 요청을 잘 보냈는지 로그를 확인한다.
read url로 요청을 잘 보낸 것을 확인할 수 있다.
참고
https://developer-been.tistory.com/27
[AWS + Spring] EB + Spring Boot + Aurora RDS 연동 및 Read/Write 분산 처리 (3)
저번 포스팅에선 bastion host로 ssh turnnel 방식을 사용하여 Intellij DataBase Tool에 Aurora Server를 연동 하고 Aurora Read Instance(읽기 전용)를 추가했었다. 이번 포스팅에선 Spring Boot에서 ReplicationRouting Configura
developer-been.tistory.com
[JPA] Read replica와 Write replica로 분산 처리하기
Read / Write DB와 JPA 함께 사용하기 🧐
velog.io
'Spring > Spring Boot' 카테고리의 다른 글
컨트롤러와 서비스에서 Embedded PK를 어떻게 교환? (0) | 2024.01.09 |
---|---|
Embeddable Type (0) | 2024.01.09 |