All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.crazycake.shiro.LettuceRedisClusterManager Maven / Gradle / Ivy

package org.crazycake.shiro;

import io.lettuce.core.*;
import io.lettuce.core.cluster.ClusterClientOptions;
import io.lettuce.core.cluster.RedisClusterClient;
import io.lettuce.core.cluster.api.StatefulRedisClusterConnection;
import io.lettuce.core.cluster.api.async.RedisAdvancedClusterAsyncCommands;
import io.lettuce.core.cluster.api.async.RedisClusterAsyncCommands;
import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands;
import io.lettuce.core.cluster.api.sync.RedisClusterCommands;
import io.lettuce.core.cluster.models.partitions.ClusterPartitionParser;
import io.lettuce.core.cluster.models.partitions.Partitions;
import io.lettuce.core.codec.ByteArrayCodec;
import io.lettuce.core.support.ConnectionPoolSupport;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.crazycake.shiro.exception.PoolException;

import java.time.Duration;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

/**
 * @author Teamo
 * @since 2022/05/19
 */
public class LettuceRedisClusterManager implements IRedisManager {

    /**
     * Comma-separated list of "host:port" pairs to bootstrap from. This represents an
     * "initial" list of cluster nodes and is required to have at least one entry.
     */
    private List nodes;

    /**
     * Default value of count.
     */
    private static final int DEFAULT_COUNT = 100;

    /**
     * timeout for RedisClient try to connect to redis server, not expire time! unit seconds.
     */
    private Duration timeout = RedisURI.DEFAULT_TIMEOUT_DURATION;

    /**
     * Redis database.
     */
    private int database = 0;

    /**
     * Redis password.
     */
    private String password;

    /**
     * Whether to enable async.
     */
    private boolean isAsync = true;

    /**
     * The number of elements returned at every iteration.
     */
    private int count = DEFAULT_COUNT;

    /**
     * genericObjectPoolConfig used to initialize GenericObjectPoolConfig object.
     */
    private GenericObjectPoolConfig> genericObjectPoolConfig = new GenericObjectPoolConfig<>();

    /**
     * GenericObjectPool.
     */
    private volatile GenericObjectPool> genericObjectPool;

    /**
     * ClusterClientOptions used to initialize RedisClient.
     */
    private ClusterClientOptions clusterClientOptions = ClusterClientOptions.create();

    private void initialize() {
        if (genericObjectPool == null) {
            synchronized (LettuceRedisClusterManager.class) {
                if (genericObjectPool == null) {
                    RedisClusterClient redisClusterClient = RedisClusterClient.create(getClusterRedisURI());
                    redisClusterClient.setOptions(clusterClientOptions);
                    StatefulRedisClusterConnection connect = redisClusterClient.connect(new ByteArrayCodec());
                    genericObjectPool = ConnectionPoolSupport.createGenericObjectPool(() -> connect, genericObjectPoolConfig);
                }
            }
        }
    }

    private StatefulRedisClusterConnection getStatefulConnection() {
        if (genericObjectPool == null) {
            initialize();
        }
        try {
            return genericObjectPool.borrowObject();
        } catch (Exception e) {
            throw new PoolException("Could not get a resource from the pool", e);
        }
    }

    private List getClusterRedisURI() {
        Objects.requireNonNull(nodes, "nodes must not be null!");
        return nodes.stream().map(node -> {
            String[] hostAndPort = node.split(":");
            RedisURI.Builder builder = RedisURI.builder()
                    .withHost(hostAndPort[0])
                    .withPort(Integer.parseInt(hostAndPort[1]))
                    .withDatabase(database)
                    .withTimeout(timeout);
            if (password != null) {
                builder.withPassword(password.toCharArray());
            }
            return builder.build();
        }).collect(Collectors.toList());
    }

    @Override
    public byte[] get(byte[] key) {
        if (key == null) {
            return null;
        }
        byte[] value = null;
        try (StatefulRedisClusterConnection connection = getStatefulConnection()) {
            if (isAsync) {
                RedisAdvancedClusterAsyncCommands async = connection.async();
                value = LettuceFutures.awaitOrCancel(async.get(key), timeout.getSeconds(), TimeUnit.SECONDS);
            } else {
                RedisAdvancedClusterCommands sync = connection.sync();
                value = sync.get(key);
            }
        }
        return value;
    }

    @Override
    public byte[] set(byte[] key, byte[] value, int expire) {
        if (key == null) {
            return null;
        }
        try (StatefulRedisClusterConnection connection = getStatefulConnection()) {
            if (isAsync) {
                RedisAdvancedClusterAsyncCommands async = connection.async();
                if (expire > 0) {
                    async.set(key, value, SetArgs.Builder.ex(expire));
                } else {
                    async.set(key, value);
                }
            } else {
                RedisAdvancedClusterCommands sync = connection.sync();
                if (expire > 0) {
                    sync.set(key, value, SetArgs.Builder.ex(expire));
                } else {
                    sync.set(key, value);
                }
            }
        }
        return value;
    }

    @Override
    public void del(byte[] key) {
        try (StatefulRedisClusterConnection connection = getStatefulConnection()) {
            if (isAsync) {
                RedisAdvancedClusterAsyncCommands async = connection.async();
                async.del(key);
            } else {
                RedisAdvancedClusterCommands sync = connection.sync();
                sync.del(key);
            }
        }
    }

