π λΆμ°λ½ λμ λ°°κ²½
jmeterλ‘ μ¬λ¬ μ μ κ° λμμ κ°μ μν€μ΄λΉμ μ€ν¬λ©νλλ‘ ν΄λ΄€λ€.
νμ§λ§ count μ λ°μ΄νΈκ° μ΄μνλ€.
λΆλͺ ν μ μ 10λͺ μ΄ μ€ν¬λ©νλλ°, μ€ν¬λ© μλ 10μ΄ μλλΌ 1μ΄μλ€.
λμμ± λ¬Έμ κ° λ°μν κ²μ΄λ€.
μ΄λ‘ μΈν΄ λ°μ΄ν° μ ν©μ±μ΄ κΉ¨μ§κ² λμλ€.
μ΄μ νμλ λΆμ°λ½μ λμ ν΄ μ΄λ₯Ό ν΄κ²°νκ³ μ νλ€.
π λμμ± λ¬Έμ
π΅ λμμ±
λμμ±μ μ¬λ¬ μμ μ΄ κ²Ήμ³μ μ€νλλ κ²μ λ§νλ€.
μ΄λ, μ€μ λ‘ μμ μ΄ λμμ μ€νλλ€λ λ»μ μλλ€.
μ¬μ΄ μμλ‘ CPUμ μμ λ°©μμ λ€ μ μλ€.
CPUμ μ½μ΄μμλ νΉμ μμ μ νλμ μμ λ§ μν κ°λ₯νλ€.
νμ§λ§ CPUκ° μμ κ° μ νμ λΉ λ₯΄κ² νκΈ° λλ¬Έμ, μμ μ΄ κ±°μ λμμ μ€νλλ κ²μ²λΌ λ³΄μΌ λΏμ΄λ€.
π΅ λμμ± λ¬Έμ
2λͺ μ μ μ κ° μκ³ , μ¬κ³ κ°μ μμ²μ λμμ νλ€κ³ κ°μ νμ.
λ§μ½ μλ²μ λμμ± μ²λ¦¬κ° λμ΄μμ§ μλ€λ©΄, μ΄λ€ μν©μ΄ λ²μ΄μ§κΉ?
μμ²μ΄ μ μμ μΌλ‘ μ²λ¦¬λμ§ μμ κ°λ₯μ±μ΄ λλ€.
κ°λ°νλ©΄μ λμμ±μ κ΄ν λ¬Έμ λ νν μ ν μ μλ€.
μλμ κ°μ μμκ° μμ μ μλ€.
- κ°μ μμ²μ λμμ νμ κ²½μ° (νν, λ°λ₯μ΄λΌ λΆλ₯Έλ€)
- λ€νΈμν¬ νΌμ‘ λλ¬Έμ, λ μμ²μ΄ λμμ λμ°©ν κ²½μ°
- λκ·λͺ¨ νΈλν½μ΄ λͺ°λ¦΄ κ²½μ°
μ°λ¦¬λ μ΄λ° μν©μ Race Condition (κ²½μ μν)κ° λ°μνλ€κ³ νλ€.
π΅ ν΄κ²°λ²
μμμ λ°μν λ¬Έμ μ κ²½μ°, λ¬Έμ λ₯Ό ν΄κ²°ν μ μλ λ°©λ²μ λ€μνλ€.
- Java Synchronized, ReentrantLock
- DB Lock
- Redis, Zookeeper λ± μΈλΆ μΈνλΌ μ΄μ©ν λΆμ°λ½
νλνλ μμ보μ
π Java Synchronized
Javaμμ λμμ±μ κ΄λ¦¬νκΈ° μν΄ μ¬μ©νλ λ©μ»€λμ¦μ΄λ€.
μλ°μ λ΄μ₯λ λ½μΌλ‘μ μ΄λ₯Ό μ묡μ μΈ λ½(Intrinsic Lock) νΉμ λͺ¨λν°λ½(Monitor Lock)μ΄λΌκ³ νλ€.
synchronized ꡬ문μ ν΅ν΄ λͺ¨λν° μμμ λκΈ°νν μ μλ€.
public synchronized void decrease(Long id, Long quantity) {
Stock stock = stockRepository.findById(id).orElseThrow();
stock.decrease(quantity);
stockRepository.saveAndFlush(stock);
}
μμ κ°μ΄ μ¬μ©ν κ²½μ° Race Conditionμ ν΄κ²°ν μ μλ€.
νμ§λ§ λ€μκ³Ό κ°μ κ²½μ°μμ Synchronizedλ μλν λλ‘ μλνμ§ μλλ€.
1. @Transactionalκ³Ό λμμ μ¬μ©
@Transactional
public synchronized void decrease(Long id, Long quantity) {
Stock stock = stockRepository.findById(id).orElseThrow();
stock.decrease(quantity);
stockRepository.saveAndFlush(stock);
}
μμ κ°μ΄ μ¬μ©λ κ²½μ° λμμ± λ¬Έμ κ° λ°μνλ€.
μ΄λ @Transactionalμ λμ λ°©μκ³Ό κ΄λ ¨ μλ€.
@Transactionalμ νλ‘μ λ°©μμΌλ‘ λμνλ€.
κ·Έλ¬λ―λ‘ νΈλμμ μμ -> λ½ μ€μ -> μλΉμ€ νΈμΆ -> λ½ ν΄μ -> νΈλμμ μ’ λ£ μμΌλ‘ μ§νλλ€.
λ§μ½ νΈλμμ μ’ λ£ μ , λ€λ₯Έ μ€λ λκ° κ°μ λ©μλμ μ κ·Όνλ©΄ 컀λ°λμ§ μμ λ°μ΄ν°μ μ κ·Όμ΄ κ°λ₯νλ€.
λμμ± λ¬Έμ κ° λ°μ κ°λ₯νλ€.
2. λ€μ€ μλ²μμ μ¬μ©
synchronizedλ λ¨μΌ νλ‘μΈμ€μμ μ μλνλ€.
νμ§λ§ λ€μ€ μλ²μΌ κ²½μ° μ΄λ race conditionμ ν΄κ²°νμ§ λͺ»νλ€.
μ΄μ λ€μ€ μλ²μ νκ²½μ μ ν©νμ§ μμ λ°©λ²μ΄λ€.
π DB Lock
λ§ κ·Έλλ‘ DBλ₯Ό μ΄μ©ν Lockμ΄λ€.
λ€μν λ°©λ²μ΄ μλ€.
- Optimistic Lock : versionμ μ΄μ©νμ¬ μ ν©μ±μ λ§μΆλ λ°©λ²
- Pessimistic Lock : Exclusive Lockμ μ΄μ©νμ¬ μ ν©μ±μ λ§μΆλ λ°©λ²
- Named Lock : μ΄λ¦μ κ°μ§ Lockμ μ΄μ©νμ¬ Lockμ κ±°λ λ°©λ²
μ΄ λ°©λ² μ΄μΈμλ, flag columnμ μ΄μ©ν λ°©λ²λ μλ€.
μ΄λ κΈ°μ‘΄ μ°κ²°λ DBλ₯Ό μ΄μ©νκΈ° λλ¬Έμ, μΆκ°μ μΈ μΈνλΌ λμ μ κ³ λ €νμ§ μμλ λλ€.
νμ§λ§ μ΄ λ°©λ²μ μ ννμ§ μμλ€.
μ ν리μΌμ΄μ κ³Ό RDBμ λΆνλ₯Ό μ΅λν μ κ² κ°μ Έκ°κ³ μΆμκΈ° λλ¬Έμ΄λ€.
λκ΄μ λ½μ κ²½μ°, μ ν리μΌμ΄μ μ΄ λ²μ μ κ΄λ¦¬νλ©° μ΄μ λν μ²λ¦¬ λ° μ€ν¨ μ νμ²λ¦¬λ₯Ό λ΄λΉν΄μΌ νλ―λ‘,
νΈλμμ 컀λ°μ μμ΄μ 볡μ‘λκ° μ¦κ°νλ€.
λΉκ΄μ λ½μ κ²½μ°, λ°λλ½μ μνμ΄ λλ€κ³ μκ°νλ€.
μ΄ μνμ±μ μΈμ§νκ³ μλΉμ€λ₯Ό ꡬννλ€ νλλΌλ, λμ€μλ μ΄ λΆλΆμ μμ΄μ μλΉμ€ ꡬνμ μ μ½μ΄ ν΄ κ²μ΄λΌκ³ μκ°νλ€.
Named λ½μ κ²½μ°, DB μμμ λ μ¬μ©ν΄μΌ νλ λ¬Έμ κ° λ°μνλ€.
λ½ ν΄μ μμ κ΄λ ¨ λ¬Έμ λλ¬Έμ, λ‘μ§μ λν νΈλμμ κ³Ό DB lockμ λν νΈλμμ μ λΆλ¦¬νμ¬ μ¬μ©ν΄μΌ νλ€.
μ΄μ λ½μ μ μ©ν νΈλμμ μ κ²½μ° DB 컀λ₯μ μ 2κ° μ¬μ©ν΄μΌ νλ€.
Redissonκ³Ό λΉμ·νκ² μ¬μ©ν μ μλ λ°©λ²μ΄μ§λ§, DB μμμ λ μ¬μ©νλ€λ μ μ κ³ λ €νμ¬ μ¬μ©νμ§ μμλ€.
λν μ΄λ―Έ λΆμ°λ½μ ꡬν κ°λ₯ν RedisλΌλ μΈνλΌ μλΉμ€λ₯Ό λμ νμ¬ μ¬μ©νκ³ μμλ€.
μ΄μ νμλ DB Lockμ λμ νμ§ μμλ€.
π μΈλΆ μΈνλΌλ₯Ό μ΄μ©ν λΆμ°λ½
λΆμ°λ½μ ꡬννλλ° μ°μ΄λ μΈνλΌ κΈ°μ λ‘ Redisμ Zookeeperλ₯Ό λ€ μ μλ€.
κ²°λ‘ λΆν° λ§νμλ©΄ νμλ Redisλ₯Ό μ νν΄μ ꡬννλ€.
Redisλ μ΄λ―Έ μ¬μ©νκ³ μμκ³ , μ±κΈ μ€λ λλ‘ μλνκΈ° λλ¬Έμ λμμ± μ²λ¦¬μ μ ν©ν κ±°λΌ μκ°νκΈ° λλ¬Έμ΄λ€.
λν μ ν리μΌμ΄μ κ³Ό DB λΆνλ₯Ό μ€μΌ μ μλ λ°©λ²μ΄λΌ μκ°νλ€.
κ·Έλ λ€λ©΄ Zookeeperλ μ μ ννμ§ μμμκΉ?
μ΄λ―Έ Redisλ₯Ό μ¬μ©νκ³ μμλ€λ μ μ΄ ν¬κ² μμ©νλ€.
λν Zookeeperλ₯Ό λμ νμ¬ λ€λ₯Έ κ΄λ¦¬ ν¬μΈνΈλ₯Ό μΆκ°νλ κ²μ΄ λΆλ΄μΌλ‘ λ€κ°μλ€.
π Redisλ₯Ό μ΄μ©ν λΆμ°λ½
Redisλ‘ λΆμ°λ½μ μ΄μ©νλ λ°©λ²μ 2κ°μ§ μλ€.
ν΄λΌμ΄μΈνΈλ‘ Lettuceλ₯Ό μ¬μ©νκΈ°, ν΄λΌμ΄μΈνΈλ‘ Redissonμ μ¬μ©νκΈ°
1. Lettuce
Lettuceλ κΈ°λ³Έμ μΌλ‘ λ½ κΈ°λ₯μ μ 곡νμ§ μλλ€.
λλ¬Έμ μ¬μ©μκ° μ§μ setnx, setex κ°μ λͺ λ Ήμ΄λ₯Ό ν΅ν΄ λΆμ°λ½μ ꡬνν΄μΌ νλ€.
μ΄λ λ½μ Spin Lock λ°©μμ΄λ€.
μ±κΈ μ€λ λμΈ Redisμ Spin Lockμ ν° λΆνλ‘ μ΄μ΄μ§ μ μλ€.
λν setnxκ° expired timeμ μ§μ ν μ μμ΄μ Lock ν΄μ κ° λΆκ°λ₯νλ€.
μ΄ λλ¬Έμ Dead Lock λ°μ κ°λ₯μ±μ΄ μ‘΄μ¬νλ€.
2. Redisson
Redissonμ λ½ κΈ°λ₯μ μ§μν΄ μ€λ€.
pub/sub λ°©μμ μ¬μ©νμ¬ λ½μ΄ ν΄μ λ λλ§λ€ subscribe νλ ν΄λΌμ΄μΈνΈλ€μκ² μλ¦Όμ μ£Όλ ꡬ쑰μ΄λ€.
Lettuceμ λΉν΄ Redisμ λΆνκ° λ μ κ² μ΄μν μ μλ€.
ν΄λΉ μλΉμ€μ λ λμ€λ₯Ό μ΄μ©ν μλΉμ€κ° λ§μκΈ°μ, λ λμ€μ μ£Όμ΄μ§λ λΆνκ° μ μ λ°©λ²μ μ νν΄μΌ νλ€.
κ·Έλμ νμλ Redissonμ μ ννλ€.
π AOP κΈ°λ° Redisson λΆμ°λ½
π΅ Propagation.NEVER
Redisson λΆμ°λ½μ μΈν°λ·μ κ²μν κ²½μ°, νΈλμμ μ ν μμ±μ REQUIRES_NEWλ₯Ό μ¬μ©νλ κ²μ λ³Ό μ μλ€.
μ΄ κ²½μ° λ‘€λ°±μ λ¬Έμ κ° λ°μν μ μλ€.
μλ νΈλμμ μμ λΆμ°λ½ μ²λ¦¬λ₯Ό μν΄ μλ‘μ΄ νΈλμμ μ λ§λ€λ©΄ 2κ°μ νΈλμμ μ΄ μ‘΄μ¬νλ€.
μ΄λ, μ΄λ€ ν νΈλμμ μ΄ μ€ν¨νμ¬ λ‘€λ°±λλ€κ³ κ°μ ν΄ λ³΄μ.
κ·Έλ λ€λ©΄ λ€λ₯Έ νΈλμμ λ λ‘€λ°±ν μ μμκΉ?
ν μ μλ€.
μλ‘ λ 립λ νΈλμμ μ΄λ―λ‘, μλ‘κ° μλ‘μκ² μν₯μ λ―ΈμΉ μ μλ€.
μ΄ λ¬Έμ λ₯Ό λ½μ΄ μ€μ λ λ¨μΌ νΈλμμ μ μ΄μ©νλ€λ©΄ λμμ± λ¬Έμ λ₯Ό ν΄κ²°ν μ μμ κ²μ΄λΌ μκ°νλ€.
λ¨μΌ νΈλμμ μ΄λΌλ©΄, μΌλΆλΆμ μμ μ΄ μ€ν¨νλλΌλ λͺ¨λ λ‘€λ°±μ΄ κ°λ₯νκΈ° λλ¬Έμ,
μμμ λ°μ κ°λ₯ν λ‘€λ°± κ΄λ ¨ λ¬Έμ λ₯Ό 극볡 κ°λ₯ν κ²μ΄λ€.
μ΄λ₯Ό ꡬννκΈ° μν΄ μ ν μμ±μ NEVERλ₯Ό μ΄μ©νλ€.
μ ν μμ± NEVERμ λν μ€λͺ μ μ ν νΈλμμ μ΄ μ‘΄μ¬νλ©΄ μλ¬λ₯Ό λ°μμν¨λ€λΌκ³ μ νμλ€.
μ΄ λ§μ NEVER μ ν μμ±μ μ΄μ©νλ©΄, μ ν νΈλμμ μ΄ μμμ 보μ₯ν μ μλ€.
κ·Έ ν REQUIREDλ₯Ό ν΅ν΄ νΈλμμ μ μμ±νλ©΄, μ ν νΈλμμ μ΄ μ‘΄μ¬νμ§ μμ μνμμ νΈλμμ μ μμ±ν μ μλ€.
λν μ΄ νΈλμμ μμ λ€λ₯Έ νΈλμμ μ λ°μμν€μ§ μλλ€λ©΄, λ¨μΌ νΈλμμ μ μ μ§νλ κ²μ΄ κ°λ₯νλ€.
κ·Έλ¬λ―λ‘ νμλ NEVER μμ±μ μ΄μ©νμ¬ λΆμ°λ½μ ꡬνν κ²μ΄λ€.
π΅ AOP
λ½μ λͺ¨λ λ‘μ§μ μ μ©νμ§ μλλ€.
νΉμ λ‘μ§μλ§ μ μ©ν΄μΌ νλ€.
νμ§λ§ μ΄λ₯Ό μ μ©νκΈ° μν΄ λΆμ°λ½ λ‘μ§μ λ§€λ² λΉμ¦λμ€ λ‘μ§μ λ£λλ€λ©΄, λΉμ¦λμ€ λ‘μ§μ΄ μ€μΌλ κ°λ₯μ±μ΄ μ‘΄μ¬νλ€.
μ΄ μ§μ μ AOPλ₯Ό μ¬μ©νμ¬ κ·Ήλ³΅νλ€.
λ½μ΄ νμν λΆλΆμ @DistributedLockμ μ μ©νλ©΄, μ£Όμ΄μ§ κ°μ λ°λΌμ λ½μ΄ μ€μ λλλ‘ κ΅¬ννλ€.
ꡬνμ κ΄ν μμΈν λ΄μ©μ λ€μμ 보면 λλ€.
π΅ DistributedLock AOP
DistributedLock
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DistributedLock {
// λ½ νμ
DistributedLockType lockType();
// λΆμ°λ½μ κ±Έ νλΌλ―Έν° λ€μ
String[] identifier();
}
μμμ μ€λͺ ν κ²μ²λΌ AOPλ₯Ό μ΄μ©νκΈ° μν΄ μ΄λ Έν μ΄μ μ ꡬννλ€.
μ΄λ DistributedLockμ μ μνλ μ΄λ Έν μ΄μ μ΄λ€.
λ½ νμ κ³Ό, νλΌλ―Έν° μ΄λ¦ μΈμλ‘ λ°μμ λ€μ€ ν€μ λν λ½μ λμνλ€.
DistributedLockAop
@Aspect
@Component
@RequiredArgsConstructor
public class DistributedLockAop {
private final LockManager lockManager;
@Around("@annotation(allchive.server.domain.common.aop.distributedLock.DistributedLock)")
public Object lock(final ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
DistributedLock distributedLock = method.getAnnotation(DistributedLock.class);
return lockManager.lock(joinPoint, getKey(joinPoint, distributedLock));
}
private String getKey(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
String[] methodParameterNames = methodSignature.getParameterNames();
return REDISSON_LOCK_PREFIX
+ distributedLock.lockType().getLockName()
+ "-"
+ createDynamicKey(
methodParameterNames, joinPoint.getArgs(), distributedLock.identifier());
}
private String createDynamicKey(
String[] methodParameterNames, Object[] methodArgs, String[] identifiers) {
List<String> resultList = new ArrayList<>();
for (String identifier : identifiers) {
int indexOfKey = Arrays.asList(methodParameterNames).indexOf(identifier);
Object arg = methodArgs[indexOfKey];
if (arg == null) {
throw InvalidLockIdentifierException.EXCEPTION;
}
resultList.add(arg.toString());
}
return String.join(":", resultList);
}
}
DistributedLock μ΄λ Έν μ΄μ μ μ€μ§μ μΈ λμμ ꡬνν μ½λμ΄λ€.
AOPμμλ ν€λ₯Ό λ§λλ μμ κΉμ§λ§ μ§ννκ³ , Lockμ λν ꡬνμ LockManagerκ° μ²λ¦¬νλ€.
ν€λ μ΄λ Έν μ΄μ μ μ£Όμ΄μ§ μΈμ μ΄λ¦μ κ°μ§ νλΌλ―Έν° κ°μ κ°μ Έμμ λμ μΌλ‘ ν€λ₯Ό μμ±νλλ‘ κ΅¬ννλ€.
μ΄λ‘μ¨ λ ꡬ체μ μΈ κ°μ μ΄μ©νμ¬ λ½μ κ±Έ μ μμκ³ , λ½μ μ€μ ν μ μλ λ²μλ₯Ό λκ² κ°μ Έκ° μ μμλ€.
AOPκ° μ²λ¦¬νλ μμ κ³Ό Lockμ μ€μ§μ μΌλ‘ μ€μ νλ μμ μ λΆλ¦¬νκΈ° μν΄,
Lock μ€μ κ΄λ ¨ μμ μ LockManagerκ° μ§ννλ€.
LockManager
@Component
public interface LockManager {
public Object lock(ProceedingJoinPoint joinPoint, String key) throws Throwable;
}
λ½μ λ΄λΉνλ μΈν°νμ΄μ€μ΄λ€.
LockManagerImpl
@Component
@RequiredArgsConstructor
@Slf4j
public class LockManagerImpl implements LockManager {
private final RedissonClient redissonClient;
private final TransactionTemplate txTemplate;
public Object lock(ProceedingJoinPoint joinPoint, String key) throws Throwable {
RLock rLock = redissonClient.getLock(key);
log.info("lock : {}", key);
try {
boolean available = rLock.tryLock(LOCK_WAIT_TIME, LOCK_LEASE_TIME, TimeUnit.SECONDS);
if (!available) {
return false;
}
TransactionCallback txCallback =
status -> {
try {
return joinPoint.proceed();
} catch (Throwable e) {
throw TxTemplateExecutionFailException.EXCEPTION;
}
};
return executeTransaction(
status -> executeTransaction(txCallback, PROPAGATION_REQUIRED),
PROPAGATION_NEVER);
} catch (InterruptedException e) {
throw InterruptRedissonException.EXCEPTION;
} finally {
try {
log.info("unlock : {}", key);
rLock.unlock();
} catch (IllegalMonitorStateException e) {
throw AlreadyRedissonUnlockException.EXCEPTION;
}
}
}
private <T> Object executeTransaction(TransactionCallback<T> block, int propagationBehavior) {
txTemplate.setPropagationBehavior(propagationBehavior);
return txTemplate.execute(block);
}
}
LockManagerμ ꡬνμ²΄λ‘ μμ λ§ν κ²κ³Ό κ°μ΄ Lockμ λν μμ μ μννλ€.
μμ μν μμλ
λ½ μ€μ -> tx NEVER -> tx REQUIRED -> μμ -> μ»€λ° -> λ½ ν΄μ
μ΄λ€.
νΈλμμ κ΄λ ¨ μμ λͺ¨λκ° λ½μ λ΄λΆμμ μ§νλμ΄μΌ νλ€.
μ΄λ° μν©μ΄ μλ€κ³ κ°μ ν΄ λ³΄μ.
λ§μ½ 컀λ°μ΄ λ³κ²½μ νκ³ λ½μ΄ ν΄μ λμμ§λ§ 컀λ°λμ§ μμ μν©μ λ€λ₯Έ μ μ μ μ μ₯μμ 보면,
λ½μ΄ ν΄μ λμμΌλ―λ‘ μ΄ μμ μ λ³ΈμΈμ΄ μνν μ μλ κ²μ΄λ€.
μ¦, 컀λ°λ λ°μ΄ν°λ₯Ό κΈ°λ°μΌλ‘ μμ νλ κ² μλλΌ μ»€λ° μ΄μ λ°μ΄ν°λ₯Ό κΈ°λ°μΌλ‘ μμ νκ² λλ€.
λμμ± λ¬Έμ κ° λ°μνλ€.
λ½ λ΄λΆμμ νΈλμμ μ΄ μ λΆ μ§νλμ§ μμΌλ©΄ μ΄λ¬ν μΌμ΄ λ°μνλ€.
μ΄λ¬ν μν©μ νΈλμμ μμ λͺ¨λκ° λ½ λ΄λΆμμ μ΄λ£¨μ΄μ§λλ‘ νλ©΄ ν΄κ²° κ°λ₯νλ€.
λ½ λ΄λΆμμ μμ μ λͺ¨λ λ€ ν κ²½μ° λ€λ₯Έ νΈλμμ μ΄ νμ¬μ νΈλμμ μ μν₯μ λ°μ§ μμΌλ―λ‘, λμμ±μ μ μ§ν μ μλ€.
κ·Έλ¬λ―λ‘ λ½ λ΄λΆμμ νΈλμμ μμ μ΄ λͺ¨λ μ§νλμ΄μΌ νλ€.
λν νΈλμμ NEVERλ‘ μ€ν ν, νΈλμμ REQUIREDλ₯Ό λ μ€ννλ€.
μ΄μ λ NEVERμ νΉμ±μ μλ€.
NEVERλ μ ν νΈλμμ μ΄ μμμ 보μ₯ν΄ μ€λ€.
νμ§λ§ νΈλμμ μ΄ μμ λ, μλ‘μ΄ νΈλμμ μ λ§λ€μ΄μ£Όμ§ μλλ€.
κ·Έλ¬λ―λ‘ μ΄μ΄μ§ νΈλμμ μμ λ€μ λ 립μ μΈ νΈλμμ μΌλ‘ μ²λ¦¬λμ΄ λ²λ¦°λ€.
λ‘μ§ λ΄λΆ νΈλμμ μ νλλ‘ λ¬Άμ΄μ£Όλ μμ μ΄ νμνκ³ , μ΄λ₯Ό REQUIREDλ‘ μ²λ¦¬νλ€.
μ λ κ³Όμ μ ν΅ν΄ λ½μ΄ μ€μ λκ³ μ ν νΈλμμ μ΄ μ‘΄μ¬νμ§ μμ μνμμ νΈλμμ μ μμ±νμ¬ λ‘μ§μ μ²λ¦¬ν μ μμλ€.
μ μ½λλ μλμ κ°μ΄ μ¬μ©λ κ²μ΄λ€.
@DistributedLock(
lockType = DistributedLockType.ARCHIVING_PIN,
identifier = {"archivingId", "userId"})
public void execute(Long archivingId, Boolean cancel, Long userId) {
validateExecution(archivingId, userId, cancel);
archivingDomainService.updatePin(archivingId, userId, !cancel);
}
μ΄μ κ°μ λ°©λ²μ ν΅ν΄ νμκ° μνλ AOP κΈ°λ° λΆμ°λ½μ ꡬνν μ μμλ€.
π λ§λ¬΄λ¦¬
μμ κ°μ λ°©λ²μΌλ‘ λ¬Έμ λΌ μκ°νλ μ§μ μ 극볡ν μ μμλ€.
νμ§λ§ μ΄ λ°©λ²μ΄ μλ²½ν λ°©λ²μ μλλΌ μκ°νλ€.
μλμ κ°μ λ¬Έμ κ° μμ μ μλ€κ³ μκ°νλ€.
- μλΉμ€ λ‘μ§ μ 체μ μ μ©ν΄μΌ νλ€.
νΈλμμ μ΄ μ€μ λ μνμμ μνλλ λ‘μ§μ μν μκ°μ΄ κΈΈμ΄μ§λ€λ©΄, λ½ μ€μ μκ°μ΄ κΈΈμ΄μ§λ€.
μ΄λ 곧 ν΄λΉ λ½μ κ΄λ ¨λ μλΉμ€μ μμ μ΄ λΆκ°λ₯νλ€λ λ§μ΄κ³ ,
μ΄λ‘ μΈν΄ μ 체μ μΈ μλΉμ€ νμ§μ΄ μ νλ μ μλ€. - NEVER μ€μ κ° λλ¬Έμ, λΉμ¦λμ€ λ‘μ§ μν μ€ μΌλΆλΆμλ§ μ μ©ν μ μλ€.
μ΄λ₯Ό μ²λ¦¬νκΈ° μν΄μ λΉλκΈ° μ²λ¦¬λ₯Ό ν΅ν΄ μΌλΆλΆλ§ μ€νν΄μΌ νλ€.
λ‘€λ°± λ¬Έμ λ₯Ό ν΄κ²°νκΈ° μν΄ κ³ μν λ°©λ²μ΄ μ€νλ € λ‘€λ°± κ΄λ ¨ λ¬Έμ λ₯Ό λ°μμν¬ μ μμ΄μ§λ€.
λ μ’μ λ°©λ²μ΄ μμμ§ κ³ λ―Όν΄ λ΄μΌκ² λ€.
'Backend' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
<Spring> Spring Bean, IoC/DI μ 리 1 (0) | 2023.10.03 |
---|---|
<Spring> Redis κΈ°λ° κ²μμ΄ μλ μμ± κΈ°λ₯ ꡬννκΈ° (1) | 2023.10.01 |
<Spring> ResponseEntityExceptionHandler μ΄μ©ν κ³΅ν΅ μλ¬ μ²λ¦¬ (0) | 2023.05.07 |
<Spring> HttpServletRequest κ° μ¬λΌμ§λ λ¬Έμ (1) | 2023.05.06 |
<Spring> Success Custom Response μ μ©νκΈ° (0) | 2023.05.02 |