Redis:实例结合源码分析Jedis连接池原理以及Jedis连接池的实现

Posted 守夜人爱吃兔子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis:实例结合源码分析Jedis连接池原理以及Jedis连接池的实现相关的知识,希望对你有一定的参考价值。

 

目录

 

前言

示例

1. 添加依赖

2. 添加配置

3. 注入redis模板对象

4. 执行set操作

jedis连接池

1. redis自动配置

2. 创建JedisConnectionConfiguration

3. 创建JedisClientConfiguration

4. 创建单机配置RedisStandaloneConfiguration

5. 创建JedisConnectionFactory

6. 初始化JedisConnectionFactory

①创建连接池JedisPool:

②创建JedisFactory:

③初始化连接池:

7. 声明操作模板

8. 获取连接工厂

9. 获取连接

①创建连接:

②归还连接:

10. 连接管理

①驱除连接:

②创建连接:

结语


前言

我们都知道 Jedis 是 Redis 的 Java 实现客户端,提供了比较全面的Redis命令的支持,它集成了Redis的一些命令操作,封装了Redis的Java客户端,提供了连接池管理。而本文将从实例并结合源码分析jedis连接池原理,其中包括如何创建连接、释放连接、驱除连接以及如何确保最小空闲数量的连接


 

示例

1. 添加依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>

    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
    </dependency>
</dependencies>

2. 添加配置

spring:
  redis:
    url: redis://localhost:6379
    client-type: jedis
    jedis:
      pool:
        min-idle: 10
        max-active: 10

3. 注入redis模板对象

@Autowired
private StringRedisTemplate stringRedisTemplate;

4. 执行set操作

@Test
public void test() throws InterruptedException {
    stringRedisTemplate.opsForValue().set("hello", "world");
}

jedis连接池

在了解spring-boot-starter-xxx模块原理之前,我们需要先分析xxxAutoConfiguration

1. redis自动配置

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

配置注入功能通过@EnableConfigurationProperties注解开启后,RedisProperties对象中就会被自动注入在application.yml中关于redis的配置,@Import注解引入jedis、lettuce客户端连接配置,在此处引入的是JedisConnectionConfiguration配置,因为在yml配置中已声明了jedis客户端

2. 创建JedisConnectionConfiguration

protected RedisConnectionConfiguration(RedisProperties properties,
			ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider,
			ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider) {
    this.properties = properties;
    this.sentinelConfiguration = sentinelConfigurationProvider.getIfAvailable();
    this.clusterConfiguration = clusterConfigurationProvider.getIfAvailable();
}

注入RedisSentinelConfiguration、RedisProperties、RedisClusterConfiguration

3. 创建JedisClientConfiguration

private JedisClientConfiguration getJedisClientConfiguration(
			ObjectProvider<JedisClientConfigurationBuilderCustomizer> builderCustomizers) {
    // 1.设置客户端读取超时时间、连接超时时间等
    JedisClientConfigurationBuilder builder = applyProperties(JedisClientConfiguration.builder());
    RedisProperties.Pool pool = getProperties().getJedis().getPool();
    // 2.如果声明了连接池,设置连接池最小值、最大值等
    if (pool != null) {
        applyPooling(pool, builder);
    }
    if (StringUtils.hasText(getProperties().getUrl())) {
        customizeConfigurationFromUrl(builder);
    }
    builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
    // 3.构建JedisClientConfiguration
    return builder.build();
}

4. 创建单机配置RedisStandaloneConfiguration

protected final RedisStandaloneConfiguration getStandaloneConfig() {
    RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
    ConnectionInfo connectionInfo = parseUrl(this.properties.getUrl());
    // 1.设置主机
    config.setHostName(connectionInfo.getHostName());
    // 2.设置端口
    config.setPort(connectionInfo.getPort());'
    // 3.设置用户名
    config.setUsername(connectionInfo.getUsername());
    // 4.设置密码
    config.setPassword(RedisPassword.of(connectionInfo.getPassword()));
    // 5.设置数据库
    config.setDatabase(this.properties.getDatabase());
    return config;
}

5. 创建JedisConnectionFactory

private JedisConnectionFactory createJedisConnectionFactory(
			ObjectProvider<JedisClientConfigurationBuilderCustomizer> builderCustomizers) {
    JedisClientConfiguration clientConfiguration = getJedisClientConfiguration(builderCustomizers);
   	// 使用3.JedisClientConfiguration和4.RedisStandaloneConfiguration创建JedisConnectionFactory
    return new JedisConnectionFactory(getStandaloneConfig(), clientConfiguration);
}

