分布式全局唯一ID方案, 使用Redis,实战篇

使用redis和lua脚本,springboot为基础构建生成全局唯一的id

项目的目录:

image

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, 直接安装, 命令运行)

image

此时可以用ideal工具运行test案例 或者 直接用maven命令来运行测试案例,

maven运行测试案例用法

mvn clean test   这个是运行所有的test案例

mvn clean test -Dtest=TestClassName#testMethod   这个是运行指定的测试案例的一个方法

我这里使用maven命令运行(也需要安装maven环境, 和安装java环境一样, 配置一下系统变量就行)

cmd切换到运行目录,直接运行测试案例的一个方法

image

测试效果如下:

image

循环10次,最终获得唯一的id, 增量是1,  前面我们设置的是1

posted on 2021-06-14 10:23  努力做一个伪程序员  阅读(516)  评论(0编辑  收藏  举报

导航