当前位置: 首页 > news >正文

lettuce 默认情况下连接池参数不生效,源码分析

先说结论:

1.LettuceConnectionFactory 属性 shareNativeConnection 默认为true,要想连接池生效,该参数设置为false;
2.使用redisTemplate模版封装的pipeline没有意义,autoFlashCommands 默认为true;

spring2.0开始默认使用lettuce,lettuce和jedis一样支持使用连接池;这里不对比两款客户端的性能差异,只针对使用lettuce客户端执行命令获取连接的源码分析其逻辑;

版本说明:

spring-date-redis:2.3.9.RELEASE;

lettuce-core:5.3.7.RELEASE;

以单机版本reids说明;即:RedisStandaloneConfiguration,集群也是同样方法

以 stringRedisTemplate.opsForValue().get(key) 示例追踪说明;

点击进入get方法:

public V get(Object key) {return this.execute(new AbstractOperations<K, V>.ValueDeserializingRedisCallback(key) {protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {return connection.get(rawKey);}}, true);
}

接着进入execute方法:

@Nullable
<T> T execute(RedisCallback<T> callback, boolean exposeConnection) {return this.template.execute(callback, exposeConnection);
}

接着进入:org.springframework.data.redis.core.RedisTemplate#execute(org.springframework.data.redis.core.RedisCallback<T>, boolean, boolean);

@Nullable
public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) {Assert.isTrue(this.initialized, "template not initialized; call afterPropertiesSet() before using it");Assert.notNull(action, "Callback object must not be null");RedisConnectionFactory factory = this.getRequiredConnectionFactory();RedisConnection conn = null;Object var11;try {if (this.enableTransactionSupport) {conn = RedisConnectionUtils.bindConnection(factory, this.enableTransactionSupport);} else {conn = RedisConnectionUtils.getConnection(factory);}boolean existingConnection = TransactionSynchronizationManager.hasResource(factory);RedisConnection connToUse = this.preProcessConnection(conn, existingConnection);boolean pipelineStatus = connToUse.isPipelined();if (pipeline && !pipelineStatus) {connToUse.openPipeline();}RedisConnection connToExpose = exposeConnection ? connToUse : this.createRedisConnectionProxy(connToUse);T result = action.doInRedis(connToExpose);if (pipeline && !pipelineStatus) {connToUse.closePipeline();}var11 = this.postProcessResult(result, connToUse, existingConnection);} finally {RedisConnectionUtils.releaseConnection(conn, factory, this.enableTransactionSupport);}return var11;
}

到这里后进入获取连接的入口:conn = RedisConnectionUtils.getConnection(factory);

点进去到:org.springframework.data.redis.core.RedisConnectionUtils#doGetConnection

public static RedisConnection doGetConnection(RedisConnectionFactory factory, boolean allowCreate, boolean bind, boolean transactionSupport) {Assert.notNull(factory, "No RedisConnectionFactory specified");RedisConnectionUtils.RedisConnectionHolder connHolder = (RedisConnectionUtils.RedisConnectionHolder)TransactionSynchronizationManager.getResource(factory);if (connHolder != null) {if (transactionSupport) {potentiallyRegisterTransactionSynchronisation(connHolder, factory);}return connHolder.getConnection();} else if (!allowCreate) {throw new IllegalArgumentException("No connection found and allowCreate = false");} else {if (log.isDebugEnabled()) {log.debug("Opening RedisConnection");}RedisConnection conn = factory.getConnection();if (bind) {RedisConnection connectionToBind = conn;if (transactionSupport && isActualNonReadonlyTransactionActive()) {connectionToBind = createConnectionProxy(conn, factory);}connHolder = new RedisConnectionUtils.RedisConnectionHolder(connectionToBind);TransactionSynchronizationManager.bindResource(factory, connHolder);if (transactionSupport) {potentiallyRegisterTransactionSynchronisation(connHolder, factory);}return connHolder.getConnection();} else {return conn;}}
}

忽略事务相关逻辑,进入:factory.getConnection();

public RedisConnection getConnection() {if (this.isClusterAware()) {return this.getClusterConnection();} else {LettuceConnection connection = this.doCreateLettuceConnection(this.getSharedConnection(), this.connectionProvider, this.getTimeout(), this.getDatabase());connection.setConvertPipelineAndTxResults(this.convertPipelineAndTxResults);return connection;}
}

