RedissonClient 和 RedisTemplate 做Map的累加处理的一些特性
RedissonClient 和 RedisTemplate 做Map的累加处理的一些特性
文章目录
RedissonClient 和 RedisTemplate 做Map的累加处理的一些特性
配置
Spring Boot
版本: 2.6.0
Spring -data-redis
版本: 2.6.0
Redisson
版本: 3.16.8
RedissonClient 配置
对于 RedissonClient
的配置采取 实现RedissonAutoConfigurationCustomizer
的方式来实现,具体原因可以参考RedissonAutoConfiguration
类的 RedissonClient
自动配置代码
@Component
public class MyRedissonAutoConfigurationCustomizer implements RedissonAutoConfigurationCustomizer {
/**
* Customize the RedissonClient configuration.
*
* @param configuration the {@link Config} to customize
*/
@Override public void customize(Config configuration) {
System.out.println("设置默认的Redisson Codec 为 JsonJacksonCodec");
JsonJacksonCodec jacksonCodec = new JsonJacksonCodec();
//支持Java 8 新的日期类型序列化和反序列化
jacksonCodec.getObjectMapper()
.registerModule(new JavaTimeModule())
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
configuration.setCodec(jacksonCodec);
}
}
RedisTemplate 配置
@Bean(name = "jackson2JsonRedisSerializer")
public RedisSerializer<Object> jackson2JsonRedisSerializer() {
//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
ObjectMapper redisOm = new ObjectMapper();
redisOm.registerModule(new JavaTimeModule())
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
return new GenericJackson2JsonRedisSerializer(redisOm);
}
@Bean
public StringRedisSerializer getStringRedisSerializer() {
return new StringRedisSerializer();
}
@Bean
public RedisTemplate<Object, Object> configRedisTemplate(RedisTemplate redisTemplate){
redisTemplate.setKeySerializer(getStringRedisSerializer());
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer());
redisTemplate.setHashKeySerializer(getStringRedisSerializer());
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer());
System.out.println("自定义RedisTemplate加载成功");
return redisTemplate;
}
测试案例代码类
/**
* Redis Map 累加测试
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class RedisMapIncrementTests {
@Resource
private RedissonClient redissonClient;
@Resource
private RedisTemplate redisTemplate;
@After
public void afterAll(){
redissonClient.shutdown();
System.out.println("关闭连接!");
}
RedissonClient 案例1
测试代码
@Test
public void testIncrementWithRedisson1_1(){
String key = "testIncrementWithRedisson1_1";
String hashKey = "A1";
IntStream.range(1,101).forEach(r->{
System.out.print(incrementWithRedisson1(key,hashKey));
});
RMap<String,Long> accumulatorMap = redissonClient.getMap(key);
System.out.println(String.format("累加的结果%d",accumulatorMap.get(hashKey)));
}
private long incrementWithRedisson1(String key,String hashKey){
RMap<String,Long> accumulatorMap = redissonClient.getMap(key);
return accumulatorMap.addAndGet(hashKey,1L);
}
测试结果
Redis 的值:
控制台输出:
... 以上略
99
100
累加的结果100
RedissonClient 案例2
测试代码
@Test
public void testIncrementWithRedisson1_2(){
String key = "testIncrementWithRedisson1_2";
String hashKey = "A1";
IntStream.range(1,101).forEach(r->{
System.out.println(incrementWithRedisson1(key,hashKey));
});
RMap<String,Long> accumulatorMap = redissonClient.getMap(key);
//下面这行代码会抛出异常 java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long
Long value = accumulatorMap.get(hashKey);
System.out.println(String.format("累加的结果%d",value));
}
private long incrementWithRedisson1(String key,String hashKey){
RMap<String,Long> accumulatorMap = redissonClient.getMap(key);
return accumulatorMap.addAndGet(hashKey,1L);
}
测试结果
案例2的代码和案例1的代码唯一的区别就是取出累加后的值的方式不同,导致抛出java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long
异常,但累加结果是正确的。
原因是JsonJacksonCodec
对于 整数数字字符串默认转换为Integer
,而不是按照RMap<String,Long>
的泛型来转换,在取出值是强制转换Integer
为 Long
时,抛出异常。
相关代码:
com.fasterxml.jackson.core.json.UTF8StreamJsonParser#nextToken
...
/* And should we now have a name? Always true for Object contexts
* since the intermediate 'expect-value' state is never retained.
*/
if (!_parsingContext.inObject()) {
_updateLocation();
return _nextTokenNotInObject(i);
}
...
返回的Token
为com.fasterxml.jackson.core.JsonToken#VALUE_NUMBER_INT
Redis 的值:
控制台输出:
... 以上略
99
100
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long
RedissonClient 案例3
解决掉案例2
的问题
测试代码
@Test
public void testIncrementWithRedisson2_1(){
String key = "testIncrementWithRedisson2_1";
String hashKey = "A1";
IntStream.range(1,101).forEach(r->{
System.out.println(incrementWithRedisson2(key,hashKey));
});
//强制指定本Map的Codec 为LongCodec
RMap<String,Long> accumulatorMap = redissonClient.getMap(key,LongCodec.INSTANCE);
//下面这行代码不会抛出异常
Long value = accumulatorMap.get(hashKey);
System.out.println(String.format("累加的结果%d",value));
}
private long incrementWithRedisson2(String key,String hashKey){
//强制指定本Map的Codec 为LongCodec
RMap<String,Long> accumulatorMap = redissonClient.getMap(key, LongCodec.INSTANCE);
return accumulatorMap.addAndGet(hashKey,1L);
}
测试结果
Redis 的值:
注意,这里的Map的Key的值没有了双引号,这个也是由于LongCodec的处理导致的,因此如果切换代码时,记得处理Redis的历史数据,否则就会出现一个MapKey 有两个累加器。
控制台输出:
... 以上略
99
100
累加的结果100
RedissonClient 案例4
补一个多线程的累加案例
测试代码
/**
* 定义100个线程的线程池
*/
private static final ExecutorService
testExecutorService = new ThreadPoolExecutor(100,100,0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(1024),new ThreadFactoryBuilder()
.setNameFormat("Test-pool-%d").get());
@Test
public void testIncrementWithRedisson2_2(){
String key = "testIncrementWithRedisson2_2";
String hashKey = "A1";
List<CompletableFuture> cfList = IntStream.range(1,101).mapToObj(r->{
return CompletableFuture.runAsync(()->
incrementWithRedisson2(key,hashKey)
,testExecutorService)
.exceptionally(ex->{
ex.printStackTrace();
return null;
});
}).collect(Collectors.toList());
// 等待所有线程结束
CompletableFuture.allOf(cfList.toArray(new CompletableFuture[cfList.size()])).whenComplete((k,e)->{
//所有线程都结束后,取出累加的值
//强制指定本Map的Codec 为LongCodec
RMap<String,Long> accumulatorMap = redissonClient.getMap(key,LongCodec.INSTANCE);
Long value = accumulatorMap.get(hashKey);
System.out.println(String.format("累加的结果%d",value));
}).join();
}
private long incrementWithRedisson2(String key,String hashKey){
//强制指定本Map的Codec 为LongCodec
RMap<String,Long> accumulatorMap = redissonClient.getMap(key, LongCodec.INSTANCE);
return accumulatorMap.addAndGet(hashKey,1L);
}
测试结果
Redis 的值:
控制台输出:
累加的结果100
RedisTemplate 案例1
测试代码
@Test
public void testIncrementWithRedisTemplate1_1(){
String key = "testIncrementWithRedisTemplate1_1";
String hashKey = "A1";
IntStream.range(1,101).forEach(r->{
System.out.println(incrementWithRedisTemplate(key,hashKey));
});
System.out.println(String.format("累加的结果%d",redisTemplate.opsForHash().get(key,hashKey)));
}
private long incrementWithRedisTemplate(String key,String hashKey){
return redisTemplate.opsForHash().increment(key,hashKey,1L);
}
测试结果
Redis 的值:
控制台输出:
... 以上略
99
100
累加的结果100
RedisTemplate 案例2
测试代码
@Test
public void testIncrementWithRedisTemplate1_2(){
String key = "testIncrementWithRedisTemplate1_2";
String hashKey = "A1";
IntStream.range(1,101).forEach(r->{
System.out.println(incrementWithRedisTemplate(key,hashKey));
});
//下面这行代码会抛出异常 java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long
Long value = (Long)redisTemplate.opsForHash().get(key,hashKey);
System.out.println(String.format("累加的结果%d",value));
}
private long incrementWithRedisTemplate(String key,String hashKey){
return redisTemplate.opsForHash().increment(key,hashKey,1L);
}
测试结果
Redis 的值:
控制台输出:
... 以上略
99
100
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long
RedisTemplate 案例3
测试代码
@Test
public void testIncrementWithRedisTemplate1_3(){
String key = "testIncrementWithRedisTemplate1_3";
String hashKey = "A1";
IntStream.range(1,101).forEach(r->{
System.out.println(incrementWithRedisTemplate(key,hashKey));
});
//下面这行代码不会抛出异常
Long value = Long.valueOf(redisTemplate.opsForHash().get(key,hashKey).toString());
System.out.println(String.format("累加的结果%d",value));
}
private long incrementWithRedisTemplate(String key,String hashKey){
return redisTemplate.opsForHash().increment(key,hashKey,1L);
}
测试结果
Redis 的值:
控制台输出:
... 以上略
99
100
累加的结果100
RedisTemplate 案例4
补一个多线程的累加案例
测试代码
/**
* 定义100个线程的线程池
*/
private static final ExecutorService
testExecutorService = new ThreadPoolExecutor(100,100,0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(1024),new ThreadFactoryBuilder()
.setNameFormat("Test-pool-%d").get());
@Test
public void testIncrementWithRedisTemplate1_4(){
String key = "testIncrementWithRedisTemplate1_4";
String hashKey = "A1";
List<CompletableFuture> cfList = IntStream.range(1,101).mapToObj(r->{
return CompletableFuture.runAsync(()->
incrementWithRedisTemplate(key,hashKey)
,testExecutorService)
.exceptionally(ex->{
ex.printStackTrace();
return null;
});
}).collect(Collectors.toList());
// 等待所有线程结束
CompletableFuture.allOf(cfList.toArray(new CompletableFuture[cfList.size()])).whenComplete((k,e)->{
//所有线程都结束后,取出累加的值
Long value = Long.valueOf(redisTemplate.opsForHash().get(key,hashKey).toString());
System.out.println(String.format("累加的结果%d",value));
}).join();
}
private long incrementWithRedisTemplate(String key,String hashKey){
return redisTemplate.opsForHash().increment(key,hashKey,1L);
}
测试结果
Redis 的值:
控制台输出:
累加的结果100
更多推荐
所有评论(0)