Hibernate의 경우 SessionFactory가 RDBMS 와 1:1 매핑이 되는 관계라 위와 같이 멀티 서버의 경우에는 중간에 Proxy 서버를 두던가, MySQL Replication Driver 를 이용해야 합니다.
Proxy 서버를 둔다는 것은 Clustering 을 구성하는 것과 같으니 Proxy 서버 주소만 알면 되지만, Replication Driver를 사용 시에는 Master / Slave 서버별로 작업의 특성에 따라 구분해 줘야 합니다.
즉 Master 서버는 Read/Write를 할 수 있지만, Slave 서버는 Read 작업만 수행해야 합니다. 이렇게 하려면 작업 전에 Connection 의 isReadOnly 속성을 변경하여, 원하는 종류의 서버를 선택하게끔 해야 합니다.
이를 위해 Spring AOP 를 이용하여, 작업 시작 전에 작업 종류에 따라 Connection 속성을 변경하는 interceptor 를 제작합니다.
ConnectionInterceptor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Aspect | |
@Component | |
public class ConnectionInterceptor { | |
private static final Logger log = LoggerFactory.getLogger(ConnectionInterceptor.class); | |
private static final boolean isTraceEnabled = log.isTraceEnabled(); | |
@Autowired | |
private SessionFactory sessionFactory; | |
@Around("@annotation(kr.hconnect.data.mysql.ReadOnlyConnection)") | |
public Object proceed(ProceedingJoinPoint pjp) throws Throwable { | |
log.debug("읽기전용 작업을 수행하기 위해 현 connection를 readonly로 설정합니다..."); | |
SessionImpl session = (SessionImpl) sessionFactory.getCurrentSession(); | |
Connection connection = session.connection(); | |
boolean autoCommit = connection.getAutoCommit(); | |
boolean readOnly = connection.isReadOnly(); | |
try { | |
// MySQL SLAVE 서버에 접속하기 위해 Connection 속성을 설정합니다. | |
connection.setAutoCommit(false); | |
connection.setReadOnly(true); | |
// @ReadOnlyConnection이 선언된 메소드를 실행합니다. | |
return pjp.proceed(); | |
} finally { | |
connection.setAutoCommit(autoCommit); | |
connection.setReadOnly(readOnly); | |
log.debug("읽기전용 작업을 수행하고, connection의 원래 설정으로 재설정했습니다."); | |
} | |
} | |
} |
line 11 에 있는 @Arount 를 보시면, ReadOnlyConnection 이라는 annotation 이 있는 메소드를 intercept 하도록 합니다. 이 메소드는 readonly 작업을 뜻하므로 connection의 readonly 값을 true 로 하여, MySQL 의 Slave 서버에 접속하도록합니다.
ReadOnlyConnection annotation은 메소드에만 적용되도록 합니다.
ReadOnlyConnection.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Target( { ElementType.METHOD } ) | |
@Retention( RetentionPolicy.RUNTIME ) | |
@Inherited | |
public @interface ReadOnlyConnection { | |
} |
자 이제 테스트를 위한 서비스를 제작해 봅시다.
SimpleEntityServiceImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Slf4j | |
@Service | |
@SuppressWarnings("unchecked") | |
public class SimpleEntityServiceImpl implements SimpleEntityService { | |
@Autowired SessionFactory sessionFactory; | |
@Transactional( readOnly = true ) | |
@ReadOnlyConnection | |
@Override | |
public List<SimpleEntity> findAll() { | |
Session session = sessionFactory.getCurrentSession(); | |
return (List<SimpleEntity>)session.createCriteria(SimpleEntity.class).list(); | |
} | |
} |
이를 통해 Master/Slave 를 구분하고, 여러대의 Slave 도 Replication Driver 가 RoundRobin 방식으로 서버를 지정해주니, Application 개발자는 RDBMS 환경에 크게 신경 쓰지 않고, 자신의 분야에만 집중 할 수 있게 될 겁니다^^
또 한가지 SessionFactory가 하나로 유지되므로, 2nd Cache 도 하나가 되므로, 성능상의 잇점과 Cache와의 불일치에 대해 다른 방식보다 어느 정도 잇점이 있을 것입니다
댓글 없음:
댓글 쓰기