先看:this.getSharedConnection();核心逻辑this.shareNativeConnection 标志位判断,默认为true;

org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory#getSharedConnection

@Nullableprotected StatefulRedisConnection<byte[], byte[]> getSharedConnection() {return this.shareNativeConnection ? (StatefulRedisConnection)this.getOrCreateSharedConnection().getConnection() : null;}

在进入核心方法:this.getOrCreateSharedConnection().getConnection();this.connection同步只会创建一次

private LettuceConnectionFactory.SharedConnection<byte[]> getOrCreateSharedConnection() {synchronized(this.connectionMonitor) {if (this.connection == null) {this.connection = new LettuceConnectionFactory.SharedConnection(this.connectionProvider);}return this.connection;}}
@NullableStatefulConnection<E, E> getConnection() {synchronized(this.connectionMonitor) {if (this.connection == null) {this.connection = this.getNativeConnection();}if (LettuceConnectionFactory.this.getValidateConnection()) {this.validateConnection();}return this.connection;}}
private StatefulConnection<E, E> getNativeConnection() {return this.connectionProvider.getConnection(StatefulConnection.class);}
public <T extends StatefulConnection<?, ?>> T getConnection(Class<T> connectionType) {GenericObjectPool pool = (GenericObjectPool)this.pools.computeIfAbsent(connectionType, (poolType) -> {return ConnectionPoolSupport.createGenericObjectPool(() -> {return this.connectionProvider.getConnection(connectionType);}, this.poolConfig, false);});try {StatefulConnection<?, ?> connection = (StatefulConnection)pool.borrowObject();this.poolRef.put(connection, pool);return (StatefulConnection)connectionType.cast(connection);} catch (Exception var4) {throw new PoolException("Could not get a resource from the pool", var4);}}

这里只会从pool里获取一个链接,后面不会再获取

进入:this.doCreateLettuceConnection(this.getSharedConnection(), this.connectionProvider, this.getTimeout(), this.getDatabase())

protected LettuceConnection doCreateLettuceConnection(@Nullable StatefulRedisConnection<byte[], byte[]> sharedConnection, LettuceConnectionProvider connectionProvider, long timeout, int database) {LettuceConnection connection = new LettuceConnection(sharedConnection, connectionProvider, timeout, database);connection.setPipeliningFlushPolicy(this.pipeliningFlushPolicy);return connection;}

其中 LettuceConnection connection = new LettuceConnection(sharedConnection, connectionProvider, timeout, database); 每次会把同一个连接赋值给asyncSharedConn

LettuceConnection(@Nullable StatefulConnection<byte[], byte[]> sharedConnection, LettuceConnectionProvider connectionProvider, long timeout, int defaultDbIndex) {this.isClosed = false;this.isMulti = false;this.isPipelined = false;this.txResults = new LinkedList();this.convertPipelineAndTxResults = true;this.pipeliningFlushPolicy = LettuceConnection.PipeliningFlushPolicy.flushEachCommand();Assert.notNull(connectionProvider, "LettuceConnectionProvider must not be null.");this.asyncSharedConn = sharedConnection;this.connectionProvider = connectionProvider;this.timeout = timeout;this.defaultDbIndex = defaultDbIndex;this.dbIndex = this.defaultDbIndex;}

回到命令执行方法:org.springframework.data.redis.core.RedisTemplate#execute(org.springframework.data.redis.core.RedisCallback<T>, boolean, boolean) 

来到:T result = action.doInRedis(connToExpose);

会进到:org.springframework.data.redis.connection.DefaultStringRedisConnection#get(byte[])

this.delegate.get(key)会进入到:org.springframework.data.redis.connection.lettuce.LettuceStringCommands#get

进入this.getConnection;

org.springframework.data.redis.connection.lettuce.LettuceConnection#getConnection()

到这里就用到了之前在 LettuceConnection connection = new LettuceConnection(sharedConnection, connectionProvider, timeout, database); 方法里赋值给asyncSharedConn 的对象;

如果为null即上面shareNativeConnection为false,会走 this.getDedicatedConnection() :org.springframework.data.redis.connection.lettuce.LettucePoolingConnectionProvider#getConnection 从连接池里获取链接

获取到链接之后会进入:io.lettuce.core.AbstractRedisAsyncCommands#dispatch(io.lettuce.core.protocol.RedisCommand<K,V,T>)

至此已经能够看出shareNativeConnection参数true和false的区别

