preface

A minor upgrade to distributed- Redis-tool was mentioned earlier in my split-kill architecture practice, but I didn’t go into details.

In fact, the main reason is:

In the second kill, I did pressure test: due to the integration of the current limiting component and large concurrency, Redis was frequently connected and disconnected. The result is that the Redis Connection exception cannot be obtained.

Pooling technology

This is a classic example of poor use of scarce resources.

What is a scarce resource? Common ones are:

  • thread
  • Database connection
  • Network connection, etc.

These resources have a common characteristic: they are expensive to create and destroy.

The Redis connection involved here also belongs to this class of resources.

We want to manage these scarce resources into a pool, get them when we need them, put them back when we’re done, and wait (or return) when we’re not enough.

In this way, we only need to initialize and maintain the pool, so as to avoid frequent creation and destruction of these resources (or the situation of reducing the capacity of resources that have not been used for a long time).

This is often called a pooling technique, as is common:

  • The thread pool
  • Connection pools for various resources, etc.

To do this, I upgraded the distributed locks and distributed traffic limiting used by Redis to use connection pooling to obtain Redis connections.

A distributed lock is used as an example:

Change the API used to:

The original:

@Configuration
public class RedisLockConfig {

    @Bean
    public RedisLock build(a){
        //Need to get Redis connection 
        RedisLock redisLock = new RedisLock() ;
        HostAndPort hostAndPort = new HostAndPort("127.0.0.1".7000); JedisCluster jedisCluster =new JedisCluster(hostAndPort) ;
        RedisLock redisLock = new RedisLock.Builder(jedisCluster)
                .lockPrefix("lock_test")
                .sleepTime(100)
                .build();
                
        returnredisLock ; }}Copy the code

Now:

@Configuration
public class RedisLockConfig {
    private Logger logger = LoggerFactory.getLogger(RedisLockConfig.class);
    
    
    @Autowired
    private JedisConnectionFactory jedisConnectionFactory;
    
    @Bean
    public RedisLock build(a) {
        RedisLock redisLock = new RedisLock.Builder(jedisConnectionFactory,RedisToolsConstant.SINGLE)
                .lockPrefix("lock_")
                .sleepTime(100)
                .build();

        returnredisLock; }}Copy the code

Change the previous Jedis to JedisConnectionFactory, and subsequent Redis connections can be retrieved from this object.

And the incoming display uses RedisCluster or standalone Redis.

So you need to change it when you actually use Redis:

    public boolean tryLock(String key, String request) {
        //get connection
        Object connection = getConnection();
        String result ;
        if (connection instanceof Jedis){
            result =  ((Jedis) connection).set(lockPrefix + key, request, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, 10 * TIME);
            ((Jedis) connection).close();
        }else {
            result = ((JedisCluster) connection).set(lockPrefix + key, request, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, 10 * TIME);
            try {
                ((JedisCluster) connection).close();
            } catch (IOException e) {
                logger.error("IOException",e); }}if (LOCK_MSG.equals(result)) {
            return true;
        } else {
            return false; }}// Get the connection
    private Object getConnection(a) {
        Object connection ;
        if (type == RedisToolsConstant.SINGLE){
            RedisConnection redisConnection = jedisConnectionFactory.getConnection();
            connection = redisConnection.getNativeConnection();
        }else {
            RedisClusterConnection clusterConnection = jedisConnectionFactory.getClusterConnection();
            connection = clusterConnection.getNativeConnection() ;
        }
        return connection;
    }    
Copy the code

The biggest change is that the original object that operates on Redis (T extends JedisCommands) is fetched from the connection pool.

Thanks to the org. Springframework. Data. Redis. Connection. Jedis. JedisConnectionFactory as redis connection pool.

So if you need to use it again, construct this object:

        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxIdle(10);
        config.setMaxTotal(300);
        config.setMaxWaitMillis(10000);
        config.setTestOnBorrow(true);
        config.setTestOnReturn(true);

        RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
        redisClusterConfiguration.addClusterNode(new RedisNode("10.19.13.51".7000));

        / / single
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(config);

        / / cluster
        //JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(redisClusterConfiguration) ;
        jedisConnectionFactory.setHostName("47.98.194.60");
        jedisConnectionFactory.setPort(6379);
        jedisConnectionFactory.setPassword("");
        jedisConnectionFactory.setTimeout(100000);
        jedisConnectionFactory.afterPropertiesSet();
        / / jedisConnectionFactory. SetShardInfo (new JedisShardInfo (6379) "47.98.194.60,");
        //JedisCluster jedisCluster = new JedisCluster(hostAndPort);

        HostAndPort hostAndPort = new HostAndPort("10.19.13.51".7000);
        JedisCluster jedisCluster = new JedisCluster(hostAndPort);
        redisLock = new RedisLock.Builder(jedisConnectionFactory, RedisToolsConstant.SINGLE)
                .lockPrefix("lock_")
                .sleepTime(100)
                .build();

Copy the code

It looks like a lot of trouble, more objects need to be built.

But it’s much clearer when you integrate Spring use.

With the Spring

One of the big things Spring does is help us manage objects, so it can manage seemingly complex objects like the ones above:

   <! -- Jedis configuration -->
    <bean id="JedispoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxIdle" value="${redis.maxIdle}"/>
        <property name="maxTotal" value="${redis.maxTotal}"/>
        <property name="maxWaitMillis" value="${redis.maxWait}"/>
        <property name="testOnBorrow" value="${redis.testOnBorrow}"/>
        <property name="testOnReturn" value="${redis.testOnBorrow}"/>
    </bean>
    <! -- Redis Server Center -->
    <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="poolConfig" ref="JedispoolConfig"/>
        <property name="port" value="${redis.port}"/>
        <property name="hostName" value="${redis.host}"/>
        <property name="password" value="${redis.password}"/>
        <property name="timeout" value="${redis.timeout}"></property>
    </bean>
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="connectionFactory"/>
        <property name="keySerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
        </property>
        <property name="valueSerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
        </property>
    </bean>
Copy the code

This actually don’t have much to say, even with SpringBoot also create JedispoolConfig, connectionFactory, redisTemplate these beans.

conclusion

After the connection pool is changed, the pressure test naturally does not appear that the Redis connection cannot be obtained (there will be an error when the concurrency reaches a certain amount), indicating that it is necessary to update.

If you have used this component, please update it. Also, please raise Issues and PR.

Project Address:

Github.com/crossoverJi…