简单介绍Redisson的AsyncSemaphore

153 阅读2分钟

源码解析

AsyncSemaphoreorg.redisson.pubsub包下的一个类,主要用做异步并发信号控制器,其构造函数如下

public class AsyncSemaphore {

    private int counter;
    private final Set<Runnable> listeners = new LinkedHashSet<Runnable>();
    //传入一个int做为自己可放行的信号量
    public AsyncSemaphore(int permits) {
        counter = permits;
    }

获取执行权限

public void acquire(Runnable listener) {
    boolean run = false;
    //锁的是对象本身
    synchronized (this) {
        //如果已经达最大执行量,把runner放入监听队列,排队执行
        if (counter == 0) {
            listeners.add(listener);
            return;
        }
        //如果未达最大执行量, -1信号 
        if (counter > 0) {
            counter--;
            run = true;
        }
    }
    //执行逻辑
    if (run) {
        listener.run();
    }
}

释放信号

public void release() {
    Runnable runnable = null;
    //锁的是对象本身
    synchronized (this) {
        //可执行量+1
        counter++;
        Iterator<Runnable> iter = listeners.iterator();
        //按顺序获取监听队列中的一个执行器
        if (iter.hasNext()) {
            runnable = iter.next();
            iter.remove();
        }
    }
    //执行监听队列中的下一个执行器
    if (runnable != null) {
        acquire(runnable);
    }
}

使用场景

该类在redisson的使用场景处处皆是,拿ClientConnectionsEntry的构造函数举例

public ClientConnectionsEntry(RedisClient client, int poolMinSize, int poolMaxSize, int subscribePoolMinSize, int subscribePoolMaxSize,
        ConnectionManager connectionManager, NodeType serverMode) {
    this.client = client;
    //用AsyncSemaphore管理最大链接数
    this.freeConnectionsCounter = new AsyncSemaphore(poolMaxSize);
    this.connectionManager = connectionManager;
    this.nodeType = serverMode;
    //用AsyncSemaphore管理最大订阅数
    this.freeSubscribeConnectionsCounter = new AsyncSemaphore(subscribePoolMaxSize);

    if (subscribePoolMaxSize > 0) {
        connectionManager.getConnectionWatcher().add(subscribePoolMinSize, subscribePoolMaxSize, freeSubscribeConnections, freeSubscribeConnectionsCounter);
    }
    connectionManager.getConnectionWatcher().add(poolMinSize, poolMaxSize, freeConnections, freeConnectionsCounter);
}

使用方式

AsyncSemaphore虽然会把每个访问acquire叫做listener,并持有了一个名为listenersLinkedHashSet,但是其本身并未实现监听机制,外部访问release方法来被动的触发监听 这里又要引入RPromise了, 其底层是netty实现,通过实现其operationComplete方法,在listener执行完后释放占用的资源.如ConnectionPool#createeConnection方法

private void createConnection(final boolean checkFreezed, final AtomicInteger requests, final ClientConnectionsEntry entry, final RPromise<Void> initPromise,
        final int minimumIdleSize, final AtomicInteger initializedConnections) {

    if ((checkFreezed && entry.isFreezed()) || !tryAcquireConnection(entry)) {
        Throwable cause = new RedisConnectionException(
                "Can't init enough connections amount! Only " + (minimumIdleSize - initializedConnections.get()) + " from " + minimumIdleSize + " were initialized. Server: "
                                    + entry.getClient().getAddr());
        initPromise.tryFailure(cause);
        return;
    }
    //👆上面代码已知clientConnectioonEntry是持有了控制链接池数量的Semaphore
    //这里就是创建semaphore执行的listener
    acquireConnection(entry, new Runnable() {
        
        @Override
        public void run() {
            RPromise<T> promise = connectionManager.newPromise();
            //创建链接的主要逻辑
            createConnection(entry, promise);
            //为listener执行完成后的回调
            promise.addListener(new FutureListener<T>() {
                @Override
                public void operationComplete(Future<T> future) throws Exception {
                    if (future.isSuccess()) {
                        T conn = future.getNow();

                        releaseConnection(entry, conn);
                    }
                    //在这里释放Semaphore的信号量
                    releaseConnection(entry);

                    if (!future.isSuccess()) {
                        Throwable cause = new RedisConnectionException(
                                "Can't init enough connections amount! Only " + (minimumIdleSize - initializedConnections.get()) + " from " + minimumIdleSize + " were initialized. Server: "
                                                    + entry.getClient().getAddr(), future.cause());
                        initPromise.tryFailure(cause);
                        return;
                    }

                    int value = initializedConnections.decrementAndGet();
                    if (value == 0) {
                        log.info("{} connections initialized for {}", minimumIdleSize, entry.getClient().getAddr());
                        if (!initPromise.trySuccess(null)) {
                            throw new IllegalStateException();
                        }
                    } else if (value > 0 && !initPromise.isDone()) {
                        if (requests.incrementAndGet() <= minimumIdleSize) {
                            createConnection(checkFreezed, requests, entry, initPromise, minimumIdleSize, initializedConnections);
                        }
                    }
                }
            });
        }
    });

}