接着上一篇 php + redis + lua 实现一个简略的发号器(1)-- 原理篇,本篇讲一下spring-boot-starter 版本的发号器的具体实现。

1、基础知识

发号器的实现次要用到了上面的一些知识点:

1. php中的位运算的操作和求值

2. 计算机原码、补码、反码的基本概念

3. redis中lua脚本的编写和调试

4. 如何本人定一个spring-boot-starter

2、具体实现

├── pom.xml├── src│   ├── main│   │   ├── java│   │   │   └── com│   │   │       └── srorders│   │   │           └── starter│   │   │               ├── SignGenerator.java│   │   │               ├── UuidConfiguration.java│   │   │               └── UuidProperties.java│   │   └── resources│   │       ├── META-INF│   │       │   └── spring.factories│   │       └── application.yml│   └── test│       └── java│           └── com│               └── srorders│                   └── starter│                       └── SignGeneratorTest.java

pom的相干依赖:

<?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>    <groupId>com.srorders.starter</groupId>    <artifactId>uuid</artifactId>    <version>1.0.0</version>    <name>uuid</name>    <description>uuid</description>    <properties>        <java.version>1.8</java.version>    </properties>    <dependencies>        <!--Spring Boot Starter: START-->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter</artifactId>        </dependency>        <!-- 引入redis的starter -->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-data-redis</artifactId>        </dependency>        <dependency>            <groupId>org.projectlombok</groupId>            <artifactId>lombok</artifactId>            <optional>true</optional>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-test</artifactId>            <scope>test</scope>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-configuration-processor</artifactId>            <optional>true</optional>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-autoconfigure</artifactId>        </dependency>    </dependencies>    <dependencyManagement>        <dependencies>            <dependency>                <!-- Import dependency management from Spring Boot -->                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-dependencies</artifactId>                <version>2.6.1</version>                <type>pom</type>                <scope>import</scope>            </dependency>        </dependencies>    </dependencyManagement>    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>                <configuration>                    <excludes>                        <exclude>                            <groupId>org.projectlombok</groupId>                            <artifactId>lombok</artifactId>                        </exclude>                    </excludes>                </configuration>            </plugin>        </plugins>    </build></project>

定义一个spring-boot-starter次要分为4个局部:

1、定义一个法号器服务

package com.srorders.starter;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.data.redis.core.script.RedisScript;import java.time.Instant;import java.time.LocalDateTime;import java.time.OffsetDateTime;import java.time.format.DateTimeFormatter;import java.util.*;public class SignGenerator {    /**     * 申请64位内存     */    public static final int BITS_FULL = 64;    /**     * uuid     */    public static final String BITS_FULL_NAME = "id";    /**     * 1位符号位     */    public static final int BITS_PREFIX = 1;    /**     * 41工夫位     */    public static final int BITS_TIME = 41;    /**     * 工夫位名称     */    public static final String BITS_TIME_NAME = "diffTime";    /**     * 产生的工夫     */    public static final String BITS_GENERATE_TIME_NAME = "generateTime";    /**     * 5个服务器位     */    public static final int BITS_SERVER = 5;    /**     * 服务位名称     */    public static final String BITS_SERVER_NAME = "serverId";    /**     * 5个worker位     */    public static final int BITS_WORKER = 5;    /**     * worker位名称     */    public static final String BITS_WORKER_NAME = "workerId";    /**     * 12个自增位     */    public static final int BITS_SEQUENCE = 12;    /**     * 自增位名称     */    public static final String BITS_SEQUENCE_NAME = "sequenceNumber";    /**     * uuid配置     */    private UuidProperties uuidProperties;    /**     * redis client     */    private StringRedisTemplate redisTemplate;    /**     * 结构     *     * @param uuidProperties     */    public SignGenerator(UuidProperties uuidProperties, StringRedisTemplate redisTemplate) {        this.uuidProperties = uuidProperties;        this.redisTemplate = redisTemplate;    }    private long getStaterOffsetTime() {        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");        return LocalDateTime.parse(uuidProperties.getOffsetTime(), dateTimeFormatter).toInstant(OffsetDateTime.now().getOffset())                .toEpochMilli();    }    /**     * 获取uuid     *     * @return     */    public Map<String, Long> getNumber() throws InterruptedException {        HashMap<String, Long> result = new HashMap<>();        do {            long id = 0L;            long diffTime = Instant.now().toEpochMilli() - this.getStaterOffsetTime();            long maxDiffTime = (long) (Math.pow(2, BITS_TIME) - 1);            if (diffTime > maxDiffTime) {                throw new RuntimeException(String.format("the offsetTime: %s is too small", uuidProperties.getOffsetTime()));            }            // 对工夫位进行计算            int shift = BITS_FULL - BITS_PREFIX - BITS_TIME;            id |= diffTime << shift;            result.put(BITS_TIME_NAME, diffTime);            // 对server进行计算            shift = shift - BITS_SERVER;            id |= uuidProperties.getServerId() << shift;            result.put(BITS_SERVER_NAME, uuidProperties.getServerId());            // 对worker进行计算            shift = shift - BITS_WORKER;            id |= uuidProperties.getWorkerId() << shift;            result.put(BITS_WORKER_NAME, uuidProperties.getWorkerId());            // 对sequence进行计算            Long sequence = this.getSequence("uuid_" + diffTime);            long maxSequence = (long) (Math.pow(2, BITS_SEQUENCE) - 1);            if (sequence > maxSequence) {                Thread.sleep(1);            } else {                id |= sequence;                result.put(BITS_SEQUENCE_NAME, sequence);                result.put(BITS_FULL_NAME, id);                return result;            }        } while (true);    }    /**     * 获取自增id     *     * @param id     * @return     */    private Long getSequence(String id) {        String lua = " local sequenceKey = KEYS[1]; " +                "local sequenceNumber = redis.call(\"incr\", sequenceKey); " +                "redis.call(\"pexpire\", sequenceKey, 100); " +                "return sequenceNumber";        RedisScript<Long> redisScript = RedisScript.of(lua, Long.class);        return redisTemplate.execute(redisScript, Collections.singletonList(id));    }    /**     * 反解id     *     * @param id     * @return     */    public Map<String, Long> reverseNumber(Long id) {        HashMap<String, Long> result = new HashMap<>();        //time        int shift = BITS_FULL - BITS_PREFIX - BITS_TIME;        Long diffTime = (id >> shift) & (long) (Math.pow(2, BITS_TIME) - 1);        result.put(BITS_TIME_NAME, diffTime);        //generateTime        Long generateTime = diffTime + this.getStaterOffsetTime();        result.put(BITS_GENERATE_TIME_NAME, generateTime);        //server        shift = shift - BITS_SERVER;        Long server = (id >> shift) & (long) (Math.pow(2, BITS_SERVER) - 1);        result.put(BITS_SERVER_NAME, server);        //worker        shift = shift - BITS_WORKER;        Long worker = (id >> shift) & (long) (Math.pow(2, BITS_WORKER) - 1);        result.put(BITS_WORKER_NAME, worker);        //sequence        Long sequence = id & (long) (Math.pow(2, BITS_SEQUENCE) - 1);        result.put(BITS_SEQUENCE_NAME, sequence);        return result;    }}

2、定义一个产生bean的主动配置类

package com.srorders.starter;import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;import org.springframework.boot.context.properties.EnableConfigurationProperties;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.core.StringRedisTemplate;/** * @author zero */@Configuration@EnableConfigurationProperties(UuidProperties.class)@ConditionalOnClass({StringRedisTemplate.class, UuidProperties.class})public class UuidConfiguration {   @Bean   @ConditionalOnMissingBean(SignGenerator.class)   public SignGenerator signGenerator(UuidProperties  uuidProperties, StringRedisTemplate stringRedisTemplate) {       return new SignGenerator(uuidProperties, stringRedisTemplate);   }}

3、定义一个映射application.properties(application.yml)配置的对象

package com.srorders.starter;import lombok.Data;import org.springframework.boot.context.properties.ConfigurationProperties;/** * @author zero */@ConfigurationProperties("spring.uuid")@Datapublic class UuidProperties {    private Long serverId = 0L;    private Long workerId = 0L;    private String offsetTime = "2021-12-07 00:00:00";}

4、在resources目录下创立 META-INF 目录,而后在该目录下创立 spring.factories 文件,内容如下:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.srorders.starter.UuidConfiguration

3、运行一把

1、建设一个简略的spring-boot-web我的项目, pom.xml 文件内容如下:

<?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.6.1</version>        <relativePath/> <!-- lookup parent from repository -->    </parent>    <groupId>com.srorders.spring.boot</groupId>    <artifactId>starter-demo</artifactId>    <version>0.0.1-SNAPSHOT</version>    <name>starter-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-web</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-test</artifactId>            <scope>test</scope>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-autoconfigure</artifactId>        </dependency>        <!-- 这里咱们引入咱们自定义的 starter -->        <dependency>            <groupId>com.srorders.starter</groupId>            <artifactId>uuid</artifactId>            <version>1.0.0</version>        </dependency>    </dependencies>    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>                <configuration>                    <excludes>                        <exclude>                            <groupId>org.projectlombok</groupId>                            <artifactId>lombok</artifactId>                        </exclude>                    </excludes>                </configuration>            </plugin>        </plugins>    </build></project>

2、新建一个控制器内容如下

package com.srorders.spring.boot.controller;import com.srorders.starter.SignGenerator;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;import java.util.Map;@RestControllerpublic class UuidController {    @Autowired    SignGenerator signedService;    @GetMapping("/getUuid")    public String getUuid() throws InterruptedException {        return this.signedService.getNumber().get(SignGenerator.BITS_FULL_NAME).toString();    }    @GetMapping("/reverse")    public Map<String,Long> reverse(@RequestParam(value = "id") Long id) throws InterruptedException {        return this.signedService.reverseNumber(id);    }}