1.å°åºå¦ä½å¨springä¸ä½¿ç¨redis
2.分析SpringBoot 的码查Redis源码
3.Spring Boot Redis Cluster 实战干货
4.Jedis那么低性能,还在用?赶紧换上 lettuce 吧!码查
5.Netty åºç° Connection reset by peer å¼å¸¸çå 个åå
å°åºå¦ä½å¨springä¸ä½¿ç¨redis
1. Redis使ç¨åºæ¯
Redisæ¯ä¸ä¸ªå¼æºç使ç¨ANSI Cè¯è¨ç¼åãæ¯æç½ç»ãå¯åºäºå å亦å¯æä¹ åçæ¥å¿åãKey-Valueæ°æ®åºï¼å¹¶æä¾å¤ç§è¯è¨çAPIã
æ们é½ç¥éï¼å¨æ¥å¸¸çåºç¨ä¸ï¼æ°æ®åºç¶é¢æ¯æ容æåºç°çãæ°æ®é太大åé¢ç¹çæ¥è¯¢ï¼ç±äºç£çIOæ§è½çå±éæ§ï¼å¯¼è´é¡¹ç®çæ§è½è¶æ¥è¶ä½ã
è¿æ¶åï¼åºäºå åçç¼åæ¡æ¶ï¼å°±è½è§£å³æ们å¾å¤é®é¢ãä¾å¦Memcacheï¼Redisçãå°ä¸äºé¢ç¹ä½¿ç¨çæ°æ®æ¾å ¥ç¼å读åï¼å¤§å¤§éä½äºæ°æ®åºçè´æ ãæåäºç³»ç»çæ§è½ã
å ¶å®ï¼å¯¹äºhibernateçäºçº§ç¼åï¼æ¯åæ ·çéçãå©ç¨å åé«éç读åé度ï¼æ¥è§£å³ç¡¬ççç¶é¢ã
2. é 置使ç¨redis
é¦å ï¼æ们éè¦å¼å ¥åºæ¬çjarå ãmavenä¸çåºæ¬å¼ç¨å¦ä¸ï¼
ãããã<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.4.2.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.6.2</version>
</dependency>
ç¶åï¼å¨applicationContextä¸é ç½®å¦ä¸:
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${ redis.maxIdle}" />
<property name="maxTotal" value="${ redis.maxActive}" />
<property name="maxWaitMillis" value="${ redis.maxWait}" />
<property name="testOnBorrow" value="${ redis.testOnBorrow}" />
</bean>
<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:host-name="${ redis.host}" p:port="${ redis.port}" p:password="${ redis.pass}"
p:pool-config-ref="poolConfig" />
<bean id="stringSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
<!-- å¼å¯äºå¡ï¼å¯ä»¥éè¿transcational注解æ§å¶ -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="connectionFactory" />
<property name="keySerializer" ref="stringSerializer" />
<property name="enableTransactionSupport" value="true" />
</bean>
对äºhibernateçé ç½®å¯ç¥ï¼ç¬¬ä¸ä¸ªpoolconfigæ¯å¯¹è¿æ¥æ± çé ç½®ãå æ¬æ大è¿æ¥æ°ï¼éåæ°ï¼åæ´»æ¶é´ï¼æ大çå¾ æ¶é´ççï¼è¿æä¸äºé¢å¤çé ç½®ï¼è¯·ç´æ¥ç¹å»JedisPoolConfigç±»æºç ï¼è¿è¡æ¥çã
è¿äºé ç½®çææå¦æä¸æç½çè¯ï¼ä¸å®è¦å»æ线ç¨æ± 好好å¦ä¹ ä¸ã
第ä¸ä¸ªé ç½®æ¯è¿æ¥å·¥åï¼é¡¾åæä¹ï¼æåºæ¬ç使ç¨ä¸å®æ¯å¯¹è¿æ¥çæå¼åå ³éãæ们éè¦ä¸ºå ¶é ç½®redisæå¡å¨çè´¦æ·å¯ç ï¼ç«¯å£å·ãï¼è¿éè¿å¯ä»¥é ç½®æ°æ®åºçindexï¼ä½æ¯æ使ç¨æ¶åä¸ç´ä½¿ç¨redisçé»è®¤æ°æ®åºï¼ä¹å°±æ¯ç¬¬0个ï¼
æåä¸ä¸ªé ç½®ç¹å«éè¦ãè¿ä¸ªç±»ä¼¼äºspringæä¾çHibernateDaoSupportã
æ¥ä¸æ¥ï¼å ¨é¨è®²è§£é½å°å´ç»è¿ä¸ªç±»å±å¼ã
3. RedisTemplateç使ç¨
è¿ä¸ªç±»ä½ä¸ºä¸ä¸ªæ¨¡çç±»ï¼æä¾äºå¾å¤å¿«é使ç¨redisçapiï¼èä¸éè¦èªå·±æ¥ç»´æ¤è¿æ¥ï¼äºå¡ã
æåçæ¶åï¼æå建çBaseRedisDaoæ¯ç»§æ¿èªè¿ä¸ªç±»çã继æ¿ç好å¤æ¯æçæ¯ä¸ªDaoä¸ï¼é½å¯ä»¥èªç±çæ§å¶åºååå¨ï¼èªç±çæ§å¶èªå·±æ¯å¦éè¦äºå¡ï¼è¿ä¸ªå ä¸éè¦äºè§£ï¼è·çæç®åçè¿ç§é ç½®æ¹æ³æ¥å³å¯ã
templateæä¾äºä¸ç³»åçoperation,码查æ¯å¦valueOperation,HashOperation,ListOperation,SetOperationçï¼ç¨æ¥æä½ä¸åæ°æ®ç±»åçRedisã
并ä¸ï¼RedisTemplateè¿æä¾äºå¯¹åºç*OperationsEditorï¼ç¨æ¥éè¿RedisTemplateç´æ¥æ³¨å ¥å¯¹åºçOperationãæ们ææ¶ä¸è®²è¿ä¸ªã
对äºä¸é¢çtest1æ¹æ³ï¼æ们ææ¶ä¸ç¨èèï¼å äºè§£éè¿RedisTemplateæ¥ä½¿ç¨connectionæä½Redisã
Test代ç å¦ä¸ï¼
package cn.test.spjedis;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.cn.redis2.dao.IncrDao;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class TestRedis {
@Resource(name = "redisTemplate")
private RedisTemplate<String, String> template; // inject the template as ListOperations
//è³äºè¿ä¸ªä¸ºä»ä¹å¯ä»¥æ³¨å ¥ãéè¦åèAbstractBeanFactory doGetBean
//super.setValue(((RedisOperations) value).opsForValue());å°±è¿ä¸è¡ä»£ç ä¾é ä¸ä¸ªeditor
@Resource(name = "redisTemplate")
private ValueOperations<String, Object> vOps;
public void testSet(){
template.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
byte [] key = "tempkey".getBytes();
byte[] value = "tempvalue".getBytes();
connection.set(key, value);
return true;
}
});
}
public void testSet1(){
vOps.set("tempkey", "tempvalue");
}
@Autowired
private IncrDao incr;
@Test
public void addLink() {
System.out.println(incr.incr());
System.out.println(incr.get());
}
}
è¿ä¸ªæ¯å¯¹Stringç±»åæå ¥ç两个æµè¯ãtestæ¹æ³ä¸ï¼ä½¿ç¨äºæ¨¡çç±»æ交åè°(RedisCallBack)çæ¹æ³æ¥ä½¿ç¨jedis connectionæä½æ°æ®ãè¿ä¸é¨åï¼æ没æä¼¼æ¾ç¸è¯å¢ï¼
HibernateTemplateçHibernateCallbackï¼ä»¥åHibernate Sessionç±»ä¸çdoWork以ådoReturningWorkæ¹æ³ï¼é½æ¯ä½¿ç¨äºè¿æ ·çæºå¶ï¼æ¹ä¾¿å¯¹äºè¿æ¥æè sessionçç»ä¸ç®¡çã
public int excuteHqlUpdate(final String hql,final Object ...params){
return getHibernateTemplate().executeWithNativeSession(new HibernateCallback<Integer>() {
@Override
@SuppressWarnings("unchecked")
public Integer doInHibernate(Session session) throws HibernateException {
Query queryObject = session.createQuery(hql);
if (params != null) {
for (int i = 0; i < params.length; i++) {
queryObject.setParameter(i, params[i]);
}
}
return queryObject.executeUpdate();
}
});
}
分析SpringBoot 的Redis源码
在Spring Boot 2.X版本中,官方简化了项目配置,码查如无需编写繁琐的码查web.xml和相关XML文件,只需在pom.xml中引入如spring-boot-starter-data-redis的码查knime 源码starter包即可完成大部分工作,这极大地提高了开发效率。码查
深入理解其原理,码查我们研究了spring-boot-autoconfigure和spring-boot-starter-data-redis的码查源码。首先,码查配置项在application.properties中的码查设置会被自动映射到名为RedisProperties的类中,此类由RedisAutoConfiguration类负责扫描和配置。码查该类会检测是码查否存在RedisOperations接口的实现,例如官方支持的码查Jedis或Lettuce,以此来决定使用哪个客户端。码查
在RedisAutoConfiguration中,kernel 源码阅读工具通过@Bean注解,它引入了LettuceConnectionConfiguration和JedisConnectionConfiguration,这两个配置类会创建RedisConnectionFactory实例。在注入RedisTemplate时,实际使用的会是第一个被扫描到的RedisConnectionFactory,这里通常是LettuceConnectionFactory,因为它们在@Import注解的导入顺序中位于前面。
自定义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 的各个环节。
链接: pan.baidu.com/s/wLzA6...
提取码: ztsj
版权申明:本文系 "Java技术栈" 原创,原创实属不易,转载、引用本文内容请注明出处,禁止抄袭、洗稿,请自重,尊重他人劳动成果和知识产权。
Jedis那么低性能,还在用?赶紧换上 lettuce 吧!
在大型企业的技术栈中,经常面临着选择Redis客户端工具的决策:Jedis、Redisson还是Lettuce?本文将深入探讨这三者的优缺点,以助你做出明智的选择。
首先,我们来看一下官方推荐的三款Java客户端:Jedis,老牌的Redis客户端,它提供了全面的Redis命令支持,其官网链接为<a href="tool.oschina.net/upload...">tool.oschina.net/upload...,其优点在于功能全面,但可能在性能上存在短板。
相比之下,Redisson是一个基于Redis的Java内存网格框架,它简化了Redis的使用,提供了一系列分布式Java对象和服务,如分布式锁和Spring cache等。Redisson官网和Git地址分别为:<a href="redisson.org/">redisson.org/ 和 <a href="github.com/redisson/red...">
github.com/redisson/red...。Redisson强调关注分离,有助于业务逻辑的处理,但不支持某些高级Redis特性。
然后是Lettuce,一个可扩展的线程安全Redis客户端,支持异步操作,并在多线程环境中表现出色,特别是对于避免阻塞和事务操作。Lettuce基于Netty,支持高级Redis功能,官网和Git地址是:<a href="lettuce.io/">lettuce.io/ 和 <a href="github.com/lettuce-io/l...">
github.com/lettuce-io/l...。Lettuce在性能上优于Jedis,特别适合对高级功能需求不高的场景。
在实际选择中,Jedis和Lettuce适合基础Redis操作,而Redisson则适合需要分布式和扩展性功能的应用。如果考虑使用分布式锁或高级数据结构,Redisson是更好的选择,但需注意其对字符串操作的限制。在Spring Boot 2及更高版本中,lettuce成为了默认连接选择,反映了其在性能上的优势。
关于链接断裂的问题,Lettuce不提供心跳机制,但可以利用Netty的空闲检测机制来维持连接。这需要对Netty的开发和源码有一定理解,相关资料可以参考《java高并发核心编程卷1加强版》。
总之,如果你追求性能和基础操作,Lettuce是值得考虑的升级选择。别让技术选择阻碍你的进步,及时更新和学习新的工具,保持技术的前沿性。
Netty åºç° Connection reset by peer å¼å¸¸çå 个åå
æè¿ä½¿ç¨ netty è¿ç¨ä¸åç°äºå 个æ¯è¾ç»èç Connection reset by peer å¼å¸¸ï¼å个ç¬è®°ã
è¿ä¸ªåºæ¯åºç°å¨ç¨ Jedis ping æ£æµçåºæ¯ï¼ç¨å®ç´æ¥ closeï¼æå¡ç«¯ç¨³å®åºç° Connection reset by peerã
tcpdump ä¸ä¸å°±å¾å®¹æå®ä½å°é®é¢æå¨ï¼å®¢æ·ç«¯æ¶å° PONG ååºåç´æ¥åäºä¸ä¸ª RST å ç»æå¡ç«¯ï¼
æ¥ç Jedis çæºç åç° socket æ个æ¯è¾ç¹æ®çé ç½® socket.setSoLinger(true, 0) ã
å çä¸ä¸ man7/socket.7 ç解éï¼
å¦ç½è¯´ä¸æ¯å¾æç½å¥ææããã
æç»å¨ stackoverflow ä¸æ¾å°ä¸ä¸ªæ¯è¾å®¹æç解ç解éï¼
ç®èè¨ä¹ï¼è®¾ç½® SO_LINGER(0) å¯ä»¥ä¸è¿è¡å次æ¥æç´æ¥å ³é TCP è¿æ¥ï¼å¨å议交äºä¸å°±æ¯ç´æ¥å RST å ï¼è¿æ ·ç好å¤æ¯å¯ä»¥é¿å é¿æ¶é´å¤äº TIME_WAIT ç¶æï¼å½ç¶ TIME_WAIT åå¨ä¹æ¯æåå çï¼å¤§é¨åè¯è®ºé½ä¸å»ºè®®è¿æ ·é ç½®ã
è¿ä¸ªåºæ¯æç¹å¿å¾®å¦ï¼é¦å å¾ç解ä¸ä¸ tcp ç两个éåã
è¿ç¯æç« è®²å¾æ¯è¾æ¸ æ¥ï¼ SYN packet handling in the wild
accept éå满é常æ¯ç±äº netty boss 线ç¨å¤çæ ¢ï¼ç¹å«æ¯å¨å®¹å¨åä¹åï¼æå¡åå¯å¨çæ¶åå¾å®¹æåºç° CPU åéã
为äºæ¨¡æè¿ä¸ªç°è±¡ï¼æåäºä¸ªç¤ºä¾ç¨åº shichaoyuan/netty-backlog-test ï¼è®¾ç½® SO_BACKLOG 为 1ï¼å¹¶ä¸å¨ accept 第ä¸ä¸ªè¿æ¥å设置 autoRead 为 falseï¼ä¹å°±æ¯è®© boss 线ç¨ä¸åç»§ç» accept è¿æ¥ã
å¯å¨ç¬¬ä¸ä¸ª Clientï¼å¯ä»¥æ£å¸¸è¿æ¥ï¼åé PINGï¼æ¥æ¶ PONGã
å¯å¨ç¬¬äºä¸ª Clientï¼ä¹å¯ä»¥æ£å¸¸è¿æ¥ï¼ä½æ¯æ²¡ææ¶å° PONGï¼
å¯è§è¿ä¸ªè¿æ¥å建æåäºï¼å·²ç»å¨ Accept Queue éäºï¼ä½æ¯è¿ç¨æ²¡æ acceptï¼æ以没æä¸è¿ç¨ç»å®ã
å¯å¨ç¬¬ä¸ä¸ª Clientï¼ä¹å¯ä»¥æ£å¸¸è¿æ¥ï¼ä¹æ²¡ææ¶å° PONGï¼
ä¸ç¬¬äºä¸ªè¿æ¥ä¸æ ·ã
å¯å¨ç¬¬å个 Clientï¼ä¹å¯ä»¥æ£å¸¸è¿æ¥ï¼ä½æ¯å¨åé PING ååºç° Connection reset by peerï¼
è¿ä¸ªè¿æ¥å¨æå¡ç«¯å¹¶æ²¡æè¿å ¥ accept queueï¼å¤äº SYN_RECV ç¶æï¼å¹¶ä¸å¾å¿«å°±æ¶å¤±äºï¼å 为 accept queue å·²ç»æ»¡äºï¼æ æ³è½¬å ¥ ESTABLISHED ç¶æï¼ã
æå çä¸ä¸ï¼
ä»å®¢æ·ç«¯è§è§æ¥çè¿æ¥ç¡®å®æ¯å»ºæåäºï¼æä¸ä¸ªæ¯è¾ç¹æ®çå°æ¹å¨ä¸æ¬¡æ¡æä¹åï¼æå¡ç«¯åå客æ·ç«¯åéäºä¸ä¸ª [S.]ï¼å®¢æ·ç«¯åå¤äºä¸ä¸ª [.]ï¼è¿ä¸ªäº¤äºçèµ·æ¥ä¸å½±åè¿æ¥ã
æå¡ç«¯åæ¥éæ¯äºè¿æ¥ï¼è客æ·ç«¯è¿è®¤ä¸ºè¿æ¥æ¯ ESTABLISHED çï¼åé PING æ¶æ¯ï¼æå¡ç«¯èªç¶å¾åå¤ä¸ä¸ª RSTã
PSï¼æå¨ Windows ç WSL2 ä¸å®éªè¿ç§åºæ¯æ¯å»ºè¿æ¥è¶ æ¶ï¼å¯è½ä¸åçæä½ç³»ç»æ linux çæ¬å¯¹è¿ä¸ªäº¤äºçè¿ç¨å¤çä¸åï¼å¨æ¤ä¸è¿è¡è¿ä¸æ¥æµè¯äºã
以ä¸ï¼è¿ä¸ªæ äºåè¯æ们å¤æè¿æ¥æ¯å¦å¯ç¨ï¼å»ºæåä¹ååºè¯¥å个å¿è·³å æµè¯ä¸ä¸ã