连接池

这里主要介绍这些知识点~😝

image-20210601002212731

数据库连接池

回忆下 JDBC 的写法 ✍ ,就能感受到连接池的好处了~

void jdbcTest() throws ClassNotFoundException {
    String url = "jdbc:mysql://localhost:3306/db";
    String username = "";
    String password = "";

    Class.forName("com.mysql.jdbc.Driver");
    Connection connection = null;
    PreparedStatement preparedStatement = null;
    try {
        connection = DriverManager.getConnection(url, username, password);
        preparedStatement= connection.prepareStatement("select * from User where name=?");
        preparedStatement.setString(1, "Java4ye");
        preparedStatement.executeQuery();
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    } finally {
        try {
            if (preparedStatement!=null){
                preparedStatement.close();
            }
            if (connection != null){
                connection.close();
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

}

可以发现在这种模式下,每次都要去创建和销毁连接,造成了很大的浪费。我们可以尝试去优化下,

比如,参考上面 线程池的思想

  • 用完不销毁,先存起来;
  • 一开始的时候,就初始化多个连接;
  • 给连接加上最大空闲时间限定;
  • 加上最大连接数限定;

最后,你可以发现,你封装了一个简单的连接池了😄

img

数据库连接池原理图

数据库连接池原理图

数据库连接池有啥作用呢?

负责连接的管理,分配和释放,当连接的空闲时间超过最大空闲时间时,会释放连接,避免连接泄露

常见的数据库连接池

这里就主要说说这个 HikariCPDruid 了,其他的如 C3P0DBCP 等就不说啦(主要是没用上 哈哈哈)

HikariCP

HikariCPSpringboot2 开始默认使用的连接池(官方认证~🐷),速度最快

优点
  1. 字节码精简 :优化代码,编译后的字节码最少,这样,CPU缓存可以加载更多的程序代码;
  2. 优化代理和拦截器 :减少代码,例如 HikariCP 的 Statement proxy 只有100行代码,只有 BoneCP 的十分之一;
  3. 自定义数组类型(FastStatementList)代替ArrayList :避免每次get()调用都要进行range check,避免调用remove()时的从头到尾的扫描;
  4. 自定义集合类型(ConcurrentBag) :提高并发读写的效率;
  5. 其他针对 BoneCP 缺陷的优化。
源码

有机会可以研究看看 ~

下面这段代码是 HikariCPgetConnection() 的源码~ 给大家感受下👇 ([[HikariCP 源码]])

   // ***********************************************************************
   //                          DataSource methods
   // ***********************************************************************

   /** {@inheritDoc} */
   @Override
   public Connection getConnection() throws SQLException
   {
      if (isClosed()) {
         throw new SQLException("HikariDataSource " + this + " has been closed.");
      }

      if (fastPathPool != null) {
         return fastPathPool.getConnection();
      }

      // See http://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java
      HikariPool result = pool;
      if (result == null) {
         synchronized (this) {
            result = pool;
            if (result == null) {
               validate();
               LOGGER.info("{} - Starting...", getPoolName());
               try {
                  pool = result = new HikariPool(this);
                  this.seal();
               }
               catch (PoolInitializationException pie) {
                  if (pie.getCause() instanceof SQLException) {
                     throw (SQLException) pie.getCause();
                  }
                  else {
                     throw pie;
                  }
               }
               LOGGER.info("{} - Start completed.", getPoolName());
            }
         }
      }

      return result.getConnection();
   }

看下来就捕捉到单例设计模式的影子~

小伙伴们可以看看之前这篇 《一文带你看遍单例模式的八个例子,面试再也不怕被问了

是不是用到了单例的 双重检查锁模式 😄

配置属性

有这么多!🐷

private static final char[] ID_CHARACTERS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
private static final long CONNECTION_TIMEOUT = SECONDS.toMillis(30);
private static final long VALIDATION_TIMEOUT = SECONDS.toMillis(5);
private static final long IDLE_TIMEOUT = MINUTES.toMillis(10);
private static final long MAX_LIFETIME = MINUTES.toMillis(30);
private static final int DEFAULT_POOL_SIZE = 10;

private static boolean unitTest = false;

// Properties changeable at runtime through the HikariConfigMXBean
//
private volatile String catalog;
private volatile long connectionTimeout;
private volatile long validationTimeout;
private volatile long idleTimeout;
private volatile long leakDetectionThreshold;
private volatile long maxLifetime;
private volatile int maxPoolSize;
private volatile int minIdle;
private volatile String username;
private volatile String password;

// Properties NOT changeable at runtime
//
private long initializationFailTimeout;
private String connectionInitSql;
private String connectionTestQuery;
private String dataSourceClassName;
private String dataSourceJndiName;
private String driverClassName;
private String exceptionOverrideClassName;
private String jdbcUrl;
private String poolName;
private String schema;
private String transactionIsolationName;
private boolean isAutoCommit;
private boolean isReadOnly;
private boolean isIsolateInternalQueries;
private boolean isRegisterMbeans;
private boolean isAllowPoolSuspension;
private DataSource dataSource;
private Properties dataSourceProperties;
private ThreadFactory threadFactory;
private ScheduledExecutorService scheduledExecutor;
private MetricsTrackerFactory metricsTrackerFactory;
private Object metricRegistry;
private Object healthCheckRegistry;
private Properties healthCheckProperties;

private volatile boolean sealed;

默认值👉

image-20210515200950942

简单介绍下上面红框中的配置:

CONNECTION_TIMEOUT = SECONDS.toMillis(30); // 连接超时,默认 30 秒
VALIDATION_TIMEOUT = SECONDS.toMillis(5); // 验证连接有效性的超时时间,默认 5 秒
IDLE_TIMEOUT = MINUTES.toMillis(10); // 空闲连接存活最大时间 , 默认 10 分钟
MAX_LIFETIME = MINUTES.toMillis(30); // 连接最大存活时间 , 默认 30 分钟
DEFAULT_POOL_SIZE = 10;             // 默认连接池大小 , 10

一些常见的配置

spring:
  datasource:
    hikari:
      # 最小空闲连接数量
      minimum-idle: 5
      # 连接超时,默认 30 秒
      connection-timeout: 30
      # 空闲连接存活最大时间 , 默认 10 分钟
      idle-timeout: 3
      # 连接池最大连接数,默认是10
      maximum-pool-size: 20
      # 连接池名称
      pool-name: 4yePool_HikariCP
      # 自动提交事务,默认 true
      auto-commit: true
      # 连接最长的生命周期,默认 30 分钟
      max-lifetime: 1800

魔力转转转

嘿嘿 再来介绍下阿里的 Druid 🐂

Druid

阿里云计算平台团队出品,为监控而生的数据库连接池

Alibaba-Druid

下面那个是 ApacheDruid ,两个是不同的! 一个是连接池,一个是数据库~

Apache Druid是一个高性能的实时分析型数据库

Druid连接池介绍

主要特点是监控,提供了web页面给我们监控 SQL,Session,Spring 等等~ 而且据说监控对性能的影响很小🐷

小伙伴们点击下面连接就可以看到啦~👇

介绍

常见问题

Druid 常见问题 可以在 githubwiki 中查看😃

常见问题

很多问题都可以在上面找到答案~ 比如

功能:

如图~ (原来这么多!)

功能

优点:

经历了阿里大规模验证!😄

是 阿里巴巴内部唯一使用的连接池,在内部数据库相关中间件 TDDL/DRDS 都内置使用强依赖了Druid连接池

Springboot 集成 druid 👇

集成文档

这里配置超级全,大家就直接看文档就好啦 😄

img

HttpClient连接池

http请求过程

如果所示,这是一个 Http 请求的大致过程

http请求过程

HTTP keep-alive 机制

上面的请求也是一个短链接的过程,每次请求结束就关闭连接了,很浪费资源,而长连接和短链接的最主要区别就是这个 keep-alive 机制。

这里简单说下这个 长连接 keep-alive

http1.0 中 ,请求头中是默认没有这个 Connection: keep-alive

而在 http1.1 中 , 请求头中默认是带上这个的。

TCP keep-alive (保活)机制

除了应用层的 HTTP 有这个机制外,传输层的 TCP 也有。

TCP 保活机制的主要特点是 每隔一段时间会通过心跳包检测对端是否存活

这里参考下 linux

我们可以通过 sysctl -a | grep keepalive 查看内核参数

net.ipv4.tcp_keepalive_intvl = 75 
net.ipv4.tcp_keepalive_probes = 9
net.ipv4.tcp_keepalive_time = 7200
  • 保活时间(tcp_keepalive_time)默认:7200 秒
  • 保活时间间隔(tcp_keepalive_intvl)默认:75 秒
  • 探测循环次数(tcp_keepalive_probes)默认:9 次

这里的意思是,在两个钟(7200秒)内没有收到报文的话,会每隔75秒发送一个 保活探测包 ,重复9 次,直到收到响应,到达9次的话,如果还是没有响应,就会关闭连接。

保活机制的区别

HTTP 是为了复用连接 , 而 TCP 是为了保证对端存活,如果对端挂了的话,会关闭 TCP 连接。

连接池

这里和大家扯这么多主要是为了简单说说这个 Http 建立连接的麻烦 ,哈哈哈 频繁的创建和销毁连接很不友好。(说来说去都是这句话😝)

img

而且在 HTTP 的背后,还有着这么一个保活机制,这也意味着我们的连接池在实现这个连接的复用时,还要考虑这个Keep-alive 机制~

所以。。说了这么多,还是来看看这个 apachehttpcomponents 中的 httpclient 有啥秘密叭 😝

HttpClient 介绍

总体上分为下面七大块

  1. Fundamentals
  2. Connection management
  3. HTTP state management
  4. HTTP authentication
  5. Fluent API
  6. HTTP Caching
  7. Advanced topics
Keep Alive 策略

Keep Alive 策略

官网介绍

The HTTP specification does not specify how long a persistent connection may be and should be kept alive. Some HTTP servers use a non-standard Keep-Alive header to communicate to the client the period of time in seconds they intend to keep the connection alive on the server side. HttpClient makes use of this information if available.

If the Keep-Alive header is not present in the response, HttpClient assumes the connection can be kept alive indefinitely.

可以看到如果返回头中没有设置这个 Keep-Avlie 的话,HttpClient 会认为它是无限期存活的!

However, many HTTP servers in general use are configured to drop persistent connections after a certain period of inactivity in order to conserve system resources, quite often without informing the client. In case the default strategy turns out to be too optimistic, one may want to provide a custom keep-alive strategy.

这里直接用官网的简单例子运行下,从debug日志中可以看到,没配置的话,确实输出了 kept alive indefinitely

debug日志

代码 demo
 public static void main(String[] args) throws IOException {
        CloseableHttpClient httpclient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet("http://www.baidu.com");
        CloseableHttpResponse response1 = httpclient.execute(httpGet);
// The underlying HTTP connection is still held by the response object
// to allow the response content to be streamed directly from the network socket.
// In order to ensure correct deallocation of system resources
// the user MUST call CloseableHttpResponse#close() from a finally clause.
// Please note that if response content is not fully consumed the underlying
// connection cannot be safely re-used and will be shut down and discarded
// by the connection manager.
        try {
            System.out.println(response1.getStatusLine());
            HttpEntity entity1 = response1.getEntity();
            // do something useful with the response body
            // and ensure it is fully consumed
            EntityUtils.consume(entity1);
        } finally {
            response1.close();
        }

        HttpPost httpPost = new HttpPost("http://www.baidu.com");
        List <NameValuePair> nvps = new ArrayList <NameValuePair>();
        nvps.add(new BasicNameValuePair("username", "vip"));
        nvps.add(new BasicNameValuePair("password", "secret"));
        httpPost.setEntity(new UrlEncodedFormEntity(nvps));
        CloseableHttpResponse response2 = httpclient.execute(httpPost);

        try {
            System.out.println(response2.getStatusLine());
            HttpEntity entity2 = response2.getEntity();
            // do something useful with the response body
            // and ensure it is fully consumed
            EntityUtils.consume(entity2);
        } finally {
            response2.close();
        }
    }
源码分析

嘿嘿 ,来都来了,就顺手 debug 分析下上面这个 execute 方法~

从下图中我们可以发现,这里会去调用 reuseStrategy.keepAlive() 做判断,接着通过keepAliveStrategy.getKeepAliveDuration 去获取该连接的存活时间 🐷

源码

这两个策略分别为 重用策略 ConnectionReuseStrategy 和 保活策略 ConnectionKeepAliveStrategy

源码

省去debug步骤~ 🐷

我们直接来到这个默认的重用策略 DefaultConnectionReuseStrategy ,来看看这里是怎么去判断这个连接可以不可以重用叭~

keepAlive 源码

public boolean keepAlive(HttpResponse response, HttpContext context) {
    Args.notNull(response, "HTTP response");
    Args.notNull(context, "HTTP context");
    if (response.getStatusLine().getStatusCode() == 204) {
        Header clh = response.getFirstHeader("Content-Length");
        if (clh != null) {
            try {
                int contentLen = Integer.parseInt(clh.getValue());
                if (contentLen > 0) {
                    return false;
                }
            } catch (NumberFormatException var11) {
            }
        }

        Header teh = response.getFirstHeader("Transfer-Encoding");
        if (teh != null) {
            return false;
        }
    }

    HttpRequest request = (HttpRequest)context.getAttribute("http.request");
    if (request != null) {
        try {
            BasicTokenIterator ti = new BasicTokenIterator(request.headerIterator("Connection"));

            while(ti.hasNext()) {
                String token = ti.nextToken();
                if ("Close".equalsIgnoreCase(token)) {
                    return false;
                }
            }
        } catch (ParseException var13) {
            return false;
        }
    }

    ProtocolVersion ver = response.getStatusLine().getProtocolVersion();
    Header teh = response.getFirstHeader("Transfer-Encoding");
    if (teh != null) {
        if (!"chunked".equalsIgnoreCase(teh.getValue())) {
            return false;
        }
    } else if (this.canResponseHaveBody(request, response)) {
        Header[] clhs = response.getHeaders("Content-Length");
        if (clhs.length != 1) {
            return false;
        }

        Header clh = clhs[0];

        try {
            long contentLen = Long.parseLong(clh.getValue());
            if (contentLen < 0L) {
                return false;
            }
        } catch (NumberFormatException var10) {
            return false;
        }
    }

    HeaderIterator headerIterator = response.headerIterator("Connection");
    if (!headerIterator.hasNext()) {
        headerIterator = response.headerIterator("Proxy-Connection");
    }

    if (headerIterator.hasNext()) {
        try {
            TokenIterator ti = new BasicTokenIterator(headerIterator);
            boolean keepalive = false;

            while(ti.hasNext()) {
                String token = ti.nextToken();
                if ("Close".equalsIgnoreCase(token)) {
                    return false;
                }

                if ("Keep-Alive".equalsIgnoreCase(token)) {
                    keepalive = true;
                }
            }

            if (keepalive) {
                return true;
            }
        } catch (ParseException var12) {
            return false;
        }
    }

    return !ver.lessEquals(HttpVersion.HTTP_1_0);
}

哈哈哈 不想看分析的话 往下滑动一点点,有图~ 😝

分析

  1. 判断 HTTP 响应头中的状态码是不是 204 ,是的话进入下面的判断

    • 响应头中是否有 Content-Length ,有的话看它的值是否大于0,大于的时候 不重用

    • 响应头 中是否有 Transfer-Encoding ,有的话 不重用

      ( Tip: 204 表示 No Content:服务器成功处理了请求,但没返回任何内容 ,但是上面两种都表示还有内容,是错误的❌,所以不重用)

  2. 判断 请求头中是否有 Connection:Close ,有的话也 不重用

  3. 判断 响应头 中是否有 Transfer-Encoding,有的话看它的值,如果值 不等于 chunked不重用

  4. 判断 响应头中是否有 Content-Length , 有的话看它的值,如果值 小于 0 , 不重用

  5. 判断 响应头中是否有 Connection 或者 Proxy-Connection 其中的一个 ,有的话看它的值,如果是 Close不重用,如果是 Keep-Alive 重用

  6. 最后,如果上面的判断条件都不成立,会判断 http 的版本是不是 小于 1.0,是的话也返回 false

贴心的 4ye 居然画了这么详细的 流程图😝 (感谢老板的 一键三连 😝)

HttpClient重用策略

这里要注意,连接池中有两个默认的参数很重要(下图👇),而且官网的 demo 肯定是不能直接用在生产环境下的, 不然… 等监控报警的时候,就有得难受了 哈哈

下面整理了一些配置 给小伙伴们参考下~ 😝

如下图
HttpClientConnectionManager

这两个分别是

路由最大连接数defaultMaxPerRoute ,默认值 2 ,表示对某个 ip / 路由 一次能处理的最大并发数 。例如,如果我去请求百度,则同一时刻,最多能处理两个请求,所以 别忘了修改它!🐷 ,不然你的连接池配多少连接都没用~

总连接数maxTotal , 默认值 20 , 这个表示总的连接数,即 连接的最大并发数是20

HttpClientConnectionManager

PoolingHttpClientConnectionManager 设置
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();
poolingHttpClientConnectionManager.setMaxTotal(6);
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(1);
poolingHttpClientConnectionManager.setMaxPerRoute(new HttpRoute(new HttpHost("www.baidu.com")),4);

如上所示,还可以自定义某个 route 的最大值,效果如下~

DEBUG org.apache.http.impl.conn.PoolingHttpClientConnectionManager - Connection leased: [id: 41][route: {}->http://www.baidu.com:80][total available: 1; route allocated: 2 of 4; total allocated: 6 of 6]

其他参数 如 SocketConfigRequestConfig 等也要进行相应的配置,设置 等待数据的超时时间 和 请求超时时间 等,还有 重发策略 serviceUnavailStrategyKeep-Alive 策略 ConnectionKeepAliveStrategy

img

RequestConfig 设置
  1. connectionRequestTimout:从连接池获取连接超时

  2. connetionTimeout:连接服务器超时

  3. socketTimeout : 等待数据超时

RequestConfig requestConfig = RequestConfig.custom()
        .setConnectionRequestTimeout(1000) 
        .setConnectTimeout(1000)
        .setSocketTimeout(1000).build();
ConnectionKeepAliveStrategy (保活策略)设置

代码如下

ConnectionKeepAliveStrategy myStrategy = (response, context) -> {
            // Honor 'keep-alive' header
            HeaderElementIterator it = new BasicHeaderElementIterator(
                    response.headerIterator(HTTP.CONN_KEEP_ALIVE));
            while (it.hasNext()) {
                HeaderElement he = it.nextElement();
                String param = he.getName();
                String value = he.getValue();
                if (value != null && param.equalsIgnoreCase("timeout")) {
                    try {
                        return Long.parseLong(value) * 1000;
                    } catch (NumberFormatException ignore) {
                    }
                }
            }
            HttpHost target = (HttpHost) context.getAttribute(
                    HttpClientContext.HTTP_TARGET_HOST);
            if ("www.baidu.com".equalsIgnoreCase(target.getHostName())) {
                // Keep alive for 5 seconds only
                return 5 * 1000;
            } else {
                // otherwise keep alive for 30 seconds
                return 30 * 1000;
            }
        };

okhttp 就不展开啦,嘿嘿,这里的连接池使用场面也挺多的,比如我们接着要来讲的 RestTemplate ,还有这个 Feign (这个就先记着啦 嘿嘿 有时间再补上)

现在来简单说下怎么在 RestTemplate 中使用这个连接池~ 🐷

RestTemplate

它提供了一个简单的 SimpleClientHttpRequestFactory , 该类里面主要有 connetionTimeoutreadTimeout 这两个超时设置,额 实在是太简单了… 大部分时候还是不能满足的,所以我们还是要选择其他的连接池呀~ !

image-20210501203538974

可以看到上面中还有 OkHttp ,还有 Netty 等等,小伙伴们可以根据需要选择~ (๑•̀ㅂ•́)و✧

不过为啥 Netty 会标记为已过期了呢?

嘿嘿,这里下载了 Netty4ClientHttpRequestFactory 源码 ,可以看到 第一个红框里说,每次 http 请求的连接都会被关闭 。

这显然不能重用连接以及保持长连接了~ 😄

image-20210509224325366

配置 HttpClient

我们也可以这样配置,就可以使用到这个 HttpClient 了 。

@Configuration
public class RestTemplateConfig {
    @Bean
    public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
        RestTemplate restTemplate = new RestTemplate(factory);
        // 支持中文编码
        restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(Charset.forName("UTF-8")));
        return restTemplate;
    }

    @Bean
    public ClientHttpRequestFactory httpComponentsClientHttpRequestFactory() {
        return  new HttpComponentsClientHttpRequestFactory(httpClient());
    }
    @Bean
    public HttpClient httpClient() {

        PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();
        poolingHttpClientConnectionManager.setMaxTotal(6);
        poolingHttpClientConnectionManager.setDefaultMaxPerRoute(1);
        poolingHttpClientConnectionManager.setMaxPerRoute(new HttpRoute(new HttpHost("www.baidu.com")), 4);

        ConnectionKeepAliveStrategy myStrategy = (response, context) -> {
            // Honor 'keep-alive' header
            HeaderElementIterator it = new BasicHeaderElementIterator(
                    response.headerIterator(HTTP.CONN_KEEP_ALIVE));
            while (it.hasNext()) {
                HeaderElement he = it.nextElement();
                String param = he.getName();
                String value = he.getValue();
                if (value != null && param.equalsIgnoreCase("timeout")) {
                    try {
                        return Long.parseLong(value) * 1000;
                    } catch (NumberFormatException ignore) {
                    }
                }
            }
            HttpHost target = (HttpHost) context.getAttribute(
                    HttpClientContext.HTTP_TARGET_HOST);
            if ("www.baidu.com".equalsIgnoreCase(target.getHostName())) {
                // Keep alive for 5 seconds only
                return 5 * 1000;
            } else {
                // otherwise keep alive for 30 seconds
                return 30 * 1000;
            }
        };

        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(5000)
                .setConnectTimeout(10000)
                .setSocketTimeout(5000).build();

       return HttpClients.custom()
                .setConnectionManager(poolingHttpClientConnectionManager)
                .setKeepAliveStrategy(myStrategy)
                .setDefaultRequestConfig(requestConfig)
                .build();
    }

}

img

前往下一站,Redis 连接池

Redis 连接池

redis 的官网中 ,我们可以发现有下面这些客户端

客户端地址

image-20210531085114518

这里我们主要介绍这个 lettuce

Jedis 和 Lettuce

好久以前,在花菜🥦 lettuce 还没有出现以前,Springboot 默认使用的是Jedis

为啥现在默认使用的是这个 lettuce 呢?

作者的回答

image-20210510080302046

嘿嘿,翻译下作者的原话 😝

  1. Jedis 是一个直连Redis 客户端,在多线程环境下共享同一个 Jedis 实例,这是线程不安全的。
  2. 在多线程环境中使用 Jedis 的方法是使用连接池。每个使用 Jedis 的并发线程在Jedis 交互期间获得自己的 Jedis 实例。连接池是以每个 Jedis 实例的物理连接为代价的,这增加了 Redis 连接的数量。
  3. lettuce 是建立在 netty 和连接实例(StatefulRedisConnection)之上,可以跨多个线程共享。因此,多线程应用程序可以使用单个连接而不考虑与 Lettuce 交互的并发线程数。
配置
<!-- spring boot redis 缓存引入 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- lettuce pool 缓存连接池 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>
@Bean
public LettuceConnectionFactory lettuceConnectionFactory() {
    GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
    genericObjectPoolConfig.setMaxIdle(maxIdle);
    genericObjectPoolConfig.setMinIdle(minIdle);
    genericObjectPoolConfig.setMaxTotal(maxActive);
    genericObjectPoolConfig.setMaxWaitMillis(maxWait);
    genericObjectPoolConfig.setTimeBetweenEvictionRunsMillis(100);
    RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
    redisStandaloneConfiguration.setDatabase(database);
    redisStandaloneConfiguration.setHostName(host);
    redisStandaloneConfiguration.setPort(port);
    redisStandaloneConfiguration.setPassword(RedisPassword.of(password));
    LettuceClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder()
            .commandTimeout(Duration.ofMillis(timeout))
            .shutdownTimeout(Duration.ofMillis(shutDownTimeout))
            .poolConfig(genericObjectPoolConfig)
            .build();

    LettuceConnectionFactory factory = new LettuceConnectionFactory(redisStandaloneConfiguration, clientConfig);
    factory.setShareNativeConnection(false);
    return factory;
}

注意这里,如果想要连接池生效的话,必须设置配置这句话,不然默认只用一条连接的🐖

lettuceConnectionFactory.setShareNativeConnection(false);

特意去官网看了下,发现真是这样操作的 (⊙﹏⊙)

image-20210531220518918

而且当你配置了最小连接数时,你会发现在 redis 中,查到的连接数是比你配置的多一个,额 目前还不知道是啥连接,先记录下 嘿嘿~

img

最后

这篇文章就分享到这里啦,至于 常量池 又要夹着 jvm ,还有字节码这一块来说,所以还是另外再写✍ 啦!

欢迎关注,交个朋友呀!! ( •̀ ω •́ )y

文章首发于公众号~

地址在这~

作者简介 :Java4ye , 你好呀!!😝

公众号: Java4ye 博主滴个人公众号~ ,嘿嘿 喜欢就支持下啦 😋

让我们开始这一场意外的相遇吧!~

欢迎留言!谢谢支持!ヾ(≧▽≦*)o

1

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