    @Override
    public Long dbSize(byte[] pattern) {
        AtomicLong dbSize = new AtomicLong(0L);

        try (StatefulRedisClusterConnection connection = getStatefulConnection()) {
            if (isAsync) {
                RedisAdvancedClusterAsyncCommands async = connection.async();
                Partitions parse = ClusterPartitionParser.parse(LettuceFutures.awaitOrCancel(async.clusterNodes(), timeout.getSeconds(), TimeUnit.SECONDS));

                parse.forEach(redisClusterNode -> {
                    RedisClusterAsyncCommands clusterAsyncCommands = async.getConnection(redisClusterNode.getNodeId());

                    KeyScanCursor scanCursor = new KeyScanCursor<>();
                    scanCursor.setCursor(ScanCursor.INITIAL.getCursor());
                    ScanArgs scanArgs = ScanArgs.Builder.matches(pattern).limit(count);
                    while (!scanCursor.isFinished()) {
                        scanCursor = LettuceFutures.awaitOrCancel(clusterAsyncCommands.scan(scanCursor, scanArgs), timeout.getSeconds(), TimeUnit.SECONDS);
                        dbSize.addAndGet(scanCursor.getKeys().size());
                    }
                });
            } else {
                RedisAdvancedClusterCommands sync = connection.sync();
                Partitions parse = ClusterPartitionParser.parse(sync.clusterNodes());

                parse.forEach(redisClusterNode -> {
                    RedisClusterCommands clusterCommands = sync.getConnection(redisClusterNode.getNodeId());

                    KeyScanCursor scanCursor = new KeyScanCursor<>();
                    scanCursor.setCursor(ScanCursor.INITIAL.getCursor());
                    ScanArgs scanArgs = ScanArgs.Builder.matches(pattern).limit(count);
                    while (!scanCursor.isFinished()) {
                        scanCursor = clusterCommands.scan(scanCursor, scanArgs);
                        dbSize.addAndGet(scanCursor.getKeys().size());
                    }
                });
            }
        }
        return dbSize.get();
    }

    @Override
    public Set keys(byte[] pattern) {
        Set keys = new HashSet<>();

        try (StatefulRedisClusterConnection connection = getStatefulConnection()) {
            if (isAsync) {
                RedisAdvancedClusterAsyncCommands async = connection.async();
                Partitions parse = ClusterPartitionParser.parse(LettuceFutures.awaitOrCancel(async.clusterNodes(), timeout.getSeconds(), TimeUnit.SECONDS));

                parse.forEach(redisClusterNode -> {
                    RedisClusterAsyncCommands clusterAsyncCommands = async.getConnection(redisClusterNode.getNodeId());

                    KeyScanCursor scanCursor = new KeyScanCursor<>();
                    scanCursor.setCursor(ScanCursor.INITIAL.getCursor());
                    ScanArgs scanArgs = ScanArgs.Builder.matches(pattern).limit(count);
                    while (!scanCursor.isFinished()) {
                        scanCursor = LettuceFutures.awaitOrCancel(clusterAsyncCommands.scan(scanCursor, scanArgs), timeout.getSeconds(), TimeUnit.SECONDS);
                        keys.addAll(scanCursor.getKeys());
                    }
                });
            } else {
                RedisAdvancedClusterCommands sync = connection.sync();
                Partitions parse = ClusterPartitionParser.parse(sync.clusterNodes());

                parse.forEach(redisClusterNode -> {
                    RedisClusterCommands clusterCommands = sync.getConnection(redisClusterNode.getNodeId());

                    KeyScanCursor scanCursor = new KeyScanCursor<>();
                    scanCursor.setCursor(ScanCursor.INITIAL.getCursor());
                    ScanArgs scanArgs = ScanArgs.Builder.matches(pattern).limit(count);
                    while (!scanCursor.isFinished()) {
                        scanCursor = clusterCommands.scan(scanCursor, scanArgs);
                        keys.addAll(scanCursor.getKeys());
                    }
                });
            }
        }
        return keys;
    }

    public List getNodes() {
        return nodes;
    }

    public void setNodes(List nodes) {
        this.nodes = nodes;
    }

    public ClusterClientOptions getClusterClientOptions() {
        return clusterClientOptions;
    }

    public void setClusterClientOptions(ClusterClientOptions clusterClientOptions) {
        this.clusterClientOptions = clusterClientOptions;
    }

    public Duration getTimeout() {
        return timeout;
    }

    public void setTimeout(Duration timeout) {
        this.timeout = timeout;
    }

    public int getDatabase() {
        return database;
    }

    public void setDatabase(int database) {
        this.database = database;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public boolean isAsync() {
        return isAsync;
    }

    public void setIsAsync(boolean isAsync) {
        this.isAsync = isAsync;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public GenericObjectPoolConfig> getGenericObjectPoolConfig() {
        return genericObjectPoolConfig;
    }

    public void setGenericObjectPoolConfig(GenericObjectPoolConfig> genericObjectPoolConfig) {
        this.genericObjectPoolConfig = genericObjectPoolConfig;
    }

    public GenericObjectPool> getGenericObjectPool() {
        return genericObjectPool;
    }

    public void setGenericObjectPool(GenericObjectPool> genericObjectPool) {
        this.genericObjectPool = genericObjectPool;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy