반응형
1차 시도
master_server db 를 만들어서
마스터가 될 ip 정보를 넣어두었다
그리고 서버들은 1분마다
아래 체크를 해서
본인이 마스터인지 확인을 했다
@Transactional(isolation = Isolation.SERIALIZABLE)
fun checkIfMasterServer(): Boolean {
val masterServerEntity = masterServerRepository.findByIdOrNull(1) ?: MasterServerEntity(1)
val expireAt = masterServerEntity.expireAt
if (expireAt?.isBefore(LocalDateTime.now()) != false) {
masterServerEntity.ip = InetAddress.getLocalHost().hostAddress
masterServerEntity.expireAt = LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES).plusMinutes(2)
masterServerRepository.save(masterServerEntity)
println("오 마스터 자리 있당 : ${InetAddress.getLocalHost().hostAddress}")
} else {
//todo
println("이미 자리를 먹혔군")
}
val nowMasterServer = masterServerRepository.findByIdOrNull(1)
return nowMasterServer?.ip == InetAddress.getLocalHost().hostAddress
}
그러나 2대의 서버가 자꾸
본인을 마스터라고 우기는 현상 발생
원인은
두 서버가 master_server 의 첫번째 줄이 비어있는걸 동시에 보고
본인들의 ip 를 넣은뒤
본인들이 마스터라고 자축한것;
두 서버가 동시에 첫번째 줄이 비어있다고 보는 현상을 막기 위해
isolation 레벨을 가장 높은 Serializable 로 바꿨으나
이 현상이 계속 되었다
2차 시도
이번에는 서버들이 본인 ip 와
본인이 살아있음을 알리기 위해
health_check_at 컬럼 값을 당시 시간으로 1분마다 업데이트 하도록 했다
(master_server_health 테이블)
그리고 기존 master_server 테이블에는
health_check_at 봤을때 살아있는 애들 중에
(최근 2분안의 값인 경우)
ip 로 정렬해서 하나 뽑아서 값을 넣도록 했다
이러면 여러 서버들이 ip를 넣어도 다 같은걸 넣겠찌?!
fun checkIfMasterServer(): Boolean {
return masterServerRepository.findByIdOrNull(1)?.ip == InetAddress.getLocalHost().hostAddress
}
@Scheduled(cron = "0 * * * * *")
fun serverHealthCheck() {
val ip = InetAddress.getLocalHost().hostAddress
val entity = masterServerHealthCheckRepository.findByIdOrNull(ip)
?: MasterServerHealthCheckEntity(ip)
entity.healthCheckAt = LocalDateTime.now()
masterServerHealthCheckRepository.save(entity)
val masterIp =
masterServerHealthCheckRepository.findAllByHealthCheckAtAfterOrderByIp(LocalDateTime.now().minusMinutes(3))
.first()
.ip
//todo
println("제가 찾아낸 masterIp 는 바로 : $masterIp")
val masterEntity = masterServerRepository.findByIdOrNull(1)
?: MasterServerEntity(1)
masterEntity.ip = masterIp
masterServerRepository.save(masterEntity)
val expiredIps = masterServerHealthCheckRepository.findAllByHealthCheckAtBeforeOrderByIp(
LocalDateTime.now().minusMinutes(10)
).map { it.ip }
try {
masterServerHealthCheckRepository.deleteAllById(expiredIps)
} catch (_: Exception) {
}
}
드디어 모두가...
입을 모아 동일한 마스터를 찾기 시작...
문제 해결
꺆
반응형
'Backend' 카테고리의 다른 글
유저 닉네임 검색 속도 개선 - Postgresql 문자열 검색 빨리하기 (0) | 2024.01.17 |
---|---|
Read-Only 함수에 @Transactional 을 붙여야 하는가? (1) | 2022.09.13 |
nginx 버전이 노출되어 있으면 위험할 수 있다구? (0) | 2021.12.27 |
java 의 로깅 이란 (JCL, SLF4J, log4j, logback) (0) | 2021.12.13 |
spring boot 에서 etag 설정하기 (0) | 2021.11.29 |