当前位置: 首页 > 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;才堪堪入门。 现在我想自己写一个…...

挑战杯推荐项目

“人工智能”创意赛 - 智能艺术创作助手&#xff1a;借助大模型技术&#xff0c;开发能根据用户输入的主题、风格等要求&#xff0c;生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用&#xff0c;帮助艺术家和创意爱好者激发创意、提高创作效率。 ​ - 个性化梦境…...

PHP和Node.js哪个更爽?

先说结论&#xff0c;rust完胜。 php&#xff1a;laravel&#xff0c;swoole&#xff0c;webman&#xff0c;最开始在苏宁的时候写了几年php&#xff0c;当时觉得php真的是世界上最好的语言&#xff0c;因为当初活在舒适圈里&#xff0c;不愿意跳出来&#xff0c;就好比当初活在…...

Python:操作 Excel 折叠

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

STM32+rt-thread判断是否联网

一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

线程与协程

1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指&#xff1a;像函数调用/返回一样轻量地完成任务切换。 举例说明&#xff1a; 当你在程序中写一个函数调用&#xff1a; funcA() 然后 funcA 执行完后返回&…...

第25节 Node.js 断言测试

Node.js的assert模块主要用于编写程序的单元测试时使用&#xff0c;通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试&#xff0c;通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

Module Federation 和 Native Federation 的比较

前言 Module Federation 是 Webpack 5 引入的微前端架构方案&#xff0c;允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

QT: `long long` 类型转换为 `QString` 2025.6.5

在 Qt 中&#xff0c;将 long long 类型转换为 QString 可以通过以下两种常用方法实现&#xff1a; 方法 1&#xff1a;使用 QString::number() 直接调用 QString 的静态方法 number()&#xff0c;将数值转换为字符串&#xff1a; long long value 1234567890123456789LL; …...

Fabric V2.5 通用溯源系统——增加图片上传与下载功能

fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...

JVM虚拟机:内存结构、垃圾回收、性能优化

1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...