Skip to content

HK-hub/Lock4j

Repository files navigation

Lock4j

License Java Spring Boot

English | ไธญๆ–‡

ๅŸบไบŽ Spring Boot ็š„ๅˆ†ๅธƒๅผ้”ๆก†ๆžถ๏ผŒ้€š่ฟ‡ @Lock ๆณจ่งฃๅฎž็Žฐๆœฌๅœฐ้”ๅ’Œๅˆ†ๅธƒๅผ้”็š„ไธ€้”ฎๅŠ ้”ใ€‚

็‰นๆ€ง

  • ๐Ÿš€ ็ฎ€ๅ•ๆ˜“็”จ - ไธ€ไธช @Lock ๆณจ่งฃๆžๅฎšๅˆ†ๅธƒๅผ้”
  • ๐Ÿ”ง ๅคšๅผ•ๆ“Žๆ”ฏๆŒ - Redissonใ€RedisTemplateใ€Zookeeperใ€Etcdใ€ๆœฌๅœฐ้”
  • ๐Ÿ“ ๆดพ็”Ÿๆณจ่งฃ - @RedissonLockใ€@RedisTemplateLockใ€@ZookeeperLockใ€@EtcdLockใ€@LocalLock
  • ๐Ÿ”‘ SpEL ่กจ่พพๅผ - ็ตๆดป็š„ Key ๆž„ๅปบ๏ผŒๆ”ฏๆŒๆ–นๆณ•ๅ‚ๆ•ฐๅ’Œๅฏน่ฑกๅฑžๆ€ง
  • ๐Ÿ—๏ธ ่‡ชๅฎšไน‰ KeyBuilder - ๅคๆ‚ๅœบๆ™ฏๅฏๆ‰ฉๅฑ•
  • ๐Ÿ”’ ๅคš็ง้”็ฑปๅž‹ - ๅฏ้‡ๅ…ฅ้”ใ€ๅ…ฌๅนณ้”ใ€่ฏปๅ†™้”
  • โฑ๏ธ Watchdog ่‡ชๅŠจ็ปญๆœŸ - ้˜ฒๆญขไธšๅŠกๆœชๆ‰ง่กŒๅฎŒ้”่ฟ‡ๆœŸ
  • ๐ŸŽฏ ๅคฑ่ดฅๅค„็† - ๅฏ่‡ชๅฎšไน‰ๅŠ ้”ๅคฑ่ดฅๅค„็†็ญ–็•ฅ
  • ๐Ÿ”Œ ๆ‹ฆๆˆชๅ™จๆœบๅˆถ - ๆต็จ‹้’ฉๅญ๏ผŒๆ”ฏๆŒ็›‘ๆŽงใ€ๆ—ฅๅฟ—ใ€้“พ่ทฏ่ฟฝ่ธช
  • ๐Ÿ“ก ไบ‹ไปถ็›‘ๅฌ - ้”็”Ÿๅ‘ฝๅ‘จๆœŸไบ‹ไปถๅ‘ๅธƒ
  • ๐Ÿ”„ ๅคš Key ๅŠ ้” - ๆ”ฏๆŒๅŒๆ—ถๅฏนๅคšไธช Key ๅŠ ้”
  • ๐Ÿ—๏ธ Spring Boot ้›†ๆˆ - ่‡ชๅŠจ้…็ฝฎ๏ผŒๅผ€็ฎฑๅณ็”จ

ๆจกๅ—่ฏดๆ˜Ž

lock4j
โ”œโ”€โ”€ lock4j-core                    # ๆ ธๅฟƒๆจกๅ—๏ผŒๅฎšไน‰ๆŽฅๅฃๅ’ŒๆŠฝ่ฑก็ฑป
โ”œโ”€โ”€ lock4j-redisson                # Redisson ๅฎž็Žฐ (Redis)
โ”œโ”€โ”€ lock4j-redis-template          # RedisTemplate ๅฎž็Žฐ (Redis)
โ”œโ”€โ”€ lock4j-zookeeper               # Zookeeper ๅฎž็Žฐ (Curator 5.x)
โ”œโ”€โ”€ lock4j-etcd                    # Etcd ๅฎž็Žฐ (jetcd 0.7.x)
โ”œโ”€โ”€ lock4j-local                   # ๆœฌๅœฐ้”ๅฎž็Žฐ
โ”œโ”€โ”€ lock4j-spring-boot-starter     # Spring Boot ่‡ชๅŠจ้…็ฝฎ
โ””โ”€โ”€ lock4j-examples                # ไฝฟ็”จ็คบไพ‹

