1, Antecedents
There is a core project of the company. The client of redis has always used jedis. Later, the technical director asked to replace the jedis client with a more efficient lettuce client, and use the RedisTemplate class of spring framework to operate redis.
However, the world is unpredictable. It is such a simple demand that makes the teacher overturn the boat...
2, Accident rehearsal
According to the preset results, this development task should be very easy:
- Translate and replace the configuration item of jedis connection pool in the configuration file with lettuce;
- Delete the jedis configuration related codes in the project;
- Replace the place where jedis is used with redisTemplate.
Pseudo code
Other configuration items are not displayed one by one
spring.redis.jedis.pool.max-idle = 200 spring.redis.jedis.pool.min-idle = 10 spring.redis.jedis.pool.max-active = 200 spring.redis.jedis.pool.max-wait = 2000 Copy code
replace with
spring.redis.lettuce.pool.max-idle = 200 spring.redis.lettuce.pool.min-idle = 10 spring.redis.lettuce.pool.max-wait = 2000 spring.redis.lettuce.pool.max-active = 200 Copy code
The business code is also changed from jedis to redisTemplate
Pseudo code of jedis:
/** * Set commodity inventory to redis - jedis * @param goodId Commodity id * @param count Inventory * @return */ @PatchMapping("/storage/jedis") public String setStorageByJedis( @RequestParam("goodId") String goodId, @RequestParam("count") String count) { Jedis jedis = getJedis(); jedis.set("good:" + goodId, count); jedis.close(); return "success"; } Copy code
Pseudo code of redisTemplate:
/** * Set commodity inventory to redis - redisTemplate * @param goodId Commodity id * @param count Inventory * @return */ @PatchMapping("/storage") public String setStorage( @RequestParam("goodId") String goodId, @RequestParam("count") String count) { redisTemplate.opsForValue().set("good:" + goodId, count); return "success"; } Copy code
However, after all the work was done and released online with full confidence, online bug s broke out in a large area. It is a serious production accident.
From the error log, we can clearly see that because the data of String type cannot be converted into int type, a big question mark appears in my heart: clearly, what I saved in redis is a String that can be converted into digital type?
Cause analysis
View data through redis Desktop Manager visualization tool
It is found that the key value of string type has an extra pair of double quotation marks
what! Is there a time to change to disje?
After checking the code, it seems that one step is missing in the process of using redisTemplate: configuring serialization. Generally, if there is no special configuration or redis connection pool is to be used, it can only be added in the configuration center or configuration file
spring.redis.host = 172.0.0.1 spring.redis.port = 6379 spring.redis.password = 123456Copy to clipboardErrorCopied Copy code
Then inject redisTemplate and you can use it. It's very simple.
However, the default serializer used by RedisTemplate is JDK's own serializer. See the source code:
RedisTemplate class diagram
Because RedisTemplate inherits RedisAccessor and RedisAccessor implements InitializingBean, after the initialization of RedisTemplate class, you can override afterpropertieset() method to set serializer.
Solution
Write a redis configuration class and reset the serializer.
@Configuration @ConditionalOnClass(RedisOperations.class) public class RedisTemplateAutoConfiguration { @Bean @ConditionalOnMissingBean(name="redisTemplate") public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){ RedisTemplate template=new RedisTemplate(); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new StringRedisSerializer()); template.setConnectionFactory(redisConnectionFactory); return template; } Copy code
Here, the StringRedisSerializer serializer is only configured for the string type of redis. You can add the configuration of Hash object type according to the actual needs of the project.
Spring comes with a variety of serializers, as follows
You can also customize the serializer. You need to implement the RedisSerializer interface and override the serialize() and deserialize() methods.
For the convenience of demonstration, the global redis configuration class is not written, and the serializer is reset directly in the interface. The pseudo code is as follows:
@PatchMapping("/storage") public String setStorage( @RequestParam("goodId") String goodId, @RequestParam("count") String count) { redisTemplate.setKeySerializer(new StringRedisSerializer()); // Reset the serializer of redis string type key redisTemplate.setValueSerializer(new StringRedisSerializer()); // Reset the serializer of redis string type value redisTemplate.opsForValue().set("good:" + goodId, count); return "success"; }