JedisClientConfiguration中持有RedisStandaloneConfiguration和JedisClientConfiguration

 

6. 初始化JedisConnectionFactory

①创建连接池JedisPool:

protected Pool<Jedis> createRedisPool() {
    return new JedisPool(getPoolConfig(), getHostName(), getPort(), getConnectTimeout(), getReadTimeout(),
                         getUsername(), getPassword(), getDatabase(), getClientName(), isUseSsl(),
                         clientConfiguration.getSslSocketFactory().orElse(null), //
                         clientConfiguration.getSslParameters().orElse(null), //
                         clientConfiguration.getHostnameVerifier().orElse(null));
}

②创建JedisFactory:

public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port,
                 final int connectionTimeout, final int soTimeout, final String user, final String password,
                 final int database, final String clientName, final boolean ssl,
                 final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters,
                 final HostnameVerifier hostnameVerifier) {
    super(poolConfig, new JedisFactory(host, port, connectionTimeout, soTimeout, user, password,
                                       database, clientName, ssl, sslSocketFactory, sslParameters, hostnameVerifier));
}

③初始化连接池:

public void initPool(final GenericObjectPoolConfig poolConfig, PooledObjectFactory<T> factory) {
    if (this.internalPool != null) {
        try {
            closeInternalPool();
        } catch (Exception e) {
        }
    }

    this.internalPool = new GenericObjectPool<>(factory, poolConfig);
}

7. 声明操作模板

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean(name = "redisTemplate")
	@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
	public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

	@Bean
	@ConditionalOnMissingBean
	@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
	public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
		StringRedisTemplate template = new StringRedisTemplate();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}
}

8. 获取连接工厂

public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) {
		RedisConnectionFactory factory = getRequiredConnectionFactory();

9. 获取连接

protected Jedis fetchJedisConnector() {
    try {
		// 如果配置了连接池,则从连接池中获取连接
        if (getUsePool() && pool != null) {
            return pool.getResource();
        }

        Jedis jedis = createJedis();
        // force initialization (see Jedis issue #82)
        jedis.connect();

        potentiallySetClientName(jedis);
        return jedis;
    } catch (Exception ex) {
        throw new RedisConnectionFailureException("Cannot get Jedis connection", ex);
    }
}

①创建连接:

private PooledObject<T> create() throws Exception {
    int localMaxTotal = getMaxTotal();
    Boolean create = null;
    final PooledObject<T> p;
    try {
        // 先从连接池中获取连接,如果池中没有连接则通过上文创建的JedisFactory创建Jedis对象
        p = factory.makeObject();
        if (getTestOnCreate() && !factory.validateObject(p)) {
            createCount.decrementAndGet();
            return null;
        }
    }
    allObjects.put(new IdentityWrapper<>(p.getObject()), p);
    return p;
}

②归还连接:

RedisConnectionUtils.releaseConnection(conn, factory, enableTransactionSupport);

如果设置了连接池会将使用完的连接归还到池中

 

10. 连接管理

①驱除连接:

在上文初始化连接池时会启动一个定时器执行驱除器任务

public final void setTimeBetweenEvictionRunsMillis(
            final long timeBetweenEvictionRunsMillis) {
    this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
    startEvictor(timeBetweenEvictionRunsMillis);
}

来看下驱除器的作用

Perform numTests idle object eviction tests, evicting examined objects that meet the criteria for eviction. If testWhileIdle is true, examined objects are validated when visited (and removed if invalid); otherwise only objects that have been idle for more than minEvicableIdleTimeMillis are removed.

如果testWhileIdle设置为true,使用连接的时候需要校验连接是不是有效,无效就要移除连接;否则池中超过minEvicableIdleTimeMillis时间的连接也会被移除

②创建连接:

/**
 * Tries to ensure that the configured minimum number of idle instances are available in the pool.
 */
abstract void ensureMinIdle() throws Exception;

驱除器任务不仅仅会驱除满足条件的空闲连接,还会去创建满足min-idle数量的连接放入连接池中

 

结语

感谢阅读,如果文章对你有帮助的话记得点赞+关注支持一下,我是守夜人爱吃兔子,祝大家早日富可敌国

以上是关于Redis:实例结合源码分析Jedis连接池原理以及Jedis连接池的实现的主要内容,如果未能解决你的问题,请参考以下文章

Jedis 连接池实例

jedis连接redis

Jedis 连接池源码分析

Redis连接池Lettuce Jedis 区别

Jedis连接池配置参数

redis 连接池