ๅฟซ้€Ÿๅผ€ๅง‹

1. ๆทปๅŠ ไพ่ต–

Maven:

<dependency>
    <groupId>com.geek.lock</groupId>
    <artifactId>lock4j-spring-boot-starter</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>

<!-- ๆ นๆฎ้œ€่ฆๆทปๅŠ ๅ…ทไฝ“ Provider ไพ่ต– -->
<dependency>
    <groupId>com.geek.lock</groupId>
    <artifactId>lock4j-redisson</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>

Gradle:

implementation 'com.geek.lock:lock4j-spring-boot-starter:1.0.0-SNAPSHOT'
implementation 'com.geek.lock:lock4j-redisson:1.0.0-SNAPSHOT'

2. ้…็ฝฎ Provider

lock4j:
  enabled: true
  primary-provider: redissonLockProvider
  
  redisson:
    enabled: true
    address: localhost:6379

3. ไฝฟ็”จๆณจ่งฃ

@Service
public class OrderService {

    @Lock(keys = "#orderId")
    public void processOrder(String orderId) {
        // ไธšๅŠก้€ป่พ‘
    }
}

ๆ”ฏๆŒ็š„ LockProvider

Provider ้”็ฑปๅž‹ ๅฎž็ŽฐๆŠ€ๆœฏ ่ฏดๆ˜Ž
Redisson ๅฏ้‡ๅ…ฅใ€ๅ…ฌๅนณใ€่ฏปๅ†™ Redisson 3.x ๅŠŸ่ƒฝๆœ€ๅ…จ๏ผŒๆŽจ่ไฝฟ็”จ
RedisTemplate ๅฏ้‡ๅ…ฅ Lua ่„šๆœฌ ่ฝป้‡็บง๏ผŒไพ่ต–ๅฐ‘
Zookeeper ๅฏ้‡ๅ…ฅใ€่ฏปๅ†™ Curator 5.x CP ไธ€่‡ดๆ€ง
Etcd ๅฏ้‡ๅ…ฅใ€ๅ…ฌๅนณ jetcd 0.7.x ไบ‘ๅŽŸ็”Ÿๅœบๆ™ฏ
Local ๅฏ้‡ๅ…ฅใ€ๅ…ฌๅนณใ€่ฏปๅ†™ JDK ReentrantLock ๅ•่ฟ›็จ‹ไฝฟ็”จ

ไฝฟ็”จ็คบไพ‹

ๅŸบๆœฌไฝฟ็”จ

SpEL ่กจ่พพๅผ

@Service
public class OrderService {

    // ๆ–นๆณ•ๅ‚ๆ•ฐ
    @Lock(keys = "#orderId")
    public void processOrder(String orderId) {
        // ไธšๅŠก้€ป่พ‘
    }

    // ๅฏน่ฑกๅฑžๆ€ง
    @Lock(keys = "#order.id")
    public void processOrder(Order order) {
        // ไธšๅŠก้€ป่พ‘
    }

    // ๅตŒๅฅ—ๅฑžๆ€ง
    @Lock(keys = "#order.user.id")
    public void processOrderByUser(Order order) {
        // ไธšๅŠก้€ป่พ‘
    }

    // ๅคšไธช Key
    @Lock(keys = {"#productId", "#warehouseId"})
    public void deductStock(String productId, String warehouseId) {
        // ไธšๅŠก้€ป่พ‘
    }
}

ๆŒ‡ๅฎš้”ๅ‚ๆ•ฐ

@Service
public class PaymentService {

    // ่‡ชๅฎšไน‰็ญ‰ๅพ…ๆ—ถ้—ดๅ’Œ่ฟ‡ๆœŸๆ—ถ้—ด
    @Lock(keys = "#orderId", waitTime = 5000, leaseTime = 60000)
    public void processPayment(String orderId) {
        // ไธšๅŠก้€ป่พ‘
    }

    // ๆŒ‡ๅฎšๆ—ถ้—ดๅ•ไฝ
    @Lock(keys = "#orderId", waitTime = 5, leaseTime = 60, timeUnit = TimeUnit.SECONDS)
    public void processPaymentWithSeconds(String orderId) {
        // ไธšๅŠก้€ป่พ‘
    }

    // ๆทปๅŠ  Key ๅ‰็ผ€
    @Lock(keys = "#orderId", prefix = "order:lock:")
    public void processOrderWithPrefix(String orderId) {
        // ไธšๅŠก้€ป่พ‘
    }
}

้”็ฑปๅž‹

@Service
public class ResourceService {

    // ๅฏ้‡ๅ…ฅ้”๏ผˆ้ป˜่ฎค๏ผ‰
    @Lock(keys = "#resourceId", lockType = LockType.REENTRANT)
    public void processResource(String resourceId) {
        // ไธšๅŠก้€ป่พ‘
    }

    // ๅ…ฌๅนณ้”
    @Lock(keys = "#resourceId", lockType = LockType.FAIR)
    public void processWithFairLock(String resourceId) {
        // ไธšๅŠก้€ป่พ‘
    }

    // ่ฏป้”๏ผˆๅ…ฑไบซ้”๏ผ‰
    @Lock(keys = "#resourceId", lockType = LockType.READ)
    public String readResource(String resourceId) {
        // ่ฏปๅ–้€ป่พ‘
        return "data";
    }

    // ๅ†™้”๏ผˆๆŽ’ไป–้”๏ผ‰
    @Lock(keys = "#resourceId", lockType = LockType.WRITE)
    public void writeResource(String resourceId, String data) {
        // ๅ†™ๅ…ฅ้€ป่พ‘
    }
}

ๆดพ็”Ÿๆณจ่งฃ

ๆฏไธช LockProvider ้ƒฝๆœ‰ๅฏนๅบ”็š„ๆดพ็”Ÿๆณจ่งฃ๏ผŒ่‡ชๅŠจๆŒ‡ๅฎš Provider๏ผš

@Service
public class LockService {

    // Redisson ้”
    @RedissonLock(keys = "#id", lockType = LockType.FAIR)
    public void processWithRedisson(String id) {
        // ไธšๅŠก้€ป่พ‘
    }

    // RedisTemplate ้”
    @RedisTemplateLock(keys = "#id", waitTime = 3000, leaseTime = 30000)
    public void processWithRedisTemplate(String id) {
        // ไธšๅŠก้€ป่พ‘
    }

    // Zookeeper ้”
    @ZookeeperLock(keys = "#id", lockType = LockType.READ)
    public String readWithZookeeper(String id) {
        return "data";
    }

    // Etcd ้”
    @EtcdLock(keys = "#id", lockType = LockType.FAIR)
    public void processWithEtcd(String id) {
        // ไธšๅŠก้€ป่พ‘
    }

    // ๆœฌๅœฐ้”
    @LocalLock(keys = "#id")
    public void processWithLocal(String id) {
        // ไธšๅŠก้€ป่พ‘
    }
}

่‡ชๅฎšไน‰ KeyBuilder

ๅฝ“ SpEL ่กจ่พพๅผๆ— ๆณ•ๆปก่ถณๅคๆ‚ๅœบๆ™ฏๆ—ถ๏ผŒๅฏ่‡ชๅฎšไน‰ KeyBuilder๏ผš

// 1. ๅฎž็Žฐ่‡ชๅฎšไน‰ KeyBuilder
@Component
public class OrderKeyBuilder extends AbstractKeyBuilder {
    
    @Override
    protected String[] doBuild(Method method, String[] parameterNames, 
                               Object[] args, Lock annotation) {
        Order order = (Order) args[0];
        String userId = (String) args[1];
        // ๆ นๆฎไธšๅŠก้€ป่พ‘ๆž„ๅปบ Key
        return new String[]{
            "order:" + order.getId(),
            "user:" + userId
        };
    }
}

// 2. ไฝฟ็”จ่‡ชๅฎšไน‰ KeyBuilder
@Service
public class OrderService {

    @Lock(keyBuilder = OrderKeyBuilder.class)
    public void processOrder(Order order, String userId) {
        // ไธšๅŠก้€ป่พ‘
    }
}

ๅคฑ่ดฅๅค„็†

ๅ†…็ฝฎๅคฑ่ดฅๅค„็†ๅ™จ

@Service
public class OrderService {

    // ้ป˜่ฎค๏ผšๆŠ›ๅ‡บ LockFailureException
    @Lock(keys = "#orderId")
    public void processOrder(String orderId) {
        // ไธšๅŠก้€ป่พ‘
    }

    // ๆŠ›ๅ‡บ่‡ชๅฎšไน‰ๅผ‚ๅธธ
    @Lock(keys = "#orderId", failFast = OrderLockException.class)
    public void processOrderWithCustomException(String orderId) {
        // ไธšๅŠก้€ป่พ‘
    }
}

่‡ชๅฎšไน‰ๅคฑ่ดฅๅค„็†ๅ™จ

// 1. ๅฎž็Žฐ่‡ชๅฎšไน‰ๅคฑ่ดฅๅค„็†ๅ™จ
@Component
public class RetryFailureHandler implements FailureHandler {
    
    private static final int MAX_RETRY = 3;
    
    @Override
    public Object handle(LockFailureContext context) {
        String[] keys = context.getLockKeys();
        Method method = context.getMethod();
        Object[] args = context.getArgs();
        
        log.warn("Lock failed for keys: {}, method: {}", 
                 Arrays.toString(keys), method.getName());
        
        // ่ฟ”ๅ›ž้ป˜่ฎคๅ€ผ
        return getDefaultValue(method.getReturnType());
    }
    
    private Object getDefaultValue(Class<?> returnType) {
        if (returnType == boolean.class) return false;
        if (returnType == int.class) return 0;
        if (returnType == String.class) return "LOCK_FAILED";
        return null;
    }
}

// 2. ไฝฟ็”จ่‡ชๅฎšไน‰ๅคฑ่ดฅๅค„็†ๅ™จ
@Service
public class OrderService {

    @Lock(keys = "#orderId", failureHandler = RetryFailureHandler.class)
    public Order getOrder(String orderId) {
        // ไธšๅŠก้€ป่พ‘
        return orderRepository.findById(orderId);
    }
    
    // ๅคฑ่ดฅๆ—ถ่ฟ”ๅ›ž null ่€Œไธๆ˜ฏๆŠ›ๅผ‚ๅธธ
    @Lock(keys = "#orderId", failureHandler = RetryFailureHandler.class)
    public String processOrder(String orderId) {
        // ไธšๅŠก้€ป่พ‘
        return "SUCCESS";
    }
}

ๆ‹ฆๆˆชๅ™จ

ๆ‹ฆๆˆชๅ™จๆไพ›้”ๆ‰ง่กŒๆต็จ‹ๅ„้˜ถๆฎต็š„้’ฉๅญๆ–นๆณ•๏ผŒๅฏ็”จไบŽ็›‘ๆŽงใ€ๆ—ฅๅฟ—ใ€้“พ่ทฏ่ฟฝ่ธช็ญ‰ใ€‚

ๅฎšไน‰ๆ‹ฆๆˆชๅ™จ

@Component
public class LoggingLockInterceptor implements LockInterceptor {
    
    @Override
    public void beforeKeyBuild(Method method, Object[] args, Lock annotation) {
        log.info("[Lock] ๅ‡†ๅค‡ๆž„ๅปบ Key, ๆ–นๆณ•: {}, ๅ‚ๆ•ฐ: {}", 
                 method.getName(), Arrays.toString(args));
    }
    
    @Override
    public void afterKeyBuild(List<String> keys) {
        log.info("[Lock] Key ๆž„ๅปบๅฎŒๆˆ: {}", keys);
    }
    
    @Override
    public void beforeLock(List<String> keys, LockOptions options) {
        log.info("[Lock] ๅฐ่ฏ•ๅŠ ้”: {}, ็ญ‰ๅพ…ๆ—ถ้—ด: {}ms, ็งŸๆœŸ: {}ms", 
                 keys, options.getWaitTime(), options.getLeaseTime());
    }
    
    @Override
    public void afterLock(List<String> keys) {
        log.debug("[Lock] ๅ•ไธช Key ๅŠ ้”ๆ“ไฝœๅฎŒๆˆ: {}", keys);
    }
    
    @Override
    public void onLockSuccess(List<String> keys, LockKey lockKey) {
        log.info("[Lock] ๅŠ ้”ๆˆๅŠŸ: {}", lockKey.getKey());
    }
    
    @Override
    public void onLockFailure(List<String> keys) {
        log.warn("[Lock] ๅŠ ้”ๅคฑ่ดฅ: {}", keys);
    }
    
    @Override
    public void onException(List<String> keys, Throwable exception) {
        log.error("[Lock] ๆ‰ง่กŒๅผ‚ๅธธ: {}, keys: {}", 
                  exception.getMessage(), keys, exception);
    }
}

ไฝฟ็”จๆ‹ฆๆˆชๅ™จ

@Service
public class OrderService {

    @Lock(keys = "#orderId", interceptor = LoggingLockInterceptor.class)
    public void processOrder(String orderId) {
        // ไธšๅŠก้€ป่พ‘
    }
    
    // ็ป“ๅˆๅ…ถไป–ๅฑžๆ€ง
    @Lock(keys = "#orderId", 
          interceptor = LoggingLockInterceptor.class,
          waitTime = 5000,
          leaseTime = 60000)
    public Order processOrderWithLog(String orderId) {
        // ไธšๅŠก้€ป่พ‘
        return order;
    }
}

ๆ‹ฆๆˆชๅ™จๆ‰ง่กŒๆต็จ‹

beforeKeyBuild(method, args, annotation)
        โ†“
    ่งฃๆž Key
        โ†“
afterKeyBuild(keys)
        โ†“
beforeLock(keys, options)
        โ†“
    โ”Œโ”€โ”€โ”€ ๅพช็Žฏๆฏไธช Key โ”€โ”€โ”€โ”
    โ”‚                    โ”‚
    โ”‚  afterLock(key)    โ”‚
    โ”‚        โ†“           โ”‚
    โ”‚   ๅฐ่ฏ•ๅŠ ้”          โ”‚
    โ”‚        โ†“           โ”‚
    โ”‚  ๆˆๅŠŸ โ†’ onLockSuccess(key, lockKey)
    โ”‚  ๅคฑ่ดฅ โ†’ onLockFailure(keys) โ†’ ๅคฑ่ดฅๅค„็†
    โ”‚                    โ”‚
    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
        โ†“
   ๆ‰ง่กŒไธšๅŠกๆ–นๆณ•
        โ†“
    ๆˆๅŠŸ โ†’ ่ฟ”ๅ›ž็ป“ๆžœ
    ๅผ‚ๅธธ โ†’ onException(keys, exception)

ไบ‹ไปถ็›‘ๅฌ

้€š่ฟ‡ Spring ไบ‹ไปถๆœบๅˆถ็›‘ๅฌ้”็š„็”Ÿๅ‘ฝๅ‘จๆœŸไบ‹ไปถ๏ผš

@Component
public class LockEventListener {

    @EventListener
    public void onLockEvent(LockEvent event) {
        LockEventType type = event.getType();
        List<String> keys = event.getKeys();
        
        switch (type) {
            case BEFORE_LOCK:
                log.info("ๅŠ ้”ๅผ€ๅง‹: {}", keys);
                metrics.increment("lock.attempt");
                break;
                
            case AFTER_LOCK:
                log.info("ๅŠ ้”ๆˆๅŠŸ: {}", keys);
                metrics.increment("lock.success");
                break;
                
            case LOCK_FAILED:
                log.warn("ๅŠ ้”ๅคฑ่ดฅ: {}", keys);
                metrics.increment("lock.failed");
                break;
                
            case LOCK_ERROR:
                log.error("ๅŠ ้”้”™่ฏฏ: {}", keys);
                metrics.increment("lock.error");
                break;
                
            case BEFORE_UNLOCK:
                log.debug("ๅ‡†ๅค‡่งฃ้”: {}", keys);
                break;
                
            case AFTER_UNLOCK:
                log.debug("่งฃ้”ๅฎŒๆˆ: {}", keys);
                break;
        }
    }
}

