分佈式工具的一次小升級⏫

前言

以前在作 秒殺架構實踐 時有提到對 distributed-redis-tool 的一次小升級,可是沒有細說。java

其實主要緣由是:git

秒殺時我作壓測:因爲集成了這個限流組件,併發又比較大,因此致使鏈接、斷開 Redis 很是頻繁。 最終致使獲取不了 Redis connection 的異常。github

池化技術

這就是一個典型的對稀缺資源使用不善致使的。redis

何爲稀缺資源?常見的有:spring

  • 線程
  • 數據庫鏈接
  • 網絡鏈接等

這些資源都有共同的特色:建立銷燬成本較高數據庫

這裏涉及到的 Redis 鏈接也屬於該類資源。api

咱們但願將這些稀有資源管理起來放到一個池子裏,當須要時就從中獲取,用完就放回去,不夠用時就等待(或返回)。服務器

這樣咱們只須要初始化並維護好這個池子,就能避免頻繁的建立、銷燬這些資源(也有資源長期未使用須要縮容的狀況)。網絡

一般咱們稱這項姿式爲池化技術,如常見的:架構

  • 線程池
  • 各類資源的鏈接池等。

爲此我將使用到 Redis 的 分佈式鎖分佈式限流 都升級爲利用鏈接池來獲取 Redis 的鏈接。

這裏以分佈式鎖爲例:

將使用的 api 修改成:

原有:

@Configuration
public class RedisLockConfig {

    @Bean
    public RedisLock build(){
        //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();
                
        return redisLock ;
    }

}
複製代碼

如今:

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

        return redisLock;
    }
}
複製代碼

將之前的 Jedis 修改成 JedisConnectionFactory,後續的 Redis 鏈接就可經過這個對象獲取。

而且顯示的傳入使用 RedisCluster 仍是單機的 Redis。

因此在真正操做 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;
        }
    }
    
    //獲取鏈接
    private Object getConnection() {
        Object connection ;
        if (type == RedisToolsConstant.SINGLE){
            RedisConnection redisConnection = jedisConnectionFactory.getConnection();
            connection = redisConnection.getNativeConnection();
        }else {
            RedisClusterConnection clusterConnection = jedisConnectionFactory.getClusterConnection();
            connection = clusterConnection.getNativeConnection() ;
        }
        return connection;
    }    
複製代碼

最大的改變就是將原有操做 Redis 的對象(T extends JedisCommands)改成從鏈接池中獲取。

因爲使用了 org.springframework.data.redis.connection.jedis.JedisConnectionFactory 做爲 Redis 鏈接池。

因此須要再使用時構件好這個對象:

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));

        //單機
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(config);

        //集羣
        //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("47.98.194.60", 6379));
        //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();

複製代碼

看起比較麻煩,須要構建對象的較多。

但整合 Spring 使用時就要清晰許多。

配合 Spring

Spring 很大的一個做用就是幫咱們管理對象,因此像上文那些看似很複雜的對象均可以交由它來管理:

<!-- jedis 配置 -->
    <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服務器中心 -->
    <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>
複製代碼

這個其實沒多少好說的,就算是換成 SpringBoot 也是建立 JedispoolConfig,connectionFactory,redisTemplate 這些 bean 便可。

總結

換爲鏈接池以後再進行壓測天然沒有出現獲取不了 Redis 鏈接的異常(併發達到必定的量也會出錯)說明更新是頗有必要的。

推薦有用到該組件的朋友都升級下,也歡迎提出 Issues 和 PR。

項目地址:

github.com/crossoverJi…

相關文章
相關標籤/搜索