Business scenario
The document number format generated by handling business in different regions must be: current month and year + 5 digits, and the number increases from 00001. The initial solution is to generate self increasing sequences, but because there are too many regions to complete a sequence in each region, this scheme will not work. So I thought of using redis to realize this business scenario. For redis and lua
I'm not familiar with the script, so I'll make a problem record of the development process this time.
Introducing redis dependency into pom file
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
redis configuration class
import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @EnableCaching @Configuration public class RedisCacheConfig extends CachingConfigurerSupport { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory cf) { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>(); redisTemplate.setConnectionFactory(cf); // 6. Serialization class, object mapping settings Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL,JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); RedisSerializer<String> stringSerializer = new StringRedisSerializer(); // The key adopts the serialization method of String redisTemplate.setKeySerializer(stringSerializer); // The key of hash is also serialized by String redisTemplate.setHashKeySerializer(stringSerializer); // value serialization adopts jackson redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); // The value serialization method of hash adopts jackson redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); redisTemplate.setDefaultSerializer(jackson2JsonRedisSerializer); return redisTemplate; } }
lua script implemented
local isExists = redis.call('exists',KEYS[1]) if isExists == 1 then redis.call('hincrby',KEYS[1],KEYS[2],ARGV[3]) redis.call('hset',KEYS[1],KEYS[3],ARGV[2]) end if isExists == 0 then redis.call('hset',KEYS[1],KEYS[2],ARGV[1],KEYS[3],ARGV[2]) end local fileNoMap = {} fileNoMap['bh'] = redis.call('hget',KEYS[1],KEYS[2]) fileNoMap['id'] = redis.call('hget',KEYS[1],KEYS[3]) return cjson.encode(fileNoMap)
Implemented java code
@RequestMapping("generateBusinessFileNo") @ResponseBody public ResultModel generateBusinessFileNo(String areaCode,String process,String businessId){ String result = ""; Map<String,String> fileNoMap = Maps.newHashMap(); DefaultRedisScript<Map> redisScript = new DefaultRedisScript<>(); //Set return value type redisScript.setResultType(Map.class); //Set lua script file path redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("redis/redis_genFileNo.lua"))); List<String> keys = new ArrayList<>(); //Use business phase code and region code as key s String key = process+areaCode; keys.add(key); //Get the businessFileNo in the current redis String exist_businessFileNo = (String) redisTemplate.opsForHash().get(key,"business.file.no"); String exist_businessId = (String)redisTemplate.opsForHash().get(key,"business.id"); String year = String.valueOf(LocalDate.now().getYear()); String businessFileNo = year+"00001"; long increment=1; //If the businessFileNo in the current redis is not empty and the year is inconsistent with the current year, the number starts from 00001 if(StringUtil.isNotEmpty(exist_businessFileNo)&&!(exist_businessFileNo.substring(0,4)).equals(year)){ fileNoMap.put("business.file.no",businessFileNo); fileNoMap.put("business.id",businessId); redisTemplate.opsForHash().values(fileNoMap); }else { //Judge whether the current business id is the same as the id that has been written to redis. If not, execute the lua script if(!businessId.equals(exist_businessId)){ keys.add("business.file.no"); keys.add("business.id"); Object executeObj = redisTemplate.opsForHash().getOperations().execute(redisScript, keys,businessFileNo,businessId,increment); result = String.valueOf(executeObj); System.out.println(result); }else{ //If the id is the same, the lua script will not be executed, and the value in the current redis will be returned directly Map<String,String> entries = redisTemplate.opsForHash().entries(key); result = JSON.toJSONString(entries); } } return ResultModel.ok().data(result); }
Some pits encountered in the process of realizing functions
I haven't touched lua script in my previous work. When I finished this function, I also queried relevant articles and found set and get that are only limited to redistemplate operation
The initial idea was to directly use the String data type for operation. Later, it was found that the String type could not meet the requirements, and the file number needs to be bound and stored with the current business id. otherwise, there is no condition to judge whether the request is a re call of the current business or a new business request, and the file id needs to be increased automatically
Initially, a script of type String was used
local isExists = redis.call('exists',KEYS[1]) if isExists == 1 then redis.call('incr',KEYS[1]) end if isExists == 0 then redis.call('set',KEYS[1],ARGV[1]) end local fileNo = redis.call('get',KEYS[1]) return fileNo
The first problem is redistemplate After executing the script, execute() finds that the value obtained is empty. However, after the redis client executes the command, it finds that the data has actually been written to redis, but there is a problem when taking the value. This is because the redis configuration class of the company is not fully written and the key and value are not serialized
The second problem is the lua script:
At that time, I wanted to use redis Call ('hgetall ', KEYS[[1]]) returns all the key value s together, but after execution, it is found that only one value can be returned, and the others seem to be overwritten. Later, baidu asked how to write the script to return the json string
The third problem is that after executing the lua script, it is found that the id does not increase automatically, and the obtained value is still empty. The lua script executed is as follows:
`
local isExists = redis.call('exists',KEYS[1]) if isExists == 1 then redis.call('hincrby',KEYS[1],KEYS[2],ARGV[3]) redis.call('hset',KEYS[1],KEYS[3],ARGV[2]) end if isExists == 0 then redis.call('hset',KEYS[1],KEYS[2],ARGV[1],KEYS[3],ARGV[2]) end local fileNoMap = {} fileNoMap['bh'] = redis.call('hget',KEYS[1],KEYS[2]) fileNoMap['id'] = redis.call('hget',KEYS[1],KEYS[3]) return cjson.encode(fileNoMap)
After that, the following code will execute normally
`local fileNoMap = {} local fileNo local fileId local isExists = redis.call('exists',KEYS[1]) if isExists == 1 then redis.call('hset',KEYS[1],KEYS[3],ARGV[2]) fileNo = redis.call('hincrby',KEYS[1],KEYS[2],ARGV[3]) fileId = redis.call('hget',KEYS[1],KEYS[3]) end if isExists == 0 then redis.call('hset',KEYS[1],KEYS[2],ARGV[1],KEYS[3],ARGV[2]) fileNo = redis.call('hget',KEYS[1],KEYS[2]) fileId = redis.call('hget',KEYS[1],KEYS[3]) end fileNoMap['bh'] = fileNo fileNoMap['id'] = fileId return cjson.encode(fileNoMap)
Fourth question
The business scenario was implemented this time. At first, the concurrency scenario was considered, so lua script was selected. Now the business code is redisTemplate opsForHash. Get() / set() and redisTemplate execute lua scripts together. I don't know if there will be problems in the case of concurrency. Later, I'm going to write a multi-threaded press to see what happens. If there are problems in the case of concurrency, locking should be considered