Key ็ผบๅคฑ็ญ–็•ฅ

ๅฝ“ keys ๅ’Œ keyBuilder ้ƒฝไธบ็ฉบๆ—ถ็š„ๅค„็†็ญ–็•ฅ๏ผš

@Service
public class DefaultKeyService {

    // ไฝฟ็”จๆ–นๆณ•ๅ…จ้™ๅฎšๅไฝœไธบ Key๏ผˆ้ป˜่ฎค๏ผ‰
    @Lock
    public void processWithDefaultKey() {
        // Key = "com.example.DefaultKeyService.processWithDefaultKey"
    }

    // ไฝฟ็”จๆ–นๆณ•ๅ…จ้™ๅฎšๅไฝœไธบ Key๏ผŒ่‡ชๅฎšไน‰่ถ…ๆ—ถ
    @Lock(leaseTime = 60000)
    public void processWithLongLease() {
        // ไธšๅŠก้€ป่พ‘
    }

    // Key ไธบ็ฉบๆ—ถๆŠ›ๅ‡บๅผ‚ๅธธ
    @Lock(keyAbsentPolicy = KeyAbsentPolicy.THROW_EXCEPTION)
    public void processWithStrictKey() {
        // ๅฆ‚ๆžœๆœชๆŒ‡ๅฎš keys๏ผŒไผšๆŠ›ๅ‡บ IllegalArgumentException
    }
}

ๆŒ‡ๅฎš Provider

ๅฝ“ๅญ˜ๅœจๅคšไธช LockProvider ๆ—ถ๏ผŒๅฏไปฅๆŒ‡ๅฎšไฝฟ็”จๅ“ชไธช๏ผš

@Service
public class MultiProviderService {

    // ไฝฟ็”จ้ป˜่ฎค Provider๏ผˆprimary-provider ้…็ฝฎ๏ผ‰
    @Lock(keys = "#id")
    public void processWithDefault(String id) {
        // ไธšๅŠก้€ป่พ‘
    }

    // ๆŒ‡ๅฎšไฝฟ็”จ Redisson
    @Lock(keys = "#id", provider = RedissonLockProvider.class)
    public void processWithRedisson(String id) {
        // ไธšๅŠก้€ป่พ‘
    }

    // ๆŒ‡ๅฎšไฝฟ็”จ RedisTemplate
    @Lock(keys = "#id", provider = RedisTemplateLockProvider.class)
    public void processWithRedisTemplate(String id) {
        // ไธšๅŠก้€ป่พ‘
    }

    // ๆŒ‡ๅฎšไฝฟ็”จ Zookeeper
    @Lock(keys = "#id", provider = ZookeeperLockProvider.class)
    public void processWithZookeeper(String id) {
        // ไธšๅŠก้€ป่พ‘
    }

    // ๆŒ‡ๅฎšไฝฟ็”จๆœฌๅœฐ้”
    @Lock(keys = "#id", provider = LocalLockProvider.class)
    public void processWithLocal(String id) {
        // ไธšๅŠก้€ป่พ‘
    }
}

็ปผๅˆ็คบไพ‹

@Service
public class ComprehensiveOrderService {

    private final OrderRepository orderRepository;
    
    /**
     * ๅฎŒๆ•ด็š„้”ไฝฟ็”จ็คบไพ‹
     * - ไฝฟ็”จ SpEL ่กจ่พพๅผๆž„ๅปบ Key
     * - ๆทปๅŠ ๅ‰็ผ€
     * - ่‡ชๅฎšไน‰็ญ‰ๅพ…ๆ—ถ้—ดๅ’Œ็งŸๆœŸ
     * - ไฝฟ็”จๅ…ฌๅนณ้”
     * - ๆทปๅŠ ๆ‹ฆๆˆชๅ™จ
     * - ่‡ชๅฎšไน‰ๅคฑ่ดฅๅค„็†
     */
    @Lock(
        keys = "#order.id",
        prefix = "order:process:",
        waitTime = 5000,
        leaseTime = 60000,
        lockType = LockType.FAIR,
        interceptor = LoggingLockInterceptor.class,
        failureHandler = RetryFailureHandler.class
    )
    public Order processOrder(Order order) {
        // ไธšๅŠก้€ป่พ‘
        return orderRepository.save(order);
    }
    
    /**
     * ๅคš Key ๅŠ ้”็คบไพ‹
     * ๅŒๆ—ถ้”ๅฎš็”จๆˆทๅ’Œ่ฎขๅ•
     */
    @Lock(
        keys = {"#userId", "#orderId"},
        prefix = "deduct:",
        interceptor = LoggingLockInterceptor.class
    )
    public void deductBalance(String userId, String orderId, BigDecimal amount) {
        // ๆ‰ฃๅ‡ไฝ™้ข้€ป่พ‘
    }
    
    /**
     * ่ฏปๅ†™้”็คบไพ‹
     */
    @Lock(
        keys = "#productId",
        lockType = LockType.READ,
        provider = RedissonLockProvider.class
    )
    public Product getProduct(String productId) {
        return orderRepository.findProductById(productId);
    }
    
    @Lock(
        keys = "#product.id",
        lockType = LockType.WRITE,
        provider = RedissonLockProvider.class
    )
    public Product updateProduct(Product product) {
        return orderRepository.saveProduct(product);
    }
}

@Lock ๆณจ่งฃๅฑžๆ€ง

ๅฑžๆ€ง ่ฏดๆ˜Ž ็ฑปๅž‹ ้ป˜่ฎคๅ€ผ
keys ้” Key ๆ•ฐ็ป„๏ผŒๆ”ฏๆŒ SpEL String[] {}
keyBuilder ่‡ชๅฎšไน‰ KeyBuilder ็ฑปๅž‹ Class<? extends KeyBuilder> KeyBuilder.class
keyAbsentPolicy Key ็ผบๅคฑ็ญ–็•ฅ KeyAbsentPolicy USE_METHOD_PATH
prefix ้” Key ๅ‰็ผ€ String ""
waitTime ็ญ‰ๅพ…่Žทๅ–้”ๆ—ถ้—ด(ms) long 3000
leaseTime ้”่ฟ‡ๆœŸๆ—ถ้—ด(ms) long 30000
timeUnit ๆ—ถ้—ดๅ•ไฝ TimeUnit MILLISECONDS
lockType ้”็ฑปๅž‹ LockType REENTRANT
failureHandler ๅŠ ้”ๅคฑ่ดฅๅค„็†ๅ™จ Class<? extends FailureHandler> FailureHandler.Default.class
failFast ๅคฑ่ดฅๅผ‚ๅธธ็ฑปๅž‹ Class<? extends RuntimeException> LockFailureException.class
provider ๆŒ‡ๅฎš LockProvider Class<? extends LockProvider> LockProvider.class
interceptor ๆ‹ฆๆˆชๅ™จ Class<? extends LockInterceptor> LockInterceptor.class

้…็ฝฎ่ฏดๆ˜Ž

ๅฎŒๆ•ด้…็ฝฎ็คบไพ‹

lock4j:
  # ๅ…จๅฑ€้…็ฝฎ
  enabled: true
  primary-provider: redissonLockProvider
  default-wait-time: 3000
  default-lease-time: 30000

  # Redisson ้…็ฝฎ
  redisson:
    enabled: true
    address: localhost:6379
    password: 
    database: 0
    connection-pool-size: 64
    connection-minimum-idle-size: 10
    timeout: 3000ms
    # ๅ•ๆœบๆจกๅผ
    # address: localhost:6379
    # ้›†็พคๆจกๅผ
    cluster:
      node-addresses:
        - redis://127.0.0.1:7000
        - redis://127.0.0.1:7001
        - redis://127.0.0.1:7002
    # ๅ“จๅ…ตๆจกๅผ
    sentinel:
      master-name: mymaster
      sentinel-addresses:
        - redis://127.0.0.1:26379
        - redis://127.0.0.1:26380

  # RedisTemplate ้…็ฝฎ
  redis:
    enabled: true
    host: localhost
    port: 6379
    password:
    database: 0
    connect-timeout: 3000ms

  # Zookeeper ้…็ฝฎ
  zookeeper:
    enabled: false
    connect-string: localhost:2181
    session-timeout: 30000
    connection-timeout: 10000
    base-path: /lock4j

  # Etcd ้…็ฝฎ
  etcd:
    enabled: false
    endpoints:
      - http://localhost:2379
    user:
    password:
    connect-timeout: 5000

  # ๆœฌๅœฐ้”้…็ฝฎ
  local:
    enabled: true

ๆžถๆž„่ฎพ่ฎก

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                      @Lock Annotation                        โ”‚
โ”‚   (@RedissonLock, @RedisTemplateLock, @ZookeeperLock, ...)  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                               โ”‚
                               โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                        LockAspect                           โ”‚
โ”‚                   (AOP ๅˆ‡้ขๆ‹ฆๆˆช)                             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                               โ”‚
                               โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    LockExecutor                             โ”‚
โ”‚    (SpEL่งฃๆž / KeyBuilder / Interceptor / ๅคฑ่ดฅๅค„็†)         โ”‚
โ”‚                                                             โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”         โ”‚
โ”‚  โ”‚ SpEL Parser โ”‚  โ”‚ KeyBuilder  โ”‚  โ”‚FailureHandlerโ”‚         โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜         โ”‚
โ”‚                                                             โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚  โ”‚              LockInterceptor (ๆต็จ‹้’ฉๅญ)               โ”‚   โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                               โ”‚
                               โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                  LockProviderFactory                        โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                               โ”‚
          โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
          โ–ผ            โ–ผ               โ–ผ            โ–ผ
    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
    โ”‚ Redisson โ”‚ โ”‚  Redis   โ”‚   โ”‚Zookeeper โ”‚ โ”‚   Etcd   โ”‚
    โ”‚ Provider โ”‚ โ”‚Template  โ”‚   โ”‚ Provider โ”‚ โ”‚ Provider โ”‚
    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

ๆŠ€ๆœฏๆ ˆ

  • JDK 17+
  • Spring Boot 3.x
  • Spring AOP
  • Redisson 3.25.2
  • Curator 5.5.0
  • jetcd 0.7.6
  • Lombok

ๆณจๆ„ไบ‹้กน

  1. ้” Key ่ฎพ่ฎก - ๅปบ่ฎฎไฝฟ็”จไธšๅŠก็›ธๅ…ณ็š„ๅ”ฏไธ€ๆ ‡่ฏ†ไฝœไธบ Key
  2. ้”่ถ…ๆ—ถๆ—ถ้—ด - leaseTime ๅบ”ๅคงไบŽไธšๅŠกๆ‰ง่กŒๆ—ถ้—ด
  3. ็ญ‰ๅพ…ๆ—ถ้—ด - waitTime ้œ€ๆ นๆฎไธšๅŠกๅœบๆ™ฏๆƒ่กก
  4. ๅคš Key ๅŠ ้” - ้‡‡็”จๅ…จ้ƒจๅŠ ้”็ญ–็•ฅ๏ผŒๅฟ…้กป่Žทๅ–ๆ‰€ๆœ‰้”ๆ‰็ฎ—ๆˆๅŠŸ
  5. ๅผ‚ๅธธๅค„็† - ไธšๅŠกๆ–นๆณ•ๆŠ›ๅ‡บๅผ‚ๅธธๆ—ถ๏ผŒ้”ไผš่‡ชๅŠจ้‡Šๆ”พ
  6. Watchdog - ๅฏ็”จๅŽ่‡ชๅŠจ็ปญๆœŸ๏ผŒ้˜ฒๆญขไธšๅŠกๆœชๆ‰ง่กŒๅฎŒ้”่ฟ‡ๆœŸ

License

MIT License

About

๐Ÿ” Distributed lock framework for Spring Boot with @lock annotation. Supports Redisson, RedisTemplate, Zookeeper, Etcd, and Local locks. Features SpEL, interceptors, watchdog, and multiple lock types.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages