-
DB 이중화하기 with Spring AOP개발 기록 2023. 3. 19. 15:12728x90
목표
데이터 베이스를 이중화하여 슬레이브에서 select를 하려고 한다.
코드는 github에 있다.(https://github.com/neunggu/score.git)
환경
java 17
spring boot 2.7.6
spring-boot-starter-aop 사용
방법
1. 마스터, 슬레이브 2개의 DataSource를 생성해 AbstractRoutingDataSource에 담는다.
(세션에 접속할 db를 선정해 두고 사용할 예정)
(determineCurrentLookupKey 구현 필요)
@Configuration public class DatabaseConfig { @Value("${db.driver-class-name}") private String driver; @Value("${db.username}") private String user; @Value("${db.password}") private String pw; @Value("${db.master.url}") private String masterUrl; @Value("${db.slave.url}") private String slaveUrl; @Bean public DataSource createRouterDatasource() { AbstractRoutingDataSource routingDataSource = new RoutingDataSource(); Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put("current:master", createDataSource(driver, masterUrl, user, pw)); targetDataSources.put("current:slave", createDataSource(driver, slaveUrl, user, pw)); routingDataSource.setTargetDataSources(targetDataSources); return routingDataSource; } private DataSource createDataSource(String driver, String url, String user, String password) { HikariDataSource dataSource = new HikariDataSource(); dataSource.setDriverClassName(driver); dataSource.setConnectionInitSql("SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci;"); dataSource.setUsername(user); dataSource.setPassword(password); dataSource.setJdbcUrl(url); dataSource.setMaxLifetime(30000); return dataSource; } }
public class RoutingDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { Object dbKey; if(RequestContextHolder .getRequestAttributes() == null || RequestContextHolder.getRequestAttributes() .getAttribute("db_key", RequestAttributes.SCOPE_SESSION) == null) { dbKey = "master"; } else { dbKey = RequestContextHolder .getRequestAttributes() .getAttribute("db_key", RequestAttributes.SCOPE_SESSION); } return "current:" + dbKey; } }
2. 세션에 선택된 db를 셋팅하는 유틸 생성.
(3번에서 만들 aspect에서만 사용할 것이어서 굳이 클래스로 뺄 필요는 없다.)
@Component public class DBSessionConfigUtil { public void setDbKey(HttpSession httpSession, String dbKey) { if(httpSession != null && httpSession.getAttribute("db_key") != null ) { String dbSessionkey = (String) httpSession.getAttribute("db_key"); if(!dbSessionkey.equals(dbKey)) { httpSession.setAttribute("db_key", dbKey); } } else { httpSession.setAttribute("db_key", dbKey); } } }
3. Aspect 생성
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface SlaveDB { }
@Aspect @Component @RequiredArgsConstructor public class DB{ private final HttpSession httpSession; private final DBSessionConfigUtil dbSessionUtil; @Around("@annotation(SlaveDB)") public Object setSlave(ProceedingJoinPoint joinPoint) { Object result = null; try { dbSessionUtil.setDbKey(httpSession, "slave"); result = joinPoint.proceed(); } catch (Throwable e) { } finally { dbSessionUtil.setDbKey(httpSession, "master"); } return result; } }
4. 필요한 곳에 AOP 사용
@Mapper public interface ScoreMapper { @SlaveDB @Select("SELECT * FROM score WHERE user_id = #{userId}") ScoreEntity findById(String userId); ... }
결론: AOP를 이용하면 편한 점들이 꽤 있다.
728x90반응형'개발 기록' 카테고리의 다른 글
Spring Boot[2.x.x] is not compatible with this Spring Cloud release train (0) 2023.04.03 java.lang.IllegalStateException: Unable to find a @SpringBootConfiguration (0) 2023.03.27 Spring AOP에서 만난 예상치 못한 오류 --enable-preview (0) 2023.03.15 r2dbc-pool. R2dbcNonTransientResourceException: Connection validation failed (0) 2023.03.14 Webflux + Jwt(es256) (1) 2023.03.14