单独看下pipeline的执行方法,

首先pipeline获取链接的方法为:org.springframework.data.redis.connection.lettuce.LettuceConnection#getOrCreateDedicatedConnection

private StatefulConnection<byte[], byte[]> getOrCreateDedicatedConnection() {if (this.asyncDedicatedConn == null) {this.asyncDedicatedConn = this.doGetAsyncDedicatedConnection();}return this.asyncDedicatedConn;}

赋值给了asyncDedicatedConn;后面不在赋值;跟简单命令set/get使用的不是相同的链接;但是获取链接的方法少了synchronized同步关键字;猜想作者觉得pipeline使用场景并发不大不用同步;

然后找到pipeline命令执行方法,可以在此处断点验证,pipeline第一条命令执行完redis是否已经有值了:io.lettuce.core.protocol.DefaultEndpoint#write(io.lettuce.core.protocol.RedisCommand<K,V,T>)

这个属性autoFlushCommands 在对象创建时赋值,目前没有提供动态配置:

public DefaultEndpoint(ClientOptions clientOptions, ClientResources clientResources) {this.endpointId = ENDPOINT_COUNTER.incrementAndGet();this.sharedLock = new SharedLock();this.debugEnabled = logger.isDebugEnabled();this.closeFuture = new CompletableFuture();this.autoFlushCommands = true;this.inActivation = false;this.queueSize = 0;this.status = 0;LettuceAssert.notNull(clientOptions, "ClientOptions must not be null");LettuceAssert.notNull(clientOptions, "ClientResources must not be null");this.clientOptions = clientOptions;this.clientResources = clientResources;this.reliability = clientOptions.isAutoReconnect() ? DefaultEndpoint.Reliability.AT_LEAST_ONCE : DefaultEndpoint.Reliability.AT_MOST_ONCE;this.disconnectedBuffer = LettuceFactories.newConcurrentQueue(clientOptions.getRequestQueueSize());this.commandBuffer = LettuceFactories.newConcurrentQueue(clientOptions.getRequestQueueSize());this.boundedQueues = clientOptions.getRequestQueueSize() != 2147483647;this.rejectCommandsWhileDisconnected = isRejectCommand(clientOptions);}

相关文章:

lettuce 默认情况下连接池参数不生效,源码分析

先说结论&#xff1a; 1.LettuceConnectionFactory 属性 shareNativeConnection 默认为true&#xff0c;要想连接池生效&#xff0c;该参数设置为false; 2.使用redisTemplate模版封装的pipeline没有意义&#xff0c;autoFlashCommands 默认为true;spring2.0开始默认使用lettuc…...

《宇宙机器人》提示错误弹窗“找不到d3dx9_43.dll”是什么原因?“d3dx9_43.dll缺失”怎么解决?

电脑游戏运行时常见问题解析&#xff1a;《宇宙机器人》提示“找不到d3dx9_43.dll”的解决之道 TGA2024落幕&#xff0c;年度最佳游戏——《宇宙机器人》&#xff0c;作为一名在软件开发领域深耕多年的从业者&#xff0c;我深知电脑游戏在运行过程中可能会遇到的各种挑战&…...

应用于项目的 C++单例基类的设计、实现与应用

文章目录 应用于项目的 C单例基类的设计、实现与应用一、引言二、单例基类的设计2.1 线程安全的单例基类2.2 局部静态变量的单例基类 三、单例基类的实现3.1 配置管理单例类 四、单例基类的应用4.1 多线程环境下的配置管理 五、深入探讨5.1 单例的线程安全问题5.2 单例的延迟初…...

Mongodb 启用认证

MongoDB 启用认证的完整指南 启用 MongoDB 的认证功能需要按照以下步骤进行设置&#xff1a; 检查 MongoDB 配置文件 在 MongoDB 配置文件中&#xff08;通常为 mongod.conf&#xff09;&#xff0c;需要启用认证功能。 修改配置文件 打开 mongod.conf 文件&#xff0c;找…...

QT:vlc出错处理及重新播放

这个问题一直想解决&#xff0c;昨天认真研究了一下。 要点 视频用的Widget不能重复使用&#xff0c;每次出错后&#xff0c;都要新建。 回调函数的处理。 代码1 关键在于libvlc_event_attach void VideoWidget::play() {libvlc_media_t* media;if (strstr(video_path, &…...

密钥管理系统在数据安全解决方案中的重要性

