使用redis和lua脚本,springboot为基础构建生成全局唯一的id
项目的目录:
pom.xml内容, 这里注意: 需要加入 spring-boot-starter-data-redis依赖, 后面需要使用
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.4.7</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.xum</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>3.1.0</version> </plugin> </plugins> </build> </project>application.properties内容
spring.redis.database=0 spring.redis.host=127.0.0.1 spring.redis.port=6379 spring.redis.password= spring.redis.pool.max-active=200 spring.redis.pool.max-wait=-1 spring.redis.pool.max-idle=20 spring.redis.pool.min-idle=0 spring.redis.timeout=5000
在resources下面建立redis目录, 这里面主要放lua脚本, 本次使用的是get_uniquid_seq.lua脚本
local function get_uniquid_seq() --KEYS[1]:在redis中建立Hash哈希表, 类似用redis命令, hsetnx myHash k1 v1, 创建myHash哈希表, key为k1, value为v1 local key = tostring(KEYS[1]) --KEYS[2]:第二个参数代表序列号增长速度 local increment = tonumber(KEYS[2]) --KEYS[3]:第三个参数为myHash的key,指定加入myHash的key local hkey = tostring(KEYS[3]) --KEYS[4]:第四个参数指定为myHash的key赋值, 就是初始值 local seq = tonumber(KEYS[4]) --设置key的有效时间, 30天, 单位为秒 local month_in_seconds = 24 * 60 * 60 * 30 if (1 == redis.call('hsetnx', key, hkey, seq)) then redis.call('expire', key, month_in_seconds) return seq else local prev_seq = redis.call('hget', key, hkey) if(tonumber(prev_seq) < seq) then redis.call('hset', key, hkey, seq) return seq else return redis.call('hincrby', key, hkey, increment) end end end return get_uniquid_seq()
这里使用Junitest测试案例来运行效果, DemoApplicationTests.java内容:
package com.xum.demo; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.support.EncodedResource; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.scripting.support.ResourceScriptSource; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.UUID; import java.util.concurrent.TimeUnit; @SpringBootTest class DemoApplicationTests { private static final Logger LOG = LoggerFactory.getLogger(DemoApplicationTests.class); @Autowired private StringRedisTemplate redisTemplate; @Test public void testGetUniquidSeq() { for (int j = 1; j <=10; j++) { // 执行 lua 脚本 DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(); // 指定 lua 脚本 redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("redis/get_uniquid_seq.lua"))); // 指定返回类型 redisScript.setResultType(Long.class); //DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddhhmmSSk"); //LocalDateTime localDate = LocalDateTime.now(); //String uniquid_seq_value = localDate.format(formatter); String uniquid_seq_value = "2000061305253917"; //固定值, 格式就是yyyyMMddhhmmSSk, 可以随意变化 List<String> keyList = new ArrayList<String>(); keyList.add("uniquid_seq_by_lua"); //需要生成的哈希表 keyList.add("1"); //增量 keyList.add("uniquid_seq_key"); //生成对应哈希表里面的key keyList.add(uniquid_seq_value); //key对应的value, 初始值 // 参数一:redisScript,参数二:key列表,参数三:arg(可多个,也可以没有) Long result = redisTemplate.execute(redisScript, keyList); //获取uniquid LOG.info(j + " ,generate uniquid: " + result); } } }
这里测试的是 循环10遍, 来获得uniquid, 是为了测试效果
先启动redis, 如下图, 说明成功, 如何搭建redis环境,网上搜索一下(其实很简单, 下载redis, 直接安装, 命令运行)
此时可以用ideal工具运行test案例 或者 直接用maven命令来运行测试案例,
maven运行测试案例用法
mvn clean test 这个是运行所有的test案例
mvn clean test -Dtest=TestClassName#testMethod 这个是运行指定的测试案例的一个方法
我这里使用maven命令运行(也需要安装maven环境, 和安装java环境一样, 配置一下系统变量就行)
cmd切换到运行目录,直接运行测试案例的一个方法
测试效果如下:
循环10次,最终获得唯一的id, 增量是1, 前面我们设置的是1