DevLog ๐ถ
[Redis] Redis๋ ์ธ์ ํ์ฉํ ์ ์์๊น? 1ํธ - ๋ถ์ฐ๋ฝ ๊ตฌํํ๊ธฐ (2) ๋ณธ๋ฌธ
[Redis] Redis๋ ์ธ์ ํ์ฉํ ์ ์์๊น? 1ํธ - ๋ถ์ฐ๋ฝ ๊ตฌํํ๊ธฐ (2)
dolmeng2 2023. 10. 17. 09:24๐ฑ ๋ค์ด๊ฐ๊ธฐ ์
์ง๋ ํฌ์คํ ์์๋ ๋ ๋์ค๋ฅผ ํ์ฉํด ๋ถ์ฐ๋ฝ์ ๊ตฌํํ๋ ๋ค์ํ ๋ฐฉ๋ฒ๋ค์ ์ดํด๋ณด์๋ค.
์ด๋ฒ ํฌ์คํ ์์๋ ์กฐ๊ธ ๋ ์๋ค๋ฅธ ๋ฐฉ๋ฒ์ผ๋ก ๋ ๋์ค๋ฅผ ํ์ฉํ์ฌ ๋ถ์ฐ๋ฝ์ ๊ตฌํํด๋ณด๊ณ ์ ํ๋ค.
โ Case 4 - Lua Script ํ์ฉํ๊ธฐ
Redis 2.6 ๋ฒ์ ์์ ๋ฃจ์ ์คํฌ๋ฆฝํธ ์์ง์ด ์ถ๊ฐ๋๋ฉด์, ๋ ๋์ค ์๋ฒ์์ ๋ฃจ์ ์คํฌ๋ฆฝํธ๋ฅผ ์คํํ ์ ์๊ฒ ๋์๋ค.
๋ ๋์ค ๋ด๋ถ์์๋ EVAL (ํน์ EVALSHA)์ด๋ผ๋ ๋ช ๋ น์ด๋ฅผ ํตํด ์ฌ์ฉํ ์ ์๋ค.
EVAL script numkeys [key [key ...]] [arg [arg ...]]
๋ง์ฝ ์คํฌ๋ฆฝํธ๊ฐ ๊ธธ์ด์ง๊ฒ ๋๋ค๋ฉด ์คํฌ๋ฆฝํธ ์ ์ฒด๋ฅผ EVAL๋ก ์ ์กํ๊ธฐ์๋ ๋คํธ์ํฌ ๋์ญ์ ๋น์ฉ์ด ๋ฐ์ํ ์ ์๊ธฐ ๋๋ฌธ์, ๋ ๋์ค์์๋ SCRIPT LOAD ๋ช ๋ น์ด๋ฅผ ํตํด ์คํฌ๋ฆฝํธ๋ฅผ ์๋ฒ ์ธก์ ์บ์ฑํ ๋ค์์, ์บ์ฑ ํ ๋ฐํ๋ ํค๋ฅผ ํ์ฉํด EVALSHA๋ก ์คํฌ๋ฆฝํธ๋ฅผ ๊ด๋ฆฌํ ์ ์๋ค.
(๋ฌผ๋ก ์ด๊ฒ๋ ๋ง๋ฅ ์ข์ ๊ฑด ์๋๊ณ , ๋ ๋์ค ๋ ธ๋๊ฐ ๋ง์์ง๋ค๋ฉด ์บ์ฑ๋ ์ ์ฒด ๋ ธ๋์์ ํด์ผ ํ๊ธฐ ๋๋ฌธ์ ๋ฌธ์ ๊ฐ ์์ ์ ์๋ค.)
๋ฃจ์(Lua)๋ ์ผ์ข ์ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด๋ก, ์ ์ฐจ์งํฅ์ ์ธ์ด์ด๋ค. ์ฌ์ค C, C++์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ง๋ง, ๋ ๋์ค์์ ์ฌ์ฉํ๊ฒ ๋๋ฉด ์ฌ์ฉ์๊ฐ ๋ ๋์ค ๋ช ๋ น์ด๋ฅผ ์ง์ ์์ฑํ ์ ์์ผ๋ฉฐ, ํด๋น ์คํฌ๋ฆฝํธ๊ฐ ์คํ๋๋ ๋์์๋ ์์์ฑ์ ๋ณด์ฅํ๊ฒ ๋๋ค.
์ด๋ ๋ง๋ฅ ์ข์ ์ ์ ์๋ ๊ฒ, ์คํฌ๋ฆฝํธ์ ๋ํ ์คํ ์์ฒญ์ด ๋ค์ด์์ ๋ ๋ค๋ฅธ ๋ ๋์ค ๋ช ๋ น์ด๋ค์ ์คํ์ด ๋ถ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์ ์คํฌ๋ฆฝํธ์ ์คํ ์๊ฐ์ด ๋ฐ๋์ ๊ณ ๋ ค๋์ด์ผ ํ๋ค. ํ๋์ ์คํฌ๋ฆฝํธ์ ์ฌ๋ฌ ๋ ๋์ค ๋ช ๋ น์ด๋ฅผ ์ฌ์ฉํ๋ฉด์ ์์์ฑ์ ๋ณด์ฅํ๊ณ ์ถ์ ๋ ์ฃผ๋ก ์ฌ์ฉํ๋ค.
override fun executeScript(key: String, value: String, duration: String, script: RedisScript<Boolean>): Boolean {
return redisTemplate.execute(script, listOf(key), value, duration)
}
redisTemplate๋ ์คํฌ๋ฆฝํธ๋ฅผ ์คํํ ์ ์๋ execute() ๋ฉ์๋๋ฅผ ์ ๊ณตํ๊ณ ์์ผ๋ฉฐ, ์ค์ ๋ก ๋ด๋ถ ๊ตฌํ์ฒด๋ฅผ ์ดํด๋ณด๋ฉด eval()์ ์ฌ์ฉํ๋ ๊ฒ์ ๋ณผ ์ ์์๋ค. ์๋์ ์ฝ๋๋ฅผ ์ดํดํ ํ์๋ ์๊ณ , ๊ทธ๋ฅ eval์ ์ฌ์ฉํ๊ณ ์๊ตฌ๋... ์ ๋๋ก๋ง ๋์ด๊ฐ์!
๋ํ, key์ ๋ฆฌ์คํธ์ ๋ด๋ถ ์คํฌ๋ฆฝํธ์์ ์ฌ์ฉํ ๋ณ์๋ค์ ๊ฐ๋ณ ์ธ์๋ก (args) ๋ฐ์ ์ ์๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
์๋ฌดํผ, ์ด์ ๋ง์ถฐ์ ํ ๋ฒ ๋น์ฆ๋์ค ๋ก์ง์ ์์ฑํด๋ณด์.
@PostMapping("/withdraw-lua")
fun withdrawUsingLuaScript(@RequestBody request: WithdrawRequest): ResponseEntity<BalanceResponse> {
val result = accountService.withdrawUsingLuaScript(request)
return ResponseEntity.ok(result)
}
@Transactional
fun withdrawUsingLuaScript(request: WithdrawRequest): BalanceResponse {
val accountId = request.accountId
val accountKey = keyGenerator.generateAccountKey(accountId)
// ํ์ผ๋ก ๋ก๋ํด๋ ๋์ง๋ง ์ง๊ด์ ์ผ๋ก ์ฌ๊ธฐ์ ๋ฃ์๋ค.
val script = """
local key = KEYS[1]
local value = ARGV[1]
local expiration = tonumber(ARGV[2])
local existingValue = redis.call('GET', key)
if not existingValue then
redis.call('SET', key, value, 'EX', expiration)
return true
else
return false
end
""".trimIndent()
val redisScript = RedisScript<Boolean>(script)
while (!cacheService.executeScript(accountKey, "account-withdraw", "5", redisScript)) {
Thread.sleep(1000)
}
val account = accountRepository.getAccountById(accountId)
account.subtractBalance(request.amount)
// ์บ์ ์ ๊ฑฐ
cacheService.delete(accountKey)
return BalanceResponse(account.id, account.balance)
}
์คํฌ๋ฆฝํธ์ ๊ฒฝ์ฐ ํ์ผ๋ก ๊ด๋ฆฌํด๋ ๋์ง๋ง, ๊ทธ๋ ๊ฒ ๊ธธ์ง ์๊ธฐ ๋๋ฌธ์ ๋ฌธ์์ด์ ์ฌ์ฉํ์ฌ ๋ฐ๋ก ๋ก๋ํ์๋ค.
local key = KEYS[1]
local value = ARGV[1]
local expiration = tonumber(ARGV[2])
local existingValue = redis.call('GET', key)
if not existingValue then
redis.call('SET', key, value, 'EX', expiration)
return true
else
return false
end
๋ด๋ถ ์คํฌ๋ฆฝํธ๋ฅผ ํ ๋ฒ ์ดํด๋ณด์.
KEYS๋ฅผ ํตํด listOf(key)๋ก ๋๊ฒจ์ค ํค์ ์ฒซ ๋ฒ์งธ ๊ฐ์ ๋ฐ์์ค๊ณ , ARGV๋ฅผ ํตํด ๊ฐ๊ฐ ์ธ์๋ก ๋๊ฒจ์ค ๋ณ์๋ฅผ ์ฌ์ฉํ๋ค.
์ด๋, ์ซ์๊ฐ์ ์ฌ์ฉํ๊ธฐ ์ํด์ tonumber()๋ฅผ ํ์ฉํ ์ ์๋ค.
๋ด๋ถ ๋ก์ง์ ๊ฒฝ์ฐ GET์ ํตํด ๋ฐ์์ค๊ณ , ๋ง์ฝ ๊ฐ์ด ์กด์ฌํ์ง ์์ผ๋ฉด SET์ ํตํด ๊ฐ์ ์ง์ ํ๊ณ , ์๋๋ฉด false๋ฅผ ๋ฐํํ๋ ๊ฐ๋จํ ๋ก์ง์ด๋ค. ์ฌ์ค์ ์์ Case 2๋ฒ์ ์ ํ๋ฆฌ์ผ์ด์ ๋ ๋ฒจ์์ ์งํํ๋ ํ์๋ฅผ ๋ฃจ์ ์คํฌ๋ฆฝํธ๋ฅผ ํตํด์ ์์์ฑ์ ๋ณด์ฅํ๋๋ก ๋ง๋ค์ด์ค ๊ฒ์ด๋ค.
@Test
fun ๋ฃจ์์คํฌ๋ฆฝํธ๋ฅผ_ํตํ_๋์_์ถ๊ธ์์ฒญ() {
// given
val accountId = ๊ณ์ข_์์ฑ()
์ด๊ธฐ_์์ก_์ค์ (accountId, 100)
val requestCount = 2
// when
// ๋์์ 20์ฉ ์ถ๊ธ์ ์์ฒญํจ
val executor = Executors.newFixedThreadPool(requestCount)
val latch = CountDownLatch(requestCount)
repeat(requestCount) {
executor.submit {
๋ฃจ์์คํฌ๋ฆฝํธ๋ฅผ_ํตํ_์ถ๊ธ_์์ฒญ(accountId, 20)
latch.countDown()
}
}
latch.await()
// then
val balanceResponse = ์์ก_์กฐํ(accountId)
// ๋ฝ์ผ๋ก ์ธํด์ ์์ฐจ์ ์ผ๋ก ์ฒ๋ฆฌ๋๊ธฐ ๋๋ฌธ์ ์ ์์ ์ผ๋ก 60 ๋ฐํ
assertThat(balanceResponse.balance)
.isEqualTo(60)
}
private fun ๋ฃจ์์คํฌ๋ฆฝํธ๋ฅผ_ํตํ_์ถ๊ธ_์์ฒญ(accountId: Long, amount: Int): BalanceResponse {
val withdrawRequest = WithdrawRequest(accountId, amount)
return RestAssured.given()
.log().all()
.contentType(MediaType.APPLICATION_JSON_VALUE)
.`when`()
.body(withdrawRequest)
.post("/accounts/withdraw-lua")
.then()
.log().all()
.extract()
.`as`(object : TypeRef<BalanceResponse>() {})
}
ํ ์คํธ ์ฝ๋๋ฅผ ๋๋ ค๋ณด๋ฉด ์ญ์ ์ ๋์ํ๋ ๊ฒ์ ํ์ธํ ์ ์์๋ค.
โ Case 5 - ์คํ๋ฝ ๋์ pub-sub ๊ตฌ์กฐ ํ์ฉํ๊ธฐ (Redisson)
ํ์ง๋ง, ์์ ๋ฐฉ์๋ค์ ๋ชจ๋ ์คํ๋ฝ์ ์ฌ์ฉํ๋ค ๋ณด๋ ๋ฝ์ ์ป๊ธฐ ์ํด์ ๋ ๋์ค ์๋ฒ๋ก ๊ณ์ ์์ฒญ์ ๋ณด๋ด์ผ ํ๋ค๋ ๋ถ๋ด์ด ์๋ค.
๋ํ, ์์ ์์ ๋ค์์๋ ํค์ ๋ํด TTL๋ ๊ฑธ์ด์ฃผ๊ณ , ๋ช ์์ ์ผ๋ก delete๋ ์ง์ ํด์ฃผ์๊ธฐ ๋๋ฌธ์ ํฌ๊ฒ ๋ฌธ์ ๊ฐ ๋์ง ์์์ง๋ง, ๊ฐ๋ฐ์๊ฐ ์ง์ ์คํ๋ฝ์ ๊ตฌํํ๋ค ๋ณด๋ ๋ง์ฝ TTL์ ๋นผ๋จน๊ฑฐ๋, ํค์ ๋ํ ์ญ์ ์ฒ๋ฆฌ๋ฅผ ์งํํ์ง ์์ผ๋ฉด ๊ณ์ ๋ฝ์ ์ ์ ํ๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์๋ ์๋ค.
๊ทธ๋์, Lettuce ๋์ ์ Redission์์ ์ ๊ณตํ๋ ๋ถ์ฐ๋ฝ์ ์ฌ์ฉํ์ฌ ์ด๋ฅผ ํด๊ฒฐํด๋ณด์.
Redisson์ ๊ฒฝ์ฐ ๊ธฐ๋ณธ์ ์ผ๋ก Pub-Sub ๊ธฐ๋ฐ์ ๊ตฌ์กฐ์ด๊ธฐ ๋๋ฌธ์ ๊ณ์ ์ฐ๊ฒฐ์ ์๋ํ์ง ์๋๋ค.
Publish-Subscribe (Pub-Sub) ๊ตฌ์กฐ๋ Publisher์ Subscriber๊ฐ ์๋ก ์์ง ๋ชปํด๋ ํต์ ์ด ๊ฐ๋ฅํ๋๋ก ๋ง๋ค์ด์ง ํจํด์ด๋ค.
Publisher๋ Subscriber์๊ฒ ์ง์ ์ ์ผ๋ก ๋ฉ์์ง๋ฅผ ๋ณด๋ด์ง ์๊ณ , Channel์ publish๋ฅผ ์งํํ๊ฒ ๋๋ค.
Subscriber๋ ๊ด์ฌ์ด ์๋ ์ฑ๋์ ํ์์ ๋ฐ๋ผ Subscribe๋ฅผ ํตํด ๋ฉ์์ง๋ฅผ ์์ ํ ์ ์๋ค.
ํด๋น ์ฑ๋์ Subscribe๋ฅผ ํ๊ณ ์๋ ์ค๋ ๋๋ค์ด Publish๋ ๋ฉ์์ง๋ฅผ ๋ฐ์์ผ ๋ฝ ์ ์ ๋ฅผ ์๋ํ๊ธฐ ๋๋ฌธ์ ๋น๊ต์ ๋ถํ๊ฐ ๋ํ๋ค.
๋ ๋์ค์์๋ ๋ค์๊ณผ ๊ฐ์ด ๋ช ๋ น์ด๋ฅผ ์ ๊ณตํ๊ณ ์๋ค.
// order๋ผ๋ ์ฑ๋์ ๋ํด์ new-order๋ผ๋ ๋ฉ์์ง ๋ฐํ ์์
PUBLISH order new-order
// order, delivery๋ผ๋ ์ฑ๋์ ๋ํด์ ๋ฉ์์ง ๊ตฌ๋
์์
SUBSCRIBE order, delivery
publish ์ ์ฑ๋๊ณผ ๋ฐํํ ๋ฉ์์ง๋ฅผ ์ ๋ ฅํ๊ณ , subscribe ์ ๊ตฌ๋ ํ๊ณ ์ถ์ ์ฑ๋ ์ด๋ฆ์ ๋์ดํ๋ฉด ๋๋ค.
publish ํ ์ฑ๊ณต์ ์ผ๋ก ๋ฉ์์ง๋ฅผ ๋ฐํํ๋ฉด 1์ ๋ฐํํ๊ณ , ๋ง์ฝ ์๋ฌด๋ ๊ตฌ๋ ํ๊ณ ์์ง ์๋ ์ฑ๋์ ๋ฐํ์ ํ๋ฉด 0์ ๋ฐํํ๋ค.
์ด๋ฅผ ์ฝ๋๋ก ๊ตฌํํ๊ธฐ ์ํด ๋ค์๊ณผ ๊ฐ์ ์์กด์ฑ์ ์ถ๊ฐํด์ฃผ๋๋ก ํ์.
implementation("org.redisson:redisson-spring-boot-starter:3.23.5")
๊ทธ๋ฆฌ๊ณ , ๋น์ฆ๋์ค ๋ก์ง์ ์์ฑํด๋ณด์.
@PostMapping("withdraw-redisson")
fun withdrawUsingRedisson(@RequestBody request: WithdrawRequest): ResponseEntity<BalanceResponse> {
val result = accountService.withdrawUsingRedisson(request)
return ResponseEntity.ok(result)
}
@Service
class AccountService(
// RedissonClient์ ๋ํด ์์กด์ฑ ์ฃผ์
ํ์
private val redissonClient: RedissonClient,
private val accountRepository: AccountRepository,
private val keyGenerator: KeyGenerator
) {
@Transactional
fun withdrawUsingRedisson(request: WithdrawRequest): BalanceResponse {
val accountId = request.accountId
val accountKey = keyGenerator.generateAccountKey(accountId)
// ๋ฝ ํ๋
val lock = redissonClient.getLock(accountKey)
try {
val canLock = lock.tryLock(3, 5, TimeUnit.SECONDS)
if (canLock.not()) {
throw RuntimeException("๋ฝ ํ๋์ ์คํจํ์์ต๋๋ค.")
}
val account = accountRepository.getAccountById(accountId)
account.subtractBalance(request.amount)
return BalanceResponse(account.id, account.balance)
} finally {
// ๋ฝ ํด์
lock.unlock()
}
}
}
redissionClient์์๋ getLock()์ด๋ผ๋ ๋ฉ์๋๋ฅผ ํตํด ์ธ์๋ก ๋๊ฒจ์ค ์ด๋ฆ์ ๊ฐ์ง Lock instance๋ฅผ ์์ฑํ๋ค.
์ด๋, ์ค๋ ๋๊ฐ ๋ฝ์ ํ๋ํ๋ ์์๋ฅผ ๋ณด์ฅํ์ง ์๋๋ค. (non-fair locking์ด๋ผ๊ณ ํ๋๋ฐ, ์กฐ๊ธ ๋ ์ ํํ๊ฒ๋ ํน์ ์ค๋ ๋๊ฐ ๋จผ์ ๋ค์ด์์ ์ค๋ซ๋์ ๋ฝ์ ๋๊ธฐํ๊ณ ์์ ๋, ์๋ก์ด ์ค๋ ๋๊ฐ ์์ ์์ฒญ์ ๋ณด๋ด๋ฉด ์๋ ์ค๋ ๋๊ฐ ์๋๋ผ ์๋ก์ด ์ค๋ ๋๊ฐ ๋ฝ์ ์ ์ ํ ์ ์๋ค๋ ์๋ฏธ์ธ ๊ฒ ๊ฐ๋ค. ์ฐธ๊ณ ๋งํฌ)
์ดํ, ๊ฐ์ ธ์จ ์ธ์คํด์ค๊ฐ ๋ฝ์ ํ๋ํ ์ ์๋ ์ํฉ์ด๋ผ๋ฉด (tryLock) ์ ์ ํ๊ณ , ์๋๋ผ๋ฉด ์์ธ๋ฅผ ๋ฐ์ํ๋๋ก ๋ง๋ค์๋ค.
ํญ์ ๋ฝ์ ๋ํ ํด์ ๋ก์ง์ ๋์์ผ ํ๊ธฐ ๋๋ฌธ์ try-finally ๊ตฌ๋ฌธ์ ํตํด unlock์ ๊ผญ ํด์ฃผ๋๋ก ํ์.
ํ ๋ฒ ํ ์คํธ ์ฝ๋๋ก ํ์ธํด๋ณด์.
@Test
fun ๋ ๋์จ์_ํตํ_๋์_์ถ๊ธ์์ฒญ() {
// given
val accountId = ๊ณ์ข_์์ฑ()
์ด๊ธฐ_์์ก_์ค์ (accountId, 100)
val requestCount = 2
// when
// ๋์์ 20์ฉ ์ถ๊ธ์ ์์ฒญํจ
val executor = Executors.newFixedThreadPool(requestCount)
val latch = CountDownLatch(requestCount)
repeat(requestCount) {
executor.submit {
๋ ๋์จ์_ํตํ_์ถ๊ธ_์์ฒญ(accountId, 20)
latch.countDown()
}
}
latch.await()
// then
val balanceResponse = ์์ก_์กฐํ(accountId)
// ๋ฝ์ผ๋ก ์ธํด์ ์์ฐจ์ ์ผ๋ก ์ฒ๋ฆฌ๋๊ธฐ ๋๋ฌธ์ ์ ์์ ์ผ๋ก 60 ๋ฐํ
assertThat(balanceResponse.balance)
.isEqualTo(60)
}
private fun ๋ ๋์จ์_ํตํ_์ถ๊ธ_์์ฒญ(accountId: Long, amount: Int): BalanceResponse {
val withdrawRequest = WithdrawRequest(accountId, amount)
return RestAssured.given()
.log().all()
.contentType(MediaType.APPLICATION_JSON_VALUE)
.`when`()
.body(withdrawRequest)
.post("/accounts/withdraw-redisson")
.then()
.log().all()
.extract()
.`as`(object : TypeRef<BalanceResponse>() {})
}
์์ฃผ ์ ๋์๊ฐ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
์ด๋๋ก ๋๋ด๋ฉด ์์ฝ๊ธฐ ๋๋ฌธ์, ๋ด๋ถ์ ์ผ๋ก ์ด๋ป๊ฒ pub-sub ๊ตฌ์กฐ๋ฅผ ํตํด ๋ฝ์ ํ๋ํ๋์ง ํ์ธํด๋ณด์.
boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException;
๋จผ์ , tryLock์ ๊ฒฝ์ฐ ๋ด๋ถ์ ์ผ๋ก 3๊ฐ์ ์ธ์๋ฅผ ๋ฐ๋๋ค.
waitTime์ ๋ฝ์ ์ฌ์ฉํ ์ ์์ ๋๊น์ง ๋๊ธฐํ๋ ์๊ฐ, leaseTime์ ๋ฝ์ ์ ์ ํ๋ ์๊ฐ, unit์ ์๊ฐ์ ๋ํ ๋จ์์ด๋ค.
์ด๋ ์ฆ, leaseTime์ด ์ง๋๊ฒ ๋๋ฉด ์๋์ผ๋ก ๋ฝ์ด ํด์ ๋๋ฉฐ, ์ด๋ฏธ ๋ฝ์ ์ ์ ํ๊ณ ์๋ ์ค๋ ๋๊ฐ ์กด์ฌํ ๋ ๋ค๋ฅธ ์ค๋ ๋๊ฐ ์์ฒญ์ ๋ณด๋ด๋ฉด waitTime๊น์ง ๋๊ธฐํ๋ค๋ ์๋ฏธ์ด๋ค.
tryLock์ ๋ด๋ถ ๋ก์ง์ ์ด์ง ์ดํด๋ณด๋ฉด ํ ๊ฐ์ง ํฅ๋ฏธ๋ก์ด ์ ์ ๋ฐ๊ฒฌํ ์ ์๋๋ฐ, ๋ฐ๋ก ๋ฃจ์ ์คํฌ๋ฆฝํธ๋ฅผ ํ์ฉํ๋ค๋ ์ ์ด๋ค.
์์ ๊ฐ์ด ํค์ ์กด์ฌ์ ๋ํ ์ฌ๋ถ ํ์ธ ๋ฐ ํด๋น ํค์ ๊ฐ์ ๋ํ ์ฆ๊ฐ๋ ๋ฃจ์์คํฌ๋ฆฝํธ๋ฅผ ํตํด ์งํํ๊ณ ์์๋ค.
๋ฝ์ด ์กด์ฌํ์ง ์๋๋ค๋ฉด ๋ฝ์ ๋ํ ํค์ ์ค๋ ๋ ์์ด๋๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๊ฐ์ 1 ์ฆ๊ฐ์ํค๊ณ , TTL์ ์ค์ ํ๋ ๋ก์ง์ด๋ค.
๊ทธ๋ฆฌ๊ณ , tryLock ๋ฉ์๋์ ํ๋จ์ผ๋ก ๋ด๋ ค๊ฐ๋ณด๋ฉด ์๋์ ๊ฐ์ด ๋ฝ์ ๋ํด ๋๊ธฐํ๋ ๋ก์ง์ด ๋์จ๋ค.
์ด๋, subscribe ๋ด๋ถ ๋ก์ง์ ๋ณด๋ฉด ์ธ๋งํฌ์ด๋ฅผ ํ์ฉํ๊ณ ์๋ค.
์์ ๋งํ๋ ๊ฒ์ฒ๋ผ, ๋ฉํฐ ์ค๋ ๋ ํ๊ฒฝ์ธ ์คํ๋ง๋ถํธ๊ฐ ์ฑ๊ธ ์ค๋ ๋ ๊ธฐ๋ฐ์ธ ๋ ๋์ค๋ฅผ ํธ์ถํ๊ฒ ๋๋ฉด ๋์์ฑ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๊ธฐ ๋๋ฌธ์, ์ธ๋งํฌ์ด๋ฅผ ํ์ฉํด์ ํญ์ ํ๋์ ์ค๋ ๋๋ง ์ ๊ทผ์ด ๊ฐ๋ฅํ๋๋ก ๋ง๋ค๊ณ ์๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
subscribe๋ฅผ ์คํจํ๋ฉด (์์ธ๊ฐ ๋ฐ์ํ๋ฉด) false๋ฅผ ๋ฐํํ๋ฉฐ ๋ก์ง์ ์ข ๋ฃํ๋ค.
์ดํ, while๋ฌธ์ ๋๋ฉด์ ๋จ์ ์๋ ์๊ฐ ๋์ ๋ฝ์ ํ๋ํ๊ธฐ ์ํด ์ฌ์๋๋ฅผ ์งํํ๋ค.
์ฌ๊ธฐ์ tryAcquire()์ ์ํด ๋ฐํ๋๋ ttl ๊ฐ์ ํ์ฌ ์ค๋ ๋๊ฐ ์ ์ ๋ฅผ ์๋ํ๊ณ ์๋ ๋ฝ์ด ๋ค๋ฅธ ์ค๋ ๋์ ์ํด ์ธ์ ํด์ ๋ ์ง ๋ํ๋ด๋ ๊ฐ์ (= ๋ฝ์ด ์ผ๋ง๋ ๋ ์ ์ง๋ ์ง) ์๋ฏธํ๋ฉฐ, null์ด๋ผ๋ฉด ๋ฝ์ ํ๋ํ ์ ์๋ ์ํ์ด๊ธฐ ๋๋ฌธ์ true๋ฅผ ๋ฐํํ๊ฒ ๋๋ค.
๋ง์ฝ ๋ฝ ํ๋์ ์คํจํ๋ค๋ฉด time (waitTime์ ๋ํ๋ด๋ฉฐ, ๋๊ธฐ ์๊ฐ์ ์๋ฏธํ๋ค)์ ๊ฐ์ ๊ฐฑ์ ํด์ฃผ๊ณ , ๊ฐฑ์ ๋ ๊ฐ์ด ๋ง๋ฃ๋์๋ค๋ฉด ๋ฝ ํ๋์ ์คํจํ๋ค๊ณ ๊ฐ์ฃผํ์ฌ false๋ฅผ ๋ฐํํ๊ฒ ๋๋ค.
์ดํ, ์์ง ๋๊ธฐ ์๊ฐ์ด ๋จ์ ์๋ ์ํ๋ผ๋ฉด ํ์ฌ ์๊ฐ์ ๋ค์ ๊ฐฑ์ ํ๋ค.
๋ง์ฝ ttl (๋ฝ์ ๋ํด ๋จ์ ์ ํจ ์๊ฐ)์ด 0๋ณด๋ค ํฌ๊ฑฐ๋ ๊ฐ์ผ๋ฉด์ time (๋จ์ ๋๊ธฐ ์๊ฐ)๋ณด๋ค ์์ผ๋ฉด ttl ์๊ฐ ๋์ ๋ค์ ๋ฝ ํ๋์ ๋๊ธฐํ๋ฉฐ, ๊ทธ๊ฒ ์๋๋ผ๋ฉด ๋จ์ ๋๊ธฐ ์๊ฐ ๋์ ๊ธฐ๋ค๋ฆฌ๊ฒ ๋๋ค. (= ๊ทธ๋ฅ ํ๋์ ttl, time๋งํผ ๋๊ธฐํ๋ฉฐ ์๋ํ๋ค๊ณ ์๊ฐํ๊ธฐ)
์ต์ข ์ ์ผ๋ก ๋๊ธฐ ์๊ฐ์ธ time์ ๊ฐฑ์ ํ๊ณ , ๋ง๋ฃ๋์๋ค๋ฉด ๋ฝ ํ๋์ ์คํจํ๋ค๊ณ ๊ฐ์ฃผํ๊ณ false๋ฅผ ๋ฐํํ๊ฒ ๋๋ค.
๊ทธ๋ฆฌ๊ณ ๋ง์ง๋ง์ผ๋ก ์ด๋ฌํ ๋ฝ ํ๋ ์๋๊ฐ ์ข ๋ฃ๋๋ฉด unsubscribe๋ฅผ ํตํด ๊ตฌ๋ ์ ํด์ ํ๋ค.
๋ง์ฐฌ๊ฐ์ง๋ก ๊ตฌ๋ ํด์ ์์๋ ์ด๋ ๊ฒ ์ธ๋งํฌ์ด๋ฅผ ํ์ฉํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
๐ฑ ๋ง๋ฌด๋ฆฌ
๋ ๋์ค ๊ด๋ จํด์ ๊ณ์ ์จ์ผ์ง, ์จ์ผ์ง ํ๋๋ฐ ์ด์ฉ๋ค ๋ณด๋ ๊ณ์ ๋ฏธ๋ฃจ๋ค๊ฐ ์ด์ ์์ผ ์์ฑํ๊ฒ ๋์๋ค.
10์์๋ ๊ธ 2๊ฐ ์ ๋๋ ์ฌ๋ฆฌ๋๋ก ๋ ธ๋ ฅํด๋ด์ผ๊ฒ ๋ค... ๐ฅฒ ๋ํํด์ง ๋ ๋ฐ์ฑํด...
+ ๋ฌด์กฐ๊ฑด ๋ ๋์ค๋ฅผ ํตํด ๋ถ์ฐ๋ฝ์ ๋์ ํด์ผ ํ๋ค๊ณ ๋ ์๊ฐํ์ง ์๋๋ค.
๋ชจ๋ ๊ธฐ์ ์ ๋์ ์๋ ์ด์ ๊ฐ ์์ด์ผ ํ๋ค๊ณ ์๊ฐํ๊ธฐ ๋๋ฌธ์... ๋ ๋์ค๋ฅผ ํ์ฉํ์ง ์๋๋ผ๋ ์ผ๋ง๋ ์ง ๋์์ฑ ์ ์ด๋ ๊ฐ๋ฅํ๋ค.
์์ผ๋ก ๋์ ์ ํ๊ฒ ๋๋ค๋ฉด ๋ฌด์์ ๋์ ํ์ง ์๊ณ , ์ด๋ฌํ ์ ๋ค์ ๋ํด trade-off๋ฅผ ์ ๊ณ ๋ฏผํด๋ด์ผ๊ฒ ๋ค ๐ถ