今天突然想着把旧RedisClient工具类更新一下,
因为旧写法获取环境配置用的是读取properties,不是读取环境的active,出过坑,出于装逼的想法就干脆把整个都重写吧,
使用更简洁的StringRedisTemplate去做,反正底层也是用的jedis,代码看起来也更简洁一些。
然后就开始写了,接好之后开始验证,工具类部分如下
@Component
public class RedisClient {
private static StringRedisTemplate stringRedisTemplate;
@Autowired
public void setRedisTemplate(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
public static void setString(final String key, final String value,
final int expire) {
stringRedisTemplate.opsForValue().set(key,value,expire);
}
public static String getString(final String key) {
return stringRedisTemplate.opsForValue().get(key);
}
}
由于RedisClient使用的地方很多,所以将stringRedisTemple注入static,这样子只要改工具类,引用的地方都不用改
===============================================================================================
接着在测试的时候刚好有一行代码是获取key,然后parseInt,然而代码缺报错
String string = RedisClient.getString("key");
int i = Integer.parseInt(string);
遂使用强大的debug调戏了一下,发现string就是"1",没毛病呀,于是开始怀疑自己对parseInt是不是理解的不到位,换了个Integer.vakyeOf 但是还是报错,debug如图
================================================================================================
先透露一下答案,答案是用错方法了,应该使用第一个set,而不是第二个
既然是这种用眼睛看不出来的问题,那就深度调试一下吧,将断点打在
opsForValue().set()的方法上,使用idea ctrl+alt+B可以看到执行方法在DefaultValueOperations#set
value被定位到this.rawValue()方法,代码如下
byte[] rawValue(Object value) {
return this.valueSerializer() == null && value instanceof byte[] ? (byte[])((byte[])value) : this.valueSerializer().serialize(value);
}
继续往下跟,代码从RedisSerializer#serialize()到了StringRedisSerializer#serialize(),代码如下,也就是常规的getBytes没什么特别
public byte[] serialize(String string) {
return string == null ? null : string.getBytes(this.charset);
}
断点跳出来,发现rawValue={49} 也就是1 没问题
接着断点到
RedisConnection#setRange() 实现类是DefaultStringRedisConnection#setRange()
没有错,来到这里,细心的观众可能已经发现了问题所在,但是想我这种最近眼睛有点疼的还是没发现问题!
由于使用的是集群所以接着代码来到了
JedisClusterConnection#setRange()->BinaryJedisCluster#setRange()->Jedis.setrange()
由此我们也可以知道,stringRedisTemplate底层使用的是jedis
接着代码来到了
BinaryJedis#setrange->BinaryClient#sendCommand
在sendCommand之前,代码基本上都是一路透传而已,没什么特殊的地方,到了BinaryClient开始特殊一点
public void setrange(byte[] key, long offset, byte[] value) {
this.sendCommand(Command.SETRANGE, new byte[][]{key, Protocol.toByteArray(offset), value});
}
protected Connection sendCommand(Command cmd, byte[]... args) {
try {
this.connect();
Protocol.sendCommand(this.outputStream, cmd, args);
++this.pipelinedCommands;
return this;
} catch (JedisConnectionException var6) {
JedisConnectionException ex = var6;
try {
String errorMessage = Protocol.readErrorLineIfPossible(this.inputStream);
if (errorMessage != null && errorMessage.length() > 0) {
ex = new JedisConnectionException(errorMessage, ex.getCause());
}
} catch (Exception var5) {
;
}
this.broken = true;
throw ex;
}
}
开始获取JedisConnection,也就是到这里,"1"的byte数组还是对的
继续往下走,到了
Jedis.Connection#getIntegerReply()
-------------------------------------
public Long getIntegerReply() {
this.flush();
--this.pipelinedCommands;
return (Long)this.readProtocolWithCheckingBroken();
}
-----------------------------------------------------------
protected void flush() {
try {
this.outputStream.flush();
} catch (IOException var2) {
this.broken = true;
throw new JedisConnectionException(var2);
}
}
-----------------------------------------------------------------------------------------
接着代码到了RedisOutPutStream#flush()
-----------------------------------------------------------------------------------------
public void flush() throws IOException {
this.flushBuffer();
this.out.flush();
}
-----------------------------------------------------------------------------------------
public void flush() throws IOException {
this.flushBuffer();
this.out.flush();
}
------------------------------------------------------------------------------------------
private void flushBuffer() throws IOException {
if (this.count > 0) {
this.out.write(this.buf, 0, this.count);
this.count = 0;
}
}
对socket这一套还记得的,可能知道,从建立连接到flush出去,代码到这里已经结束了,也就是问题出在buf这里,接下来由于代码太多,我就不多说了,其实就是buf前面被莫名奇妙的补了600个0
protected final byte[] buf;
然后这个时候我定睛一看,原来答案一早就告诉我了,一直用的是setrange,所以回到最初的
ValueOperations 看了一下set方法,分别有
void set(K var1, V var2); void set(K var1, V var2, long var3, TimeUnit var5); void set(K var1, V var2, long var3);
这里本身代码里面没有解释,差了一下官方文档,发现
我所使用的void set(K var1, V var2, long var3);意思是
将value从指定的位置开始覆盖原有的值。如果指定的开始位置大于字符串长度,先补空格在追加。
而一开始为什么我要用它呢?因为懒,懒的写个TimeUnit,以为默认是ms,谁知道spring会这么坑~
在这里我想说什么呢?不要学我的懒,更重要的是别学我的瞎!!!