密钥管理系统在数据安全解决方案中占据着举足轻重的地位&#xff0c;其重要性体现在以下几个方面&#xff1a; 一、保障数据机密性 密钥管理系统通过生成、存储和管理加密密钥&#xff0c;确保了数据的机密性。这些密钥用于加密和解密数据&#xff0c;只有授权用户才能访问和…...

Docker的容器编排

目录 1. 什么是容器编排&#xff08;Docker Compose&#xff09;2. 容器编排的功能3. 容器编排文件&#xff08;docker-compose.yml&#xff09;的介绍3.1 文件语法版本3.2 文件基本结构及常见指令 4. Docker Compose命令详解4.1 Docker Compose命令清单4.2 命令格式和常见选项…...

Java Web项目部署教程简单实用

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c; 忍不住分享一下给大家。点击跳转到网站 学习总结 1、掌握 JAVA入门到进阶知识(持续写作中……&#xff09; 2、学会Oracle数据库入门到入土用法(创作中……&#xff09; 3、手把…...

推送本地仓库到远程git仓库

目录 推送本地仓库到远程git仓库1.1修改本地仓库用户名1.2 push 命令1.3远程分支查看 推送本地仓库到远程git仓库 删除之前的仓库中的所有内容&#xff0c;从新建库&#xff0c;同时创建一个 A.txt 文件 清空原有的远程仓库内容&#xff0c;重新创建一个新的仓库&#xff0c;…...

线性池学习

一、什么是进程&#xff1f;什么是线程&#xff1f; 1. 进程的定义 从操作系统的角度解释&#xff1a; 进程是操作系统分配资源和调度执行的基本单位。每个进程都是操作系统中一个独立的实体&#xff0c;拥有自己的内存空间、文件描述符、代码、数据等资源。进程是程序在执行…...

微积分复习笔记 Calculus Volume 2 - 4.3 Separable Equations

4.3 Separable Equations - Calculus Volume 2 | OpenStax...

前端项目部署方法

ngnix服务器部署 下载nignx&#xff0c;我下的是windows版本的 下载链接&#xff1a;[https://nginx.org/en/download.html](https://nginx.org/en/download.html) 解压文件 如果原本的80端口号被占用了&#xff0c;可以改为其他的端口号 可以点击nginx.exe文件启动nginx,它可能…...

Docker创建一个mongodb实例,并用springboot连接 mongodb进行读写文件

一、通过Docker 进行运行一个 mongodb实例 1、拉取镜像 docker pull mongo:5.0.5 2、创建 mongodb容器实例 docker run -d --name mongodb2 \-e MONGO_INITDB_ROOT_USERNAMEsalaryMongo \-e MONGO_INITDB_ROOT_PASSWORD123456 \-p 27017:27017 \mongo:5.0.5 3、进入容器&am…...

Android app反编译 攻与防

大概是2020年的时候&#xff0c;有一次&#xff0c;我们的竞争同行有另外一家公司要用我们的安卓软件app,拉了个群&#xff0c;告知他用一个软件多少钱&#xff0c;然后在群里发了一个我打包的apk包。结果就没有下文了。又过了一个月。我同事在那个要买我们apk的人的朋友圈&…...

ElasticSearch 简介

一、什么是 ElastcSearch&#xff1f; ElasticSearch 是基于 Lucene 的 Restful 的分布式实时全文搜索引擎。 1.1 ElasticSearh 的基本术语概念 index 索引 索引类似与 mysql 中的数据库&#xff0c;ES 中的索引是存储数据的地方&#xff0c;包含了一堆有相似结构的文档数据…...

Kerberos实验

kdc&#xff1a;192.168.72.163 客户端&#xff08;机器账户win10&#xff09;&#xff1a;192.168.72.159 用户&#xff1a;administrator 抓包&#xff1a;开机登录win10&#xff0c;使用administrator域用户凭据登录。 生成 Kerberos 解密文件 抓取 krbtgt 用户和 win1…...

Android之RecyclerView显示数据列表和网格

一、RecyclerView的优势 RecyclerView 的最大优势在于&#xff0c;它对大型列表来说非常高效&#xff1a; 默认情况下&#xff0c;RecyclerView 仅会处理或绘制当前显示在屏幕上的项。例如&#xff0c;如果您的列表包含一千个元素&#xff0c;但只有 10 个元素可见&#xff0…...

docker mysql挂载

在提供的 docker run 命令中&#xff0c;已经挂载了三个卷到 MySQL 容器中&#xff1a;日志目录、数据目录和配置目录。然而&#xff0c;还没有挂载一个包含 cube_admin.sql 文件的目录。要将 SQL 文件放入容器中并在 MySQL 中执行它&#xff0c;可以按照以下步骤操作&#xff…...

顺序表-递增有序表合并

两个递增有序表合并操作 题目&#xff1a; 将两个递增有序的顺序表 A 和 B 合并成一个新的递增有序顺序表 C。 思路&#xff1a; 使用三个索引 i, j, k 分别遍历顺序表 A, B 和合并后的顺序表 C。比较 A 和 B 当前索引指向的元素&#xff0c;将较小的元素放入 C 中&#xf…...

【Qt】qt安装

在工作一年之后&#xff0c;还是想做一个Qt的教程&#xff0c;遥想研一刚刚接触Qt&#xff0c;从0到1学习&#xff0c;没有什么参考书籍&#xff0c;网上的资料也不多&#xff0c;幸好Qt官方文档写得好&#xff0c;加上自己肯研究&#xff0c;才堪堪入门。 现在我想自己写一个…...

Spring Boot 实现流式响应(兼容 2.7.x)

在实际开发中&#xff0c;我们可能会遇到一些流式数据处理的场景&#xff0c;比如接收来自上游接口的 Server-Sent Events&#xff08;SSE&#xff09; 或 流式 JSON 内容&#xff0c;并将其原样中转给前端页面或客户端。这种情况下&#xff0c;传统的 RestTemplate 缓存机制会…...

根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:

根据万维钢精英日课6的内容&#xff0c;使用AI&#xff08;2025&#xff09;可以参考以下方法&#xff1a; 四个洞见 模型已经比人聪明&#xff1a;以ChatGPT o3为代表的AI非常强大&#xff0c;能运用高级理论解释道理、引用最新学术论文&#xff0c;生成对顶尖科学家都有用的…...

Python 包管理器 uv 介绍

Python 包管理器 uv 全面介绍 uv 是由 Astral&#xff08;热门工具 Ruff 的开发者&#xff09;推出的下一代高性能 Python 包管理器和构建工具&#xff0c;用 Rust 编写。它旨在解决传统工具&#xff08;如 pip、virtualenv、pip-tools&#xff09;的性能瓶颈&#xff0c;同时…...

Caliper 配置文件解析:fisco-bcos.json

config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...

手机平板能效生态设计指令EU 2023/1670标准解读

手机平板能效生态设计指令EU 2023/1670标准解读 以下是针对欧盟《手机和平板电脑生态设计法规》(EU) 2023/1670 的核心解读&#xff0c;综合法规核心要求、最新修正及企业合规要点&#xff1a; 一、法规背景与目标 生效与强制时间 发布于2023年8月31日&#xff08;OJ公报&…...

OD 算法题 B卷【正整数到Excel编号之间的转换】

文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的&#xff1a;a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...

【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制

目录 节点的功能承载层&#xff08;GATT/Adv&#xff09;局限性&#xff1a; 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能&#xff0c;如 Configuration …...

热烈祝贺埃文科技正式加入可信数据空间发展联盟

2025年4月29日&#xff0c;在福州举办的第八届数字中国建设峰会“可信数据空间分论坛”上&#xff0c;可信数据空间发展联盟正式宣告成立。国家数据局党组书记、局长刘烈宏出席并致辞&#xff0c;强调该联盟是推进全国一体化数据市场建设的关键抓手。 郑州埃文科技有限公司&am…...

WEB3全栈开发——面试专业技能点P7前端与链上集成

一、Next.js技术栈 ✅ 概念介绍 Next.js 是一个基于 React 的 服务端渲染&#xff08;SSR&#xff09;与静态网站生成&#xff08;SSG&#xff09; 框架&#xff0c;由 Vercel 开发。它简化了构建生产级 React 应用的过程&#xff0c;并内置了很多特性&#xff1a; ✅ 文件系…...

李沐--动手学深度学习--GRU

1.GRU从零开始实现 #9.1.2GRU从零开始实现 import torch from torch import nn from d2l import torch as d2l#首先读取 8.5节中使用的时间机器数据集 batch_size,num_steps 32,35 train_iter,vocab d2l.load_data_time_machine(batch_size,num_steps) #初始化模型参数 def …...