1.分析SpringBoot 的Redis源码
2.Spring Boot Redis Cluster 实战干货
3.Spring Data Redis切换底层Jedis 和 Lettuce实现
4.连接池:别让连接池帮了倒忙
5.Jedis连接池究竟是何物?
6.jedis å strpin-data-redis åªä¸ä¸ªå¥½
分析SpringBoot 的Redis源码
在Spring Boot 2.X版本中,官方简化了项目配置,如无需编写繁琐的web.xml和相关XML文件,只需在pom.xml中引入如spring-boot-starter-data-redis的starter包即可完成大部分工作,这极大地提高了开发效率。
深入理解其原理,笔记啦源码我们研究了spring-boot-autoconfigure和spring-boot-starter-data-redis的源码。首先,配置项在application.properties中的设置会被自动映射到名为RedisProperties的类中,此类由RedisAutoConfiguration类负责扫描和配置。该类会检测是否存在RedisOperations接口的实现,例如官方支持的Jedis或Lettuce,以此来决定使用哪个客户端。
在RedisAutoConfiguration中,通过@Bean注解,它引入了LettuceConnectionConfiguration和JedisConnectionConfiguration,这两个配置类会创建RedisConnectionFactory实例。在注入RedisTemplate时,实际使用的会是第一个被扫描到的RedisConnectionFactory,这里通常是LettuceConnectionFactory,因为它们在@Import注解的dapper源码下载导入顺序中位于前面。
自定义starter时,可以模仿官方starter的结构,首先引入spring-boot-autoconfigure,然后创建自己的配置类(如MyRedisProperties)和操作模板类(如JedisTemplete)。在MyRedisAutoConfiguration中,你需要编写相关配置并确保在spring.factories文件中注册,以便Spring Boot在启动时扫描到你的自定义配置。
以自定义my-redis-starter为例,项目结构包括引入的依赖,配置类的属性绑定,以及创建连接池和操作方法的实现。测试时,只需在Spring Boot项目中引入自定义starter,配置好相关参数,即可验证自定义starter的正确工作。
Spring Boot Redis Cluster 实战干货
只需添加3个master节点,3个slave节点无需添加。
配置完成这些即可,Spring Boot 会自动完成其他配置。
现在可以像使用单机一样使用集群,野菜部落 源码Redis 会自动按key分片到不同的集群实例。
遇到的问题:尝试向Redis写入数据时,出现无法获取连接异常,经过长时间代码追踪,发现连接的是.0.0.1,而非配置的..1.8,这令人困惑。继续追踪代码发现是向Redis服务器获取的集群实例列表,真是坑!
源码:redis.clients.jedis.Jedis#clusterSlots
就是这里获取返回的集群列表,返回的就是.0.0.1,而非配置的..1.8。
最后修改各个集群节点的配置文件redis.conf,添加:
重启集群节点后,读写恢复正常。
更多 Spring Boot 干货:
Spring Boot 宣布移除 run 命令,真让我猝不及防!
Spring Boot 定时任务开启后,如何自动停止符合条件?
Spring Boot 保护敏感配置的 4 种方法,让你的电子档案 源码系统不再裸奔!!
Spring Boot 集成 Flyway,数据库也能做版本控制,太牛逼了!
个官方 Spring Boot Starters 出炉!别再重复造轮子了……
Spring Boot Redis 实现分布式锁,真香!!
Spring Boot 之配置导入,强大到不行!
年轻人的第一个自定义 Spring Boot Starter!
Spring Boot 面试,一个问题就干趴下了!(下)
Spring Boot 最核心的 个注解,都是干货!
好了,最后栈长再送你一份Spring Boot 学习笔记,包括底层实现原理及代码实战,非常齐全,助你快速打通 Spring Boot 的vc 抽奖 源码各个环节。
链接: pan.baidu.com/s/wLzA6...
提取码: ztsj
版权申明:本文系 "Java技术栈" 原创,原创实属不易,转载、引用本文内容请注明出处,禁止抄袭、洗稿,请自重,尊重他人劳动成果和知识产权。
Spring Data Redis切换底层Jedis 和 Lettuce实现
Spring Data Redis提供了对Redis操作的高级抽象,支持Jedis和Lettuce两种连接方式。通过简单的配置即可连接Redis并切换连接方式。具体步骤如下:
引入Redis依赖使用Spring Boot的spring-boot-starter-data-redis。
自定义配置类设置Key和Value的序列化。
修改Redis连接配置,可自由切换单节点、哨兵模式和集群模式。
注入RedisTemplate后,即可操作Redis。RedisTemplate具有两个泛型。
源码分析部分,从Redis自动配置类RedisAutoConfiguration开始,它引入了两个连接Redis配置类:LettuceConnectionConfiguration和JedisConnectionConfiguration。这两个配置类通过条件注解控制是否生效,如果生效,则会使用相应的依赖生成RedisConnectionFactory的Bean。引入Lettuce依赖时,能通过io.lettuce.core.RedisClient找到类,说明默认使用Lettuce。若无Jedis相关依赖,则当前配置类无效。
切换连接方式至Jedis有两种方式:利用@ConditionalOnClass注解排除Lettuce依赖,或利用@ConditionalOnProperty注解修改配置文件中的spring.redis.client-type为jedis。第一种方式优点在于不加载多余的依赖包,推荐使用。第二种方式则可通过配置文件自由切换连接方式。
本文由OpenWrite平台发布。请按照上述步骤进行Spring Data Redis的使用和连接方式切换。
连接池:别让连接池帮了倒忙
连接池技术在业务项目中扮演着重要角色,尤其是在数据库连接池、Redis连接池和HTTP连接池的使用上。今天,我们将围绕连接池的结构、使用和配置,以及如何避免常见错误,来深入探讨这一话题。
连接池的结构主要包括对外提供连接获取与归还接口的客户端使用,以及内部实现的连接建立、连接心跳保持、连接管理、空闲连接回收、连接可用性检测等功能。在业务项目中,数据库连接池、Redis连接池和HTTP连接池是最常见的三种连接池。
在使用第三方客户端SDK进行网络通信时,首先需要确认SDK是否基于连接池技术实现。TCP作为面向连接的基于字节流的协议,如果SDK没有使用连接池,而是直接建立TCP连接,将需要考虑每次连接的开销,并且在多线程环境下可能产生线程安全问题。因此,识别SDK连接池的实现方式至关重要。
以Jedis作为Redis操作最常见的库为例,分析其源码。Jedis类是连接池与连接分离的API,内部维护一个Socket连接,多线程环境下复用连接可能导致命令执行不完整或线程安全问题。使用连接池如JedisPool,可以实现线程安全的复用,避免这些问题。确保关闭连接池,最好通过shutdownhook在程序退出时释放资源。
连接池的配置不是一成不变的。最大连接数的设置需要根据容量规划,过大或过小都会影响性能。通过实践和监控,可以有效调整参数,满足性能需求同时避免资源浪费。连接池的正确使用方式包括复用连接和确保在程序退出前关闭连接池。
总结来说,理解连接池的实现方式、正确使用姿势和合理配置参数是确保高效、稳定的连接管理的关键。连接池技术能够显著提升性能,减少资源浪费,因此正确应用连接池对于提高系统性能至关重要。
Jedis连接池究竟是何物?
连接池是管理并回收资源的对象集合,以减少系统资源的创建和销毁开销,提升系统吞吐量,适用于创建/销毁资源耗时的场景。以Jedis为例,其底层使用的是GenericObjectPool作为连接池实现。业务从空闲连接队列获取连接,最长等待时间由maxWaitMillis决定。获取后,检查是否有效,关闭后再次放入空闲队列或销毁。连接池的参数如配置文件中详细列出,Spring-Data-Redis将参数进行收敛,用户主要配置参数较少,关键参数包括验证方法的启用和关闭。Jedis实例通过Spring-Data-Redis封装后,实现对连接池的管理,提供了BorrowObject方法获取连接和ReturnObject方法归还连接。其中BorrowObject方法可能从空闲队列获取或创建新连接,超过最大等待时间则抛出异常。ReturnObject方法负责归还连接并进行有效性验证。连接池还包括定期驱逐/保活机制、检查机制和抛弃机制,通过配置参数、源码分析和JMX工具实现连接状态监控和问题排查。了解连接池原理有助于提升资源管理效率,并在实际应用中根据业务需求优化配置。
jedis å strpin-data-redis åªä¸ä¸ªå¥½
ä¹åä¸ç´æ²¡ä»ç»çè¿ShardedJedisç代ç ï¼æè¿éå°äºshardåé群æ©å®¹åçæ°æ®è¿ç§»é®é¢ã
ä»å¤©å¨çShardedJedisæºç çæ¶åï¼åç°ShardedJedis并没æ使ç¨èç¹çIpåportåhashï¼èæ¯ç¨çinstanceç顺åºæè nameï¼å¤ªèµäºã
private void initialize(List<S> shards) {
nodes = new TreeMap<Long, S>();
for (int i = 0; i != shards.size(); ++i) {
final S shardInfo = shards.get(i);
if (shardInfo.getName() == null)
for (int n = 0; n < * shardInfo.getWeight(); n++) {
nodes.put(this.algo.hash("SHARD-" + i + "-NODE-" + n),
shardInfo);
}
else
for (int n = 0; n < * shardInfo.getWeight(); n++) {
nodes.put(
this.algo.hash(shardInfo.getName() + "*"
+ shardInfo.getWeight() + n), shardInfo);
}
resources.put(shardInfo, shardInfo.createResource());
}
}
é ç½®çæ¶åä¹é常ç®å:
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value=""/>
<property name="maxIdle" value=""/>
<property name="minIdle" value="1"/>
<property name="maxWaitMillis" value=""/>
<property name="testOnBorrow" value="true"/>
<property name="testOnReturn" value="true"/>
<property name="testWhileIdle" value="true"/>
</bean>
<bean id="shardedJedisPool" class="redis.clients.jedis.ShardedJedisPool" destroy-method="destroy">
<constructor-arg ref="jedisPoolConfig"/>
<constructor-arg>
<list>
<bean class="redis.clients.jedis.JedisShardInfo">
<constructor-arg value=".0.0.1"/>
<constructor-arg type="int" value=""/>
<constructor-arg value="instance:"/>
</bean>
<bean class="redis.clients.jedis.JedisShardInfo">
<constructor-arg value=".0.0.1"/>
<constructor-arg type="int" value=""/>
<constructor-arg value="instance:"/>
</bean>
<bean class="redis.clients.jedis.JedisShardInfo">
<constructor-arg value=".0.0.1"/>
<constructor-arg type="int" value=""/>
<constructor-arg value="instance:"/>
</bean>
</list>
</constructor-arg>
</bean>
ä¸å¼å§ä½ å¯ä»¥è®¾ç½®è¶³å¤å¤çinstanceï¼æ°æ®æ©å®¹çæ¶åï¼åªéè¦å°å 个instanceçæ°æ®copyå°å«çæºå¨ä¸ã
ç¶åä¿®æ¹é ç½®æ件çipå端å£å³å¯ãå¾æ¹ä¾¿å§ï¼
å¦å¤ï¼Jedisè¿æä¾äºå¯¹jedis sentinel poolçå°è£ ï¼æ以åç主ä»åæ¢çæ¶åï¼web serveré½ä¸éè¦éæ°é ç½®ådeployãé«å¯ç¨æ§çæä½³ä½ç°åã
@Autowired private JedisSentinelPool pool;
public void mymethod() {
Jedis jedis = null;
try {
jedis = pool.getResource();
jedis.hset(....
} catch (JedisException je) {
throw je;
} finally {
if (jedis != null) pool.returnResource(jedis);
}
}
spring beançé ç½®ï¼
<bean id="redisSentinel" class="redis.clients.jedis.JedisSentinelPool">
<constructor-arg index="0" value="mymaster" />
<constructor-arg index="1">
<set>
<value>hostofsentinel:</value>
</set>
</constructor-arg>
<constructor-arg index="2" ref="jedisPoolConfig" />
</bean>