For one, before

The company has a core project redis client is always using Jedis, the technical director of the later request to replace the Jedis client with a more efficient and efficient lettuce client, and use the spring framework’s own RedisTemplate class to operate Redis.

However, things are hard to predict, is such a simple demand but let the old master turned over the boat…

Two, accident rehearsal

By default, this development task should be very easy:

  1. Shift the jedis connection pool configuration item in the configuration file to replace it with lettuce.
  2. Remove the jedis configuration code from the project;
  3. Replace the redisTemplate where you use jedis.

Pseudo code

Other configuration items are 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 = 2000Copy to clipboardErrorCopied
Copy the code

replace

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 = 200Copy to clipboardErrorCopied
Copy the code

The business code is also changed from Jedis to the pseudo-code of redisTemplate Jedis:

/** * Set the inventory to redis-jedis *@paramGoodId commodity ID *@paramCount 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 the code

Pseudocode for redisTemplate:

/** * Set the inventory of goods to redis-redistemplate *@paramGoodId commodity ID *@paramCount Inventory *@return* /
@PatchMapping("/storage")
public String setStorage(
    @RequestParam("goodId") String goodId,
    @RequestParam("count") String count) {
    redisTemplate.opsForValue().set("good:" + goodId, count);
    return "success";
}
Copy the code

However, after all the work was done and the launch was full of confidence, a large number of online bugs broke out. It is a serious production accident.

From the error log, we can see clearly that the data of String type cannot be converted to int, so I have a big question mark in my mind: it is clear that the String I saved in Redis can be converted to numeric type.

Cause analysis,

View the data using the Redis-Desktop-Manager visual tool

The string key-value pair value is overloaded with double quotation marks

What! You don’t have jedis, you have redisTemplate?

After some code review, it appears that there is a missing step in the process of using redisTemplate: Configuration serialization is usually added in the configuration center or configuration file if there is no special configuration or redis connection pool is used

Host = 172.0.0.1 spring.redis.port = 6379 spring.redis.password = 123456Copy to clipboardErrorCopiedCopy the code

Then inject the redisTemplate and you’re ready to use it, very simple.

However, the default serializer used by RedisTemplate is the JDK’s built-in serializer.

Look at the class diagram of the RedisTemplate

Since RedisTemplate inherits RedisAccessor, which implements InitializingBean, afterPropertiesSet() can be overridden after the RedisTemplate class is initialized, Set up the serializer.

The solution

Write a redis configuration class to 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 the code

The StringRedisSerializer serializer is configured only for the String type of Redis. You can configure the Hash object type based on project requirements.

Spring comes with a variety of serializers, as follows

You can also customize serializers by implementing the RedisSerializer interface and rewriting the serialize() and deserialize() methods.

For demonstration purposes, instead of writing the global redis configuration class, reset the serializer directly in the interface, with the following pseudocode:

@PatchMapping("/storage")
public String setStorage(
    @RequestParam("goodId") String goodId,
    @RequestParam("count") String count) {
    redisTemplate.setKeySerializer(new StringRedisSerializer()); // Reset the serializer for redis String key
    redisTemplate.setValueSerializer(new StringRedisSerializer()); // Reset the serializer for redis String value
    redisTemplate.opsForValue().set("good:" + goodId, count);
    return "success";
}
Copy the code