redisson源码解析

redisson源码解析

  • 测试代码
  • lock
  • unlock
  • 阻塞
  • 监听锁释放
  • 看门狗

官方文档

https://github.com/redisson/redisson/wiki
中文版:https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95

redisson源码解析_第1张图片

测试代码

package org.example;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.TransportMode;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
 * @author WangChao
 */
public class TestLock {
    public static void main(String[] args) throws IOException, InterruptedException {
        new Thread(()->{
            try {
                Thread.sleep(3000);
                RLock lock = getrLock();
                lock.lock(15, TimeUnit.SECONDS);

            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }).start();

        RLock lock = getrLock();
        lock.lock(50, TimeUnit.SECONDS);
        Thread.sleep(7000);
        //lock.lockInterruptibly();
// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
//        boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
       lock.unlock();
    }

    private static RLock getrLock() {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379").setPassword("root");
        RedissonClient redisson = Redisson.create(config);
        return redisson.getLock("myLock");
    }
}

lock

   <T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
        internalLockLeaseTime = unit.toMillis(leaseTime);
        //通过eval命令执行Lua代码完成加锁操作
        return evalWriteAsync(getName(), LongCodec.INSTANCE, command,
                //判断当前key是否存在,如果不存在
                "if (redis.call('exists', KEYS[1]) == 0) then " +
                        //为value+1操作,如果value不存在则默认1,支持重入
                        "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
                        //设置过期时间,避免死锁,和 EXPIRE 区别在于毫秒数计数
                        "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                        "return nil; " +
                        "end; " +
                        //判断是否与当前获取锁的标记值相同
                        "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
                        //重入
                        "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
                        //重新刷新超时时间
                        "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                        "return nil; " +
                        "end; " +
                        //加锁失败,返回当前锁的过期时间
                        "return redis.call('pttl', KEYS[1]);",
                Collections.singletonList(getName()), internalLockLeaseTime, getLockName(threadId));
    }

unlock

protected RFuture<Boolean> unlockInnerAsync(long threadId) {
        return evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
                //如果key不存在,返回nil,确保锁只能被持有的释放.
                "if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +
                        "return nil;" +
                        "end; " +

                        //锁重入次数-1
                        "local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " +
                        //>0说明还有重入次数,则重置过期时间
                        "if (counter > 0) then " +
                        "redis.call('pexpire', KEYS[1], ARGV[2]); " +
                        "return 0; " +
                        "else " +

                        //删除锁
                        "redis.call('del', KEYS[1]); " +
                        //publish消息,将获取锁被阻塞的线程恢复重新获取锁
                        "redis.call('publish', KEYS[2], ARGV[1]); " +
                        "return 1; " +
                        "end; " +

                        "return nil;",
                Arrays.asList(getName(), getChannelName()), LockPubSub.UNLOCK_MESSAGE, internalLockLeaseTime, getLockName(threadId));
    }
    

阻塞

  private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException {
        //获取当前线程id
        long threadId = Thread.currentThread().getId();
        //加锁
        Long ttl = tryAcquire(leaseTime, unit, threadId);
        // lock acquired
        //获取锁成功,直接返回
        if (ttl == null) {
            return;
        }
        //加锁失败,则订阅消息,利用redis的pubsub提供一个通知机制,来减少不断重试,避免活锁
        RFuture<RedissonLockEntry> future = subscribe(threadId);
        if (interruptibly) {
            commandExecutor.syncSubscriptionInterrupted(future);
        } else {
            commandExecutor.syncSubscription(future);
        }

        try {
            while (true) {
                ttl = tryAcquire(leaseTime, unit, threadId);
                // lock acquired
                if (ttl == null) {
                    break;
                }

                // waiting for message
                if (ttl >= 0) {
                    try {
                        //调用Semaphore阻塞
                        future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
                    } catch (InterruptedException e) {
                        if (interruptibly) {
                            throw e;
                        }
                        future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
                    }
                } else {
                    if (interruptibly) {
                        future.getNow().getLatch().acquire();
                    } else {
                        future.getNow().getLatch().acquireUninterruptibly();
                    }
                }
            }
        } finally {
            unsubscribe(future, threadId);
        }
//        get(lockAsync(leaseTime, unit));
    }

监听锁释放

/**
 * Copyright (c) 2013-2020 Nikita Koksharov
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.redisson.pubsub;

import org.redisson.RedissonLockEntry;
import org.redisson.misc.RPromise;

/**
 * 
 * @author Nikita Koksharov
 *
 */
public class LockPubSub extends PublishSubscribe<RedissonLockEntry> {

    public static final Long UNLOCK_MESSAGE = 0L;
    public static final Long READ_UNLOCK_MESSAGE = 1L;

    public LockPubSub(PublishSubscribeService service) {
        super(service);
    }
    
    @Override
    protected RedissonLockEntry createEntry(RPromise<RedissonLockEntry> newPromise) {
        return new RedissonLockEntry(newPromise);
    }

    @Override
    protected void onMessage(RedissonLockEntry value, Long message) {
        if (message.equals(UNLOCK_MESSAGE)) {
            Runnable runnableToExecute = value.getListeners().poll();
            if (runnableToExecute != null) {
                runnableToExecute.run();
            }
            //说明有锁释放,则结束阻塞
            value.getLatch().release();
        } else if (message.equals(READ_UNLOCK_MESSAGE)) {
            while (true) {
                Runnable runnableToExecute = value.getListeners().poll();
                if (runnableToExecute == null) {
                    break;
                }
                runnableToExecute.run();
            }

            value.getLatch().release(value.getLatch().getQueueLength());
        }
    }

}

看门狗

维护一个普通线程,专门监控当前获取到锁的线程是否还存活,如果存活则,延长过期时间,

  private void renewExpiration() {
        ExpirationEntry ee = EXPIRATION_RENEWAL_MAP.get(getEntryName());
        if (ee == null) {
            return;
        }
       // 添加一个netty的Timeout回调任务,每(用户设置超时时间 / 3)毫秒执行一次
        Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
            @Override
            public void run(Timeout timeout) throws Exception {
                ExpirationEntry ent = EXPIRATION_RENEWAL_MAP.get(getEntryName());
                if (ent == null) {
                    return;
                }
                //获取第一个线程id
                Long threadId = ent.getFirstThreadId();
                if (threadId == null) {
                    return;
                }

                //重置过期时间,如果key存在,说明这个获取锁的线程还在执行
                RFuture<Boolean> future = renewExpirationAsync(threadId);
                future.onComplete((res, e) -> {
                    if (e != null) {
                        log.error("Can't update lock " + getName() + " expiration", e);
                        return;
                    }
                    
                    if (res) {
                        // reschedule itself
                        //递归调用本方法
                        renewExpiration();
                    }
                });
            }
        }, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
        
        ee.setTimeout(task);
    }

你可能感兴趣的:(大型网站架构)