• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

分布式全局唯一id实现-2.1 springCloud-MyBatis-Plus集成百度分布式全局iduid-generator--优化版

武飞扬头像
拽着尾巴的鱼儿
帮助1

实现方式思考:

1 要想使得id 生成服务短时全部下线的情况下,依然可以稳定的提供全局id ,就必须要有一个中间的存储介质来进行过度;
2 这个中间存储介质,需要保证:

  • 多线程并发安全问题
  • 生成的id 持久化
  • 高性能,高可用

基于对中间存储介质的这些特性,发现redis 可以完美契合,redis 基于内存,高性能,并且可以进行集群,高可用;因为其单线程的特性,也能保证并发安全;redis 也有落盘机制,即时丢失了部分id也对全局影响不大;

具体实现:
2.1 id 生成服务改造:

2.1.1 引入redis jar:

 <!-- redis jar-->
 <dependency>
     <groupId>org.apache.commons</groupId>
     <artifactId>commons-pool2</artifactId>
 </dependency>
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>

2.2.2 redis 配置文件:
RedisConfig.java

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
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.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
@EnableCaching
public class RedisConfig {

    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private int port;

    @Value("${spring.redis.database}")
    private int db;

    @Value("${spring.redis.password:null}")
    private String password;
    /**
     * 配置lettuce连接池
     *
     * @return
     */
    @ConfigurationProperties(prefix = "spring.redis.lettuce.pool")
    public GenericObjectPoolConfig redisPool() {
        return new GenericObjectPoolConfig<>();
    }
    /**
     * 配置第二个数据源
     *
     * @return
     */
    public RedisStandaloneConfiguration redisConfig() {
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(host, port);
        redisStandaloneConfiguration.setDatabase(db);
        if (password != null && !"".equals(password) && !"null".equals(password)){
            redisStandaloneConfiguration.setPassword(RedisPassword.of(password));
        }
        return redisStandaloneConfiguration;
    }
    public LettuceConnectionFactory factory(GenericObjectPoolConfig redisPool, RedisStandaloneConfiguration redisConfig) {
        LettuceClientConfiguration clientConfiguration = LettucePoolingClientConfiguration.builder().poolConfig(redisPool).build();
        LettuceConnectionFactory connectionFactory  = new LettuceConnectionFactory(redisConfig, clientConfiguration);
        connectionFactory.afterPropertiesSet();
        return connectionFactory;
    }

    /**
     * 配置第一个数据源的RedisTemplate
     * 注意:这里指定使用名称=factory 的 RedisConnectionFactory
     *
     * @param
     * @return
     */
    @Bean("redisTemplate")
    public RedisTemplate<String, Object> redisTemplate() {
        RedisConnectionFactory factory1 = factory(redisPool(),redisConfig());
        return redisTemplate(factory1);
    }

    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        //  使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
        // objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(objectMapper);

        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        // 配置连接工厂
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        //使用StringRedisSerializer来序列化和反序列化redis的key值
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        // 值采用json序列化
        redisTemplate.setValueSerializer(serializer);
        // 设置hash key 和value序列化模式
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(serializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
    /***
     * stringRedisTemplate默认采用的是String的序列化策略
     * @param redisConnectionFactory
     * @return
     */
    @Bean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory){
        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
        stringRedisTemplate.setConnectionFactory(redisConnectionFactory);
        return stringRedisTemplate;
    }
}
学新通

RedisUtil.java:




import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;

/**
 * Redis工具类
 * modified
 *
 * @author
 * @version 1.1 (GitHub文档: https://github.com/whvcse/RedisUtil )
 * @date
 */
@Component
public class RedisUtil {

    @Autowired
    @Qualifier("stringRedisTemplate")
    private StringRedisTemplate redisTemplate;

    public void setRedisTemplate(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public StringRedisTemplate getRedisTemplate() {
        return this.redisTemplate;
    }

    /** -------------------key相关操作--------------------- */

    /**
     * 删除key
     *
     * @param key
     */
    public void delete(String key) {
        redisTemplate.delete(key);
    }

    /**
     * 批量删除key
     *
     * @param keys
     */
    public void delete(Collection<String> keys) {
        redisTemplate.delete(keys);
    }

    /**
     * 序列化key
     *
     * @param key
     * @return
     */
    public byte[] dump(String key) {
        return redisTemplate.dump(key);
    }

    /**
     * 是否存在key
     *
     * @param key
     * @return
     */
    public Boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 设置过期时间
     *
     * @param key
     * @param timeout
     * @param unit
     * @return
     */
    public Boolean expire(String key, long timeout, TimeUnit unit) {
        return redisTemplate.expire(key, timeout, unit);
    }

    /**
     * 多少秒失效
     *
     * @param key
     * @param timeout 秒为单位
     * @return
     */
    public Boolean expire(String key, long timeout) {
        return redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 设置过期时间
     *
     * @param key
     * @param date
     * @return
     */
    public Boolean expireAt(String key, Date date) {
        return redisTemplate.expireAt(key, date);
    }

    /**
     * 查找匹配的key
     *
     * @param pattern
     * @return
     */
    public Set<String> keys(String pattern) {
        return redisTemplate.keys(pattern);
    }

    /**
     * 将当前数据库的 key 移动到给定的数据库 db 当中
     *
     * @param key
     * @param dbIndex
     * @return
     */
    public Boolean move(String key, int dbIndex) {
        return redisTemplate.move(key, dbIndex);
    }

    /**
     * 移除 key 的过期时间,key 将持久保持
     *
     * @param key
     * @return
     */
    public Boolean persist(String key) {
        return redisTemplate.persist(key);
    }

    /**
     * 返回 key 的剩余的过期时间
     *
     * @param key
     * @param unit
     * @return
     */
    public Long getExpire(String key, TimeUnit unit) {
        return redisTemplate.getExpire(key, unit);
    }

    /**
     * 返回 key 的剩余的过期时间
     *
     * @param key
     * @return
     */
    public Long getExpire(String key) {
        return redisTemplate.getExpire(key);
    }

    /**
     * 从当前数据库中随机返回一个 key
     *
     * @return
     */
    public String randomKey() {
        return redisTemplate.randomKey();
    }

    /**
     * 修改 key 的名称
     *
     * @param oldKey
     * @param newKey
     */
    public void rename(String oldKey, String newKey) {
        redisTemplate.rename(oldKey, newKey);
    }

    /**
     * 仅当 newkey 不存在时,将 oldKey 改名为 newkey
     *
     * @param oldKey
     * @param newKey
     * @return
     */
    public Boolean renameIfAbsent(String oldKey, String newKey) {
        return redisTemplate.renameIfAbsent(oldKey, newKey);
    }

    /**
     * 返回 key 所储存的值的类型
     *
     * @param key
     * @return
     */
    public DataType type(String key) {
        return redisTemplate.type(key);
    }

    /** -------------------string相关操作--------------------- */

    /**
     * 设置指定 key 的值
     *
     * @param key
     * @param value
     */
    public void set(String key, String value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 指定秒
     *
     * @param key
     * @param value
     * @param seconds 多少秒失效
     */
    public void set(String key, String value, long seconds) {
        redisTemplate.opsForValue().set(key, value, seconds, TimeUnit.SECONDS);
    }

    /**
     * 获取指定 key 的值
     *
     * @param key
     * @return
     */
    public String get(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * 返回 key 中字符串值的子字符
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public String getRange(String key, long start, long end) {
        return redisTemplate.opsForValue().get(key, start, end);
    }

    /**
     * 将给定 key 的值设为 value ,并返回 key 的旧值(old value)
     *
     * @param key
     * @param value
     * @return
     */
    public String getAndSet(String key, String value) {
        return redisTemplate.opsForValue().getAndSet(key, value);
    }

    /**
     * 对 key 所储存的字符串值,获取指定偏移量上的位(bit)
     *
     * @param key
     * @param offset
     * @return
     */
    public Boolean getBit(String key, long offset) {
        return redisTemplate.opsForValue().getBit(key, offset);
    }

    /**
     * 批量获取
     *
     * @param keys
     * @return
     */
    public List<String> multiGet(Collection<String> keys) {
        return redisTemplate.opsForValue().multiGet(keys);
    }

    /**
     * 设置ASCII码, 字符串'a'的ASCII码是97, 转为二进制是'01100001', 此方法是将二进制第offset位值变为value
     *
     * @param key
     * @param value 值,true为1, false为0
     * @return
     */
    public boolean setBit(String key, long offset, boolean value) {
        return redisTemplate.opsForValue().setBit(key, offset, value);
    }

    /**
     * 将值 value 关联到 key ,并将 key 的过期时间设为 timeout
     *
     * @param key
     * @param value
     * @param timeout 过期时间
     * @param unit    时间单位, 天:TimeUnit.DAYS 小时:TimeUnit.HOURS 分钟:TimeUnit.MINUTES
     *                秒:TimeUnit.SECONDS 毫秒:TimeUnit.MILLISECONDS
     */
    public void setEx(String key, String value, long timeout, TimeUnit unit) {
        redisTemplate.opsForValue().set(key, value, timeout, unit);
    }

    /**
     * 设置并失效
     *
     * @param key
     * @param value
     * @param secconds 多少秒失效
     */
    public void setEx(String key, String value, long secconds) {
        redisTemplate.opsForValue().set(key, value, secconds, TimeUnit.SECONDS);
    }

    /**
     * 只有在 key 不存在时设置 key 的值
     *
     * @param key
     * @param value
     * @return 之前已经存在返回false, 不存在返回true
     */
    public boolean setIfAbsent(String key, String value) {
        return redisTemplate.opsForValue().setIfAbsent(key, value);
    }

    /**
     * 用 value 参数覆写给定 key 所储存的字符串值,从偏移量 offset 开始
     *
     * @param key
     * @param value
     * @param offset 从指定位置开始覆写
     */
    public void setRange(String key, String value, long offset) {
        redisTemplate.opsForValue().set(key, value, offset);
    }

    /**
     * 获取字符串的长度
     *
     * @param key
     * @return
     */
    public Long size(String key) {
        return redisTemplate.opsForValue().size(key);
    }

    /**
     * 批量添加
     *
     * @param maps
     */
    public void multiSet(Map<String, String> maps) {
        redisTemplate.opsForValue().multiSet(maps);
    }

    /**
     * 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在
     *
     * @param maps
     * @return 之前已经存在返回false, 不存在返回true
     */
    public boolean multiSetIfAbsent(Map<String, String> maps) {
        return redisTemplate.opsForValue().multiSetIfAbsent(maps);
    }

    /**
     * 增加(自增长), 负数则为自减
     *
     * @param key
     * @return
     */
    public Long incrBy(String key, long increment) {
        return redisTemplate.opsForValue().increment(key, increment);
    }

    /**
     * @param key
     * @return
     */
    public Double incrByFloat(String key, double increment) {
        return redisTemplate.opsForValue().increment(key, increment);
    }

    /**
     * 追加到末尾
     *
     * @param key
     * @param value
     * @return
     */
    public Integer append(String key, String value) {
        return redisTemplate.opsForValue().append(key, value);
    }

    /** -------------------hash相关操作------------------------- */

    /**
     * 获取存储在哈希表中指定字段的值
     *
     * @param key
     * @param field
     * @return
     */
    public Object hGet(String key, String field) {
        return redisTemplate.opsForHash().get(key, field);
    }

    /**
     * 获取所有给定字段的值
     *
     * @param key
     * @return
     */
    public Map<Object, Object> hGetAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 获取所有给定字段的值
     *
     * @param key
     * @param fields
     * @return
     */
    public List<Object> hMultiGet(String key, Collection<Object> fields) {
        return redisTemplate.opsForHash().multiGet(key, fields);
    }

    public void hPut(String key, String hashKey, String value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }

    public void hPutAll(String key, Map<String, String> maps) {
        redisTemplate.opsForHash().putAll(key, maps);
    }

    /**
     * 仅当hashKey不存在时才设置
     *
     * @param key
     * @param hashKey
     * @param value
     * @return
     */
    public Boolean hPutIfAbsent(String key, String hashKey, String value) {
        return redisTemplate.opsForHash().putIfAbsent(key, hashKey, value);
    }

    /**
     * 删除一个或多个哈希表字段
     *
     * @param key
     * @param fields
     * @return
     */
    public Long hDelete(String key, Object... fields) {
        return redisTemplate.opsForHash().delete(key, fields);
    }

    /**
     * 查看哈希表 key 中,指定的字段是否存在
     *
     * @param key
     * @param field
     * @return
     */
    public boolean hExists(String key, String field) {
        return redisTemplate.opsForHash().hasKey(key, field);
    }

    /**
     * 为哈希表 key 中的指定字段的整数值加上增量 increment
     *
     * @param key
     * @param field
     * @param increment
     * @return
     */
    public Long hIncrBy(String key, Object field, long increment) {
        return redisTemplate.opsForHash().increment(key, field, increment);
    }

    /**
     * 为哈希表 key 中的指定字段的整数值加上增量 increment
     *
     * @param key
     * @param field
     * @param delta
     * @return
     */
    public Double hIncrByFloat(String key, Object field, double delta) {
        return redisTemplate.opsForHash().increment(key, field, delta);
    }

    /**
     * 获取所有哈希表中的字段
     *
     * @param key
     * @return
     */
    public Set<Object> hKeys(String key) {
        return redisTemplate.opsForHash().keys(key);
    }

    /**
     * 获取哈希表中字段的数量
     *
     * @param key
     * @return
     */
    public Long hSize(String key) {
        return redisTemplate.opsForHash().size(key);
    }

    /**
     * 获取哈希表中所有值
     *
     * @param key
     * @return
     */
    public List<Object> hValues(String key) {
        return redisTemplate.opsForHash().values(key);
    }

    /**
     * 迭代哈希表中的键值对
     *
     * @param key
     * @param options
     * @return
     */
    public Cursor<Entry<Object, Object>> hScan(String key, ScanOptions options) {
        return redisTemplate.opsForHash().scan(key, options);
    }

    /** ------------------------list相关操作---------------------------- */

    /**
     * 通过索引获取列表中的元素
     *
     * @param key
     * @param index
     * @return
     */
    public String lIndex(String key, long index) {
        return redisTemplate.opsForList().index(key, index);
    }

    /**
     * 获取列表指定范围内的元素
     *
     * @param key
     * @param start 开始位置, 0是开始位置
     * @param end   结束位置, -1返回所有
     * @return
     */
    public List<String> lRange(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }

    /**
     * 存储在list头部
     *
     * @param key
     * @param value
     * @return
     */
    public Long lLeftPush(String key, String value) {
        return redisTemplate.opsForList().leftPush(key, value);
    }

    /**
     * @param key
     * @param value
     * @return
     */
    public Long lLeftPushAll(String key, String... value) {
        return redisTemplate.opsForList().leftPushAll(key, value);
    }

    /**
     * @param key
     * @param value
     * @return
     */
    public Long lLeftPushAll(String key, Collection<String> value) {
        return redisTemplate.opsForList().leftPushAll(key, value);
    }

    /**
     * 当list存在的时候才加入
     *
     * @param key
     * @param value
     * @return
     */
    public Long lLeftPushIfPresent(String key, String value) {
        return redisTemplate.opsForList().leftPushIfPresent(key, value);
    }

    /**
     * 如果pivot存在,再pivot前面添加
     *
     * @param key
     * @param pivot
     * @param value
     * @return
     */
    public Long lLeftPush(String key, String pivot, String value) {
        return redisTemplate.opsForList().leftPush(key, pivot, value);
    }

    /**
     * @param key
     * @param value
     * @return
     */
    public Long lRightPush(String key, String value) {
        return redisTemplate.opsForList().rightPush(key, value);
    }

    /**
     * @param key
     * @param value
     * @return
     */
    public Long lRightPushAll(String key, String... value) {
        return redisTemplate.opsForList().rightPushAll(key, value);
    }

    /**
     * @param key
     * @param value
     * @return
     */
    public Long lRightPushAll(String key, Collection<String> value) {
        return redisTemplate.opsForList().rightPushAll(key, value);
    }

    /**
     * 为已存在的列表添加值
     *
     * @param key
     * @param value
     * @return
     */
    public Long lRightPushIfPresent(String key, String value) {
        return redisTemplate.opsForList().rightPushIfPresent(key, value);
    }

    /**
     * 在pivot元素的右边添加值
     *
     * @param key
     * @param pivot
     * @param value
     * @return
     */
    public Long lRightPush(String key, String pivot, String value) {
        return redisTemplate.opsForList().rightPush(key, pivot, value);
    }

    /**
     * 通过索引设置列表元素的值
     *
     * @param key
     * @param index 位置
     * @param value
     */
    public void lSet(String key, long index, String value) {
        redisTemplate.opsForList().set(key, index, value);
    }

    /**
     * 移出并获取列表的第一个元素
     *
     * @param key
     * @return 删除的元素
     */
    public String lLeftPop(String key) {
        return redisTemplate.opsForList().leftPop(key);
    }

    /**
     * 移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
     *
     * @param key
     * @param timeout 等待时间
     * @param unit    时间单位
     * @return
     */
    public String lBLeftPop(String key, long timeout, TimeUnit unit) {
        return redisTemplate.opsForList().leftPop(key, timeout, unit);
    }

    /**
     * 移除并获取列表最后一个元素
     *
     * @param key
     * @return 删除的元素
     */
    public String lRightPop(String key) {
        return redisTemplate.opsForList().rightPop(key);
    }

    /**
     * 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
     *
     * @param key
     * @param timeout 等待时间
     * @param unit    时间单位
     * @return
     */
    public String lBRightPop(String key, long timeout, TimeUnit unit) {
        return redisTemplate.opsForList().rightPop(key, timeout, unit);
    }

    /**
     * 移除列表的最后一个元素,并将该元素添加到另一个列表并返回
     *
     * @param sourceKey
     * @param destinationKey
     * @return
     */
    public String lRightPopAndLeftPush(String sourceKey, String destinationKey) {
        return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey,
                destinationKey);
    }

    /**
     * 从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
     *
     * @param sourceKey
     * @param destinationKey
     * @param timeout
     * @param unit
     * @return
     */
    public String lBRightPopAndLeftPush(String sourceKey, String destinationKey,
                                        long timeout, TimeUnit unit) {
        return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey,
                destinationKey, timeout, unit);
    }

    /**
     * 删除集合中值等于value得元素
     *
     * @param key
     * @param index index=0, 删除所有值等于value的元素; index>0, 从头部开始删除第一个值等于value的元素;
     *              index<0, 从尾部开始删除第一个值等于value的元素;
     * @param value
     * @return
     */
    public Long lRemove(String key, long index, String value) {
        return redisTemplate.opsForList().remove(key, index, value);
    }

    /**
     * 裁剪list
     *
     * @param key
     * @param start
     * @param end
     */
    public void lTrim(String key, long start, long end) {
        redisTemplate.opsForList().trim(key, start, end);
    }

    /**
     * 获取列表长度
     *
     * @param key
     * @return
     */
    public Long lLen(String key) {
        return redisTemplate.opsForList().size(key);
    }

    /** --------------------set相关操作-------------------------- */

    /**
     * set添加元素
     *
     * @param key
     * @param values
     * @return
     */
    public Long sAdd(String key, String... values) {
        return redisTemplate.opsForSet().add(key, values);
    }

    /**
     * set移除元素
     *
     * @param key
     * @param values
     * @return
     */
    public Long sRemove(String key, Object... values) {
        return redisTemplate.opsForSet().remove(key, values);
    }

    /**
     * 移除并返回集合的一个随机元素
     *
     * @param key
     * @return
     */
    public String sPop(String key) {
        return redisTemplate.opsForSet().pop(key);
    }

    /**
     * 将元素value从一个集合移到另一个集合
     *
     * @param key
     * @param value
     * @param destKey
     * @return
     */
    public Boolean sMove(String key, String value, String destKey) {
        return redisTemplate.opsForSet().move(key, value, destKey);
    }

    /**
     * 获取集合的大小
     *
     * @param key
     * @return
     */
    public Long sSize(String key) {
        return redisTemplate.opsForSet().size(key);
    }

    /**
     * 判断集合是否包含value
     *
     * @param key
     * @param value
     * @return
     */
    public Boolean sIsMember(String key, Object value) {
        return redisTemplate.opsForSet().isMember(key, value);
    }

    /**
     * 获取两个集合的交集
     *
     * @param key
     * @param otherKey
     * @return
     */
    public Set<String> sIntersect(String key, String otherKey) {
        return redisTemplate.opsForSet().intersect(key, otherKey);
    }

    /**
     * 获取key集合与多个集合的交集
     *
     * @param key
     * @param otherKeys
     * @return
     */
    public Set<String> sIntersect(String key, Collection<String> otherKeys) {
        return redisTemplate.opsForSet().intersect(key, otherKeys);
    }

    /**
     * key集合与otherKey集合的交集存储到destKey集合中
     *
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */
    public Long sIntersectAndStore(String key, String otherKey, String destKey) {
        return redisTemplate.opsForSet().intersectAndStore(key, otherKey,
                destKey);
    }

    /**
     * key集合与多个集合的交集存储到destKey集合中
     *
     * @param key
     * @param otherKeys
     * @param destKey
     * @return
     */
    public Long sIntersectAndStore(String key, Collection<String> otherKeys,
                                   String destKey) {
        return redisTemplate.opsForSet().intersectAndStore(key, otherKeys,
                destKey);
    }

    /**
     * 获取两个集合的并集
     *
     * @param key
     * @param otherKeys
     * @return
     */
    public Set<String> sUnion(String key, String otherKeys) {
        return redisTemplate.opsForSet().union(key, otherKeys);
    }

    /**
     * 获取key集合与多个集合的并集
     *
     * @param key
     * @param otherKeys
     * @return
     */
    public Set<String> sUnion(String key, Collection<String> otherKeys) {
        return redisTemplate.opsForSet().union(key, otherKeys);
    }

    /**
     * key集合与otherKey集合的并集存储到destKey中
     *
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */
    public Long sUnionAndStore(String key, String otherKey, String destKey) {
        return redisTemplate.opsForSet().unionAndStore(key, otherKey, destKey);
    }

    /**
     * key集合与多个集合的并集存储到destKey中
     *
     * @param key
     * @param otherKeys
     * @param destKey
     * @return
     */
    public Long sUnionAndStore(String key, Collection<String> otherKeys,
                               String destKey) {
        return redisTemplate.opsForSet().unionAndStore(key, otherKeys, destKey);
    }

    /**
     * 获取两个集合的差集
     *
     * @param key
     * @param otherKey
     * @return
     */
    public Set<String> sDifference(String key, String otherKey) {
        return redisTemplate.opsForSet().difference(key, otherKey);
    }

    /**
     * 获取key集合与多个集合的差集
     *
     * @param key
     * @param otherKeys
     * @return
     */
    public Set<String> sDifference(String key, Collection<String> otherKeys) {
        return redisTemplate.opsForSet().difference(key, otherKeys);
    }

    /**
     * key集合与otherKey集合的差集存储到destKey中
     *
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */
    public Long sDifference(String key, String otherKey, String destKey) {
        return redisTemplate.opsForSet().differenceAndStore(key, otherKey,
                destKey);
    }

    /**
     * key集合与多个集合的差集存储到destKey中
     *
     * @param key
     * @param otherKeys
     * @param destKey
     * @return
     */
    public Long sDifference(String key, Collection<String> otherKeys,
                            String destKey) {
        return redisTemplate.opsForSet().differenceAndStore(key, otherKeys,
                destKey);
    }

    /**
     * 获取集合所有元素
     *
     * @param key
     * @return
     */
    public Set<String> setMembers(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 随机获取集合中的一个元素
     *
     * @param key
     * @return
     */
    public String sRandomMember(String key) {
        return redisTemplate.opsForSet().randomMember(key);
    }

    /**
     * 随机获取集合中count个元素
     *
     * @param key
     * @param count
     * @return
     */
    public List<String> sRandomMembers(String key, long count) {
        return redisTemplate.opsForSet().randomMembers(key, count);
    }

    /**
     * 随机获取集合中count个元素并且去除重复的
     *
     * @param key
     * @param count
     * @return
     */
    public Set<String> sDistinctRandomMembers(String key, long count) {
        return redisTemplate.opsForSet().distinctRandomMembers(key, count);
    }

    /**
     * @param key
     * @param options
     * @return
     */
    public Cursor<String> sScan(String key, ScanOptions options) {
        return redisTemplate.opsForSet().scan(key, options);
    }

    /**------------------zSet相关操作--------------------------------*/

    /**
     * 添加元素,有序集合是按照元素的score值由小到大排列
     *
     * @param key
     * @param value
     * @param score
     * @return
     */
    public Boolean zAdd(String key, String value, double score) {
        return redisTemplate.opsForZSet().add(key, value, score);
    }

    /**
     * 元素值按维度自增
     *
     * @param key
     * @param value
     * @param score
     * @return
     */
    public Double zAddForIncre(String key, String value, double score) {
        return redisTemplate.opsForZSet().incrementScore(key, value, score);
    }

    /**
     * @param key
     * @param values
     * @return
     */
    public Long zAdd(String key, Set<TypedTuple<String>> values) {
        return redisTemplate.opsForZSet().add(key, values);
    }

    /**
     * @param key
     * @param values
     * @return
     */
    public Long zRemove(String key, Object... values) {
        return redisTemplate.opsForZSet().remove(key, values);
    }

    /**
     * 增加元素的score值,并返回增加后的值
     *
     * @param key
     * @param value
     * @param delta
     * @return
     */
    public Double zIncrementScore(String key, String value, double delta) {
        return redisTemplate.opsForZSet().incrementScore(key, value, delta);
    }

    /**
     * 返回元素在集合的排名,有序集合是按照元素的score值由小到大排列
     *
     * @param key
     * @param value
     * @return 0表示第一位
     */
    public Long zRank(String key, Object value) {
        return redisTemplate.opsForZSet().rank(key, value);
    }

    /**
     * 返回元素在集合的排名,按元素的score值由大到小排列
     *
     * @param key
     * @param value
     * @return
     */
    public Long zReverseRank(String key, Object value) {
        return redisTemplate.opsForZSet().reverseRank(key, value);
    }

    /**
     * 获取集合的元素, 从小到大排序
     *
     * @param key
     * @param start 开始位置
     * @param end   结束位置, -1查询所有
     * @return
     */
    public Set<String> zRange(String key, long start, long end) {
        return redisTemplate.opsForZSet().range(key, start, end);
    }

    /**
     * 获取集合元素, 并且把score值也获取
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<TypedTuple<String>> zRangeWithScores(String key, long start,
                                                    long end) {
        return redisTemplate.opsForZSet().rangeWithScores(key, start, end);
    }

    /**
     * 根据Score值查询集合元素
     *
     * @param key
     * @param min 最小值
     * @param max 最大值
     * @return
     */
    public Set<String> zRangeByScore(String key, double min, double max) {
        return redisTemplate.opsForZSet().rangeByScore(key, min, max);
    }

//    public Set<String> hSet(String key, String valueName, Objects value) {
//        return redisTemplate.opsForHash().put(key,valueName,value);
//    }

    /**
     * 根据Score值查询集合元素, 从小到大排序
     *
     * @param key
     * @param min 最小值
     * @param max 最大值
     * @return
     */
    public Set<TypedTuple<String>> zRangeByScoreWithScores(String key,
                                                           double min, double max) {
        return redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max);
    }

    /**
     * @param key
     * @param min
     * @param max
     * @param start
     * @param end
     * @return
     */
    public Set<TypedTuple<String>> zRangeByScoreWithScores(String key,
                                                           double min, double max, long start, long end) {
        return redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max,
                start, end);
    }

    /**
     * 获取集合的元素, 从大到小排序
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<String> zReverseRange(String key, long start, long end) {
        return redisTemplate.opsForZSet().reverseRange(key, start, end);
    }

    /**
     * 获取集合的元素, 从大到小排序, 并返回score值
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<TypedTuple<String>> zReverseRangeWithScores(String key,
                                                           long start, long end) {
        return redisTemplate.opsForZSet().reverseRangeWithScores(key, start,
                end);
    }

    /**
     * 根据Score值查询集合元素, 从大到小排序
     *
     * @param key
     * @param min
     * @param max
     * @return
     */
    public Set<String> zReverseRangeByScore(String key, double min,
                                            double max) {
        return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max);
    }

    /**
     * 根据Score值查询集合元素, 从大到小排序
     *
     * @param key
     * @param min
     * @param max
     * @return
     */
    public Set<TypedTuple<String>> zReverseRangeByScoreWithScores(
            String key, double min, double max) {
        return redisTemplate.opsForZSet().reverseRangeByScoreWithScores(key,
                min, max);
    }

    /**
     * @param key
     * @param min
     * @param max
     * @param start
     * @param end
     * @return
     */
    public Set<String> zReverseRangeByScore(String key, double min,
                                            double max, long start, long end) {
        return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max,
                start, end);
    }

    /**
     * 根据score值获取集合元素数量
     *
     * @param key
     * @param min
     * @param max
     * @return
     */
    public Long zCount(String key, double min, double max) {
        return redisTemplate.opsForZSet().count(key, min, max);
    }

    /**
     * 获取集合大小
     *
     * @param key
     * @return
     */
    public Long zSize(String key) {
        return redisTemplate.opsForZSet().size(key);
    }

    /**
     * 获取集合大小
     *
     * @param key
     * @return
     */
    public Long zZCard(String key) {
        return redisTemplate.opsForZSet().zCard(key);
    }

    /**
     * 获取集合中value元素的score值
     *
     * @param key
     * @param value
     * @return
     */
    public Double zScore(String key, Object value) {
        return redisTemplate.opsForZSet().score(key, value);
    }

    /**
     * 移除指定索引位置的成员
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Long zRemoveRange(String key, long start, long end) {
        return redisTemplate.opsForZSet().removeRange(key, start, end);
    }

    /**
     * 根据指定的score值的范围来移除成员
     *
     * @param key
     * @param min
     * @param max
     * @return
     */
    public Long zRemoveRangeByScore(String key, double min, double max) {
        return redisTemplate.opsForZSet().removeRangeByScore(key, min, max);
    }

    /**
     * 获取key和otherKey的并集并存储在destKey中
     *
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */
    public Long zUnionAndStore(String key, String otherKey, String destKey) {
        return redisTemplate.opsForZSet().unionAndStore(key, otherKey, destKey);
    }

    /**
     * @param key
     * @param otherKeys
     * @param destKey
     * @return
     */
    public Long zUnionAndStore(String key, Collection<String> otherKeys,
                               String destKey) {
        return redisTemplate.opsForZSet()
                .unionAndStore(key, otherKeys, destKey);
    }

    /**
     * 交集
     *
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */
    public Long zIntersectAndStore(String key, String otherKey,
                                   String destKey) {
        return redisTemplate.opsForZSet().intersectAndStore(key, otherKey,
                destKey);
    }

    /**
     * 交集
     *
     * @param key
     * @param otherKeys
     * @param destKey
     * @return
     */
    public Long zIntersectAndStore(String key, Collection<String> otherKeys,
                                   String destKey) {
        return redisTemplate.opsForZSet().intersectAndStore(key, otherKeys,
                destKey);
    }

    /**
     * @param key
     * @param options
     * @return
     */
    public Cursor<TypedTuple<String>> zScan(String key, ScanOptions options) {
        return redisTemplate.opsForZSet().scan(key, options);
    }

    /** -------------------锁相关操作--------------------- */

    /**
     * 尝试获取锁
     *
     * @param key
     * @param timeout 秒为单位
     * @return
     */
    public boolean tryLock(String key, long timeout) {
        if (setIfAbsent(key, "1")) {
            expire(key, timeout, TimeUnit.SECONDS);
            return true;
        } else {
            return false;
        }
    }

    /**
     * 释放锁
     * @param key
     * @return
     */
    public boolean unlock(String key) {
        delete(key);
        return true;
    }
}

学新通

2.1.3 redis 配置:

spring:
	redis:
	    host: localhost
	    port: 6379
	    timeout: 120000
	    database: 1
	    password: 123456
	    lettuce:
	      pool:
	        max-active: 50
	        max-idle: 8
	        max-wait: -1
	        min-idle: 1

2.1.4 id 的初始化:
(1) 设置id的生成规则:
UidConfig.java:

import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;

/**
 * uid 服务端参数配置
 */
@Getter
@RefreshScope
@Configuration
public class UidConfig {
	// id 初始化生成数量
    @Value("${uid.size:65536}")
    private Integer uidSize;
    // uid 剩余小于多少时补充 uid
    @Value("${uid.radio:50}")
    private Integer uidRadio;
    // 补充 uid 最大线程数
    @Value("${uid.thread-count:5}")
    private Integer threadCount;
    // uid 低于uidInitSize * uidUserRadio /100 持续 second 秒 时增加补充uid 的线程
    @Value("${uid.second:300}")
    private Long second;

}

学新通

(2)id 的初始化:
UidInit.java


import com.xfvape.uid.UidGenerator;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import lombok.extern.slf4j.Slf4j;
import org.example.api.constant.UidTakeConstant;
import org.example.api.utils.ExceptionFormatUtil;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

/**
 * uid 初始化
 */
@Slf4j
@Component
public class UidInit {
    @Resource
    private UidGenerator uidGenerator;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private UidConfig uidConfig;

    /**
     * bean 加载之后初始化 redis uid
     */
    @PostConstruct
    public void initUid() {
        Integer uidSize = uidConfig.getUidSize();
        Integer uidRadio = uidConfig.getUidRadio();
        Integer threadCount = uidConfig.getThreadCount();
        Long second = uidConfig.getSecond();
        Assert.isTrue(null != uidSize && uidSize > 1, "uid.size:"   uidSize   " 非法");
        Assert.isTrue(null != uidRadio && uidRadio < 100 && uidRadio > 0, "uid.radio:"   uidRadio   " 非法");
        Assert.isTrue(null != threadCount && threadCount > 0, "uid.thread-count:"   threadCount   " 非法");
        Assert.isTrue(null != second && second > 0, "uid.second:"   second   " 非法");

        redisTemplate.opsForValue().set(UidTakeConstant.redisUidSizeKey, uidSize);
        redisTemplate.opsForValue().set(UidTakeConstant.redisUidRadioKey, uidRadio);

        gotoInitUid(uidSize.longValue());

    }

    /**
     * 向redis 中初始化 uid 长度为 UidConfig uidSize 长度的id
     *
     * @param uidSize
     */
    private void gotoInitUid(Long uidSize) {
        log.debug("init work uid");
//        initUidList(uidSize);
        // uid 进行初始化
//        创建执行线程池SingleThreadEventExecutor
        EventExecutorGroup group = new DefaultEventExecutorGroup(1);
        //创建Callable
        Future<StringBuffer> f = group.submit(new Callable<StringBuffer>() {
            @Override
            public StringBuffer call() throws Exception {
                log.debug("初始化uid操作开始...");
                //  init
                return initUidList(uidSize);
            }
        });
        // 添加监听者
        f.addListener(new FutureListener<StringBuffer>() {
            @Override
            public void operationComplete(Future<StringBuffer> objectFuture) throws Exception {
                try {
                    if (null == objectFuture || ObjectUtils.isEmpty(objectFuture.get())) {
                        log.debug("初始化uid操作完成...");
                    } else {
                        log.error("初始化uid操作失败,cause:{}", objectFuture.get().toString());
                        throw new IllegalArgumentException(objectFuture.get().toString());
                    }
                } finally {
                    group.shutdownGracefully();
                }
            }
        });


    }

    /**
     * 初始化 uid
     */
    private StringBuffer initUidList(Long uidSize) {
        StringBuffer stringBuffer = new StringBuffer();
        // get lock
        boolean flag = redisTemplate.opsForValue().setIfAbsent(UidTakeConstant.redisUidListKey, "1", 300, TimeUnit.SECONDS);
        if (!flag) {
            log.debug("初始化uid操作未获取到锁...");
            return null;
        }
        Long nowUidSize = redisTemplate.opsForList().size(UidTakeConstant.redisUidList);
        uidSize = uidSize - nowUidSize;
        if (uidSize <= 0) {
            redisTemplate.delete(UidTakeConstant.redisUidListKey);
            return null;
        }
        try {
            for (Integer i = 0; i < uidSize; i  ) {
                long uid = uidGenerator.getUID();
                redisTemplate.opsForList().leftPush(UidTakeConstant.redisUidList, uid);
            }
        } catch (Exception ex) {
            stringBuffer.append(ExceptionFormatUtil.buildErrorMessage(ex));
        } finally {
            redisTemplate.delete(UidTakeConstant.redisUidListKey);
        }
        return stringBuffer;
    }
}

学新通

这样每次在项目启动后都会先去补充id 到预先设置的size 大小;

2.2 id 消费服务改造:

此时消费端获取id 时可以直接从redis 中获取;
2.2.1 redis jar ,redsiConfig ,redisUtil 的引入参考:2.1.1-2.1.3
2.2.2 以下改造方式二选一即可:
方式1 :对于DefaultIdentifierGenerator.java 改造,使其直接从redis 中获取id:

package com.baomidou.mybatisplus.core.incrementer;

import com.example.uidconsumer.util.ApplicationContextUtils;
import lombok.extern.slf4j.Slf4j;
import org.example.api.constant.UidTakeConstant;
import org.example.api.service.UidTakeService;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;

@Slf4j
public class DefaultIdentifierGenerator implements IdentifierGenerator {
//    @Resource
//    private UidTakeService uidTakeService;

//    @Override
//    public BigDecimal nextId(Object entity) {
//        if (null == uidTakeService ){
//            uidTakeService =ApplicationContextUtils.getBean(UidTakeService.class);
//        }
//        Long uid  = uidTakeService.getUid();
//
//        BigDecimal bUid = new BigDecimal("123" uid);
//        return bUid;
//    }

    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private UidTakeService remoteUidTakeService;


    @Override
    public BigDecimal nextId(Object entity) {
        if (null == redisTemplate) {
            redisTemplate = ApplicationContextUtils.getBean(StringRedisTemplate.class);
        }
        Assert.isTrue(null != redisTemplate, "redisTemplate bean:"   redisTemplate   " 非法");
        String uid = (String) redisTemplate.opsForList().rightPop(UidTakeConstant.redisUidList);
        // uid  为空 自旋等待 uid 线程补充
        while (ObjectUtils.isEmpty(uid)) {
            uid = (String) redisTemplate.opsForList().rightPop(UidTakeConstant.redisUidList);
        }
        BigDecimal bUid = new BigDecimal(uid);
        initRedisUidListSize();
        return bUid;
    }

    /**
     * 是否需要重置 uid size 数量
     */
    private void initRedisUidListSize() {
        Long size = redisTemplate.opsForList().size(UidTakeConstant.redisUidList);
        Integer uidSize = Integer.parseInt(redisTemplate.opsForValue().get(UidTakeConstant.redisUidSizeKey).toString());
        Integer uidRadio = Integer.parseInt(redisTemplate.opsForValue().get(UidTakeConstant.redisUidRadioKey).toString());
        Integer resetSize = uidSize * uidRadio / 100;
        if (size >= resetSize.longValue()) {
            return;
        }
        // 需要重置uid size
        if (null == remoteUidTakeService) {
            remoteUidTakeService = ApplicationContextUtils.getBean(UidTakeService.class);
        }
        log.error("need reset uid size");
        Assert.isTrue(null != remoteUidTakeService, "remoteUidTakeService bean:"   remoteUidTakeService   " 非法");
        remoteUidTakeService.resetUid();
    }

}

学新通

方式2:实现IdentifierGenerator 重写nextId 方法:

@Slf4j
@Component
public class CustomIdGenerator implements IdentifierGenerator {
	@Resource
    private RedisTemplate redisTemplate;
    @Resource
    private UidTakeService remoteUidTakeService;


    @Override
    public BigDecimal nextId(Object entity) {
        if (null == redisTemplate) {
            redisTemplate = ApplicationContextUtils.getBean(StringRedisTemplate.class);
        }
        Assert.isTrue(null != redisTemplate, "redisTemplate bean:"   redisTemplate   " 非法");
        String uid = (String) redisTemplate.opsForList().rightPop(UidTakeConstant.redisUidList);
        // uid  为空 自旋等待 uid 线程补充
        while (ObjectUtils.isEmpty(uid)) {
            uid = (String) redisTemplate.opsForList().rightPop(UidTakeConstant.redisUidList);
        }
        BigDecimal bUid = new BigDecimal(uid);
        initRedisUidListSize();
        return bUid;
    }
}

	/**
	* 是否需要重置 uid size 数量
	 */
	private void initRedisUidListSize() {
	    Long size = redisTemplate.opsForList().size(UidTakeConstant.redisUidList);
	    Integer uidSize = Integer.parseInt(redisTemplate.opsForValue().get(UidTakeConstant.redisUidSizeKey).toString());
	    Integer uidRadio = Integer.parseInt(redisTemplate.opsForValue().get(UidTakeConstant.redisUidRadioKey).toString());
	    Integer resetSize = uidSize * uidRadio / 100;
	    if (size >= resetSize.longValue()) {
	        return;
	    }
	    // 需要重置uid size
	    if (null == remoteUidTakeService) {
	        remoteUidTakeService = ApplicationContextUtils.getBean(UidTakeService.class);
	    }
	    log.error("need reset uid size");
	    Assert.isTrue(null != remoteUidTakeService, "remoteUidTakeService bean:"   remoteUidTakeService   " 非法");
	    remoteUidTakeService.resetUid();
	}

}
学新通

2.3 id 生成的服务增加id 补充的业务:

2.3.1 定义feign接口:
UidTakeService.java



import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

@FeignClient(name = "uid-take", fallback = UidTakeFeignServiceFallBack.class)
public interface UidTakeService {
    @GetMapping("getUid")
    Long getUid();

    @GetMapping("/reset/uid")
    Boolean resetUid();
}

UidTakeFeignServiceFallBack.java

import org.springframework.stereotype.Component;

@Component
public class UidTakeFeignServiceFallBack implements UidTakeService {


    @Override
    public Long getUid() {
        return null;
    }

    @Override
    public Boolean resetUid() {
        return null;
    }
}

学新通

2.3 uid 生成业务:
2.3.1 controller:



import com.example.uidtake.worker.service.IWorkerNodeService;
import com.example.uidtake.worker.service.TakeUidService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class TakeUidController {
    @Autowired
    private IWorkerNodeService workerNodeService;
    @Resource
    private TakeUidService takeUidService;

    @GetMapping("getUid")
    public Long getUid() {
        return workerNodeService.genUid();
    }

    /**
     * 重置uid
     * @return
     */
    @GetMapping("/reset/uid")
    Boolean resetUid() {
        return takeUidService.resetUid();
    }
}

学新通

2.3.2 service:




public interface TakeUidService {
    /**
     * 重置uid
     * @return
     */
    Boolean resetUid();
}

impl:


import com.example.uidtake.config.UidConfig;
import com.example.uidtake.worker.service.TakeUidService;
import com.xfvape.uid.UidGenerator;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import lombok.extern.slf4j.Slf4j;
import org.example.api.constant.UidTakeConstant;
import org.example.api.utils.ExceptionFormatUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class TakeUidServiceImpl implements TakeUidService {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private UidConfig uidConfig;
    @Resource
    private UidGenerator uidGenerator;

    // 线程池
    private EventExecutorGroup group = new DefaultEventExecutorGroup(10);
    private Integer redisKeyGlobalOutTimeSecond = 1000;

    private Integer redisKeyOutTimeSecond = 500;


    /**
     * 重置uid
     *
     * @return
     */
    @Override
    public Boolean resetUid() {
        //创建Callable
        Future<StringBuffer> f = group.submit(new Callable<StringBuffer>() {
            @Override
            public StringBuffer call() throws Exception {
                log.debug("reset uid操作开始...");
                //  init
                return resetUidList();
            }
        });
        // 添加监听者
        f.addListener(new FutureListener<StringBuffer>() {
            @Override
            public void operationComplete(Future<StringBuffer> objectFuture) throws Exception {
                try {
                    if (null == objectFuture || ObjectUtils.isEmpty(objectFuture.get())) {
                        log.debug("reset uid操作完成...");
                    } else {
                        log.error("reset 操作失败,cause:{}", objectFuture.get().toString());
                        throw new IllegalArgumentException(objectFuture.get().toString());
                    }
                } finally {

                }
            }
        });
//        resetUidList();
        return true;
    }

    /**
     * uid 重置
     * 1 如果uid list 的个数多于 临界值(uid list size * uidRadio)则不需要对uid 进行补充;
     * uid size 和 uidRadio 配置于nacos UidConfig 中, 项目启动时通过 UidInit initUid() 完成初始值设置,并将uid list size补充到 配置的uid size大小
     * <p>
     * 2 uid list 的个数小于临界值:
     * 2.1  如果没有补充uid 的线程 被启动,则会开启一个线程补充uid;线程补充uid 标识由 redis key:uidThreadFlag 提供;并增加补充uid threadcout
     * 2.2  如果已经有uid 线程正在补充,则延迟重启启动一个新的线程补充uid
     * 延迟策略:
     * 2.2.1 当前是否已经记录 uid size 低于临界值的 时间second ,如果没有则 redis 记录 初始second1 后返回;
     * 2.2.2 如果已经记录初始second 获取当前的时间second2 ,如果 second2 -second1 > UidConfig second 则启动线程补充uid 并增加threadcout;否则不开启补充uid 线程
     * 2.3 当补充uid 线程完成任务,threadcout --,删除 时间second ;如果threadcout =0 ,则补充uid 的线程已经完成了全部任务,则删除 uidThreadFlag key,删除 threadcout key
     *
     * @return
     */
    private StringBuffer resetUidList() {
        Integer uidSize = Integer.parseInt(redisTemplate.opsForValue().get(UidTakeConstant.redisUidSizeKey).toString());
        Integer uidRadio = Integer.parseInt(redisTemplate.opsForValue().get(UidTakeConstant.redisUidRadioKey).toString());
        // 临界值--初步判断,并发情况下 size 可能不准确,后续补充uid 时 需要加锁
        Integer resetSize = uidSize * uidRadio / 100;
        if (redisTemplate.opsForList().size(UidTakeConstant.redisUidList) >= resetSize.longValue()) {
            log.debug("不需要 reset uid操作");
            return null;
        }
        // 当前的时间 单位秒
        long nowSecond = LocalDateTime.now().atZone(ZoneId.of("Asia/Shanghai")).toInstant().getEpochSecond();
        log.debug("uid size reset start....time:{}", nowSecond);
        StringBuffer stringBuffer = new StringBuffer();
        Exception e = null;
        try {
            startUidTask(stringBuffer);
        } catch (Exception ex) {
            e = ex;
        } finally {
            if (null != e) {
                stringBuffer.append("uid reset task error:").append(ExceptionFormatUtil.buildErrorMessage(e));
            }
        }
        nowSecond = LocalDateTime.now().atZone(ZoneId.of("Asia/Shanghai")).toInstant().getEpochSecond();
        log.debug("uid size reset start....time:{}", nowSecond);
        return stringBuffer;
    }

    /**
     * uid task
     *
     * @param stringBuffer
     */
    private void startUidTask(StringBuffer stringBuffer) {
        // 获取全局锁
        getGlobalLock();
        try {
            if (!redisTemplate.hasKey(UidTakeConstant.redisUidThreadFlagKey)) {
                // 开启新任务
                newUidTask(stringBuffer);
                return;
            }
            if (!redisTemplate.hasKey(UidTakeConstant.redisUidLastSecondKey)) {
                // 记录时间
                recordSecond();
                return;
            }
            long nowSecond = LocalDateTime.now().atZone(ZoneId.of("Asia/Shanghai")).toInstant().getEpochSecond();
            Long lastSecond = Long.parseLong(redisTemplate.opsForValue().get(UidTakeConstant.redisUidLastSecondKey).toString());
            if (nowSecond - lastSecond < uidConfig.getSecond()) {
                // 不到预定时间 不需要重新开启线程
                return;
            }
            Long threadCount = Long.parseLong(redisTemplate.opsForValue().get(UidTakeConstant.redisUidThreadCountKey).toString());
            if (threadCount >= uidConfig.getThreadCount()) {
                // 已经超出补充uid 的线程数
                return;
            }
            newUidTask(stringBuffer);
        } finally {
            releaseGlobalLock();
        }


    }

    /**
     * 记录时间
     */
    private void recordSecond() {
        long nowSecond = LocalDateTime.now().atZone(ZoneId.of("Asia/Shanghai")).toInstant().getEpochSecond();
        redisTemplate.opsForValue().set(UidTakeConstant.redisUidLastSecondKey, nowSecond);
    }

    /**
     * 开启线程执行任务
     */
    private void newUidTask(StringBuffer stringBuffer) {
        // 设置线程数量和线程标识
        getLockAndSetStartThread();
        try {
            while (redisTemplate.opsForList().size(UidTakeConstant.redisUidList) < uidConfig.getUidSize()) {
                redisTemplate.opsForList().leftPush(UidTakeConstant.redisUidList, uidGenerator.getUID());
            }
        } catch (Exception ex) {
            stringBuffer.append(ExceptionFormatUtil.buildErrorMessage(ex));
        } finally {
            // 释放
            getLockAndSetEndThread();
        }

    }


    /**
     * 开启线程执行任务
     */
    private boolean getLockAndSetStartThread() {
        getLock();
        if (!redisTemplate.hasKey(UidTakeConstant.redisUidThreadFlagKey)) {
            // 设置线程 标识
            redisTemplate.opsForValue().set(UidTakeConstant.redisUidThreadFlagKey, "yes");
        }
        if (!redisTemplate.hasKey(UidTakeConstant.redisUidThreadCountKey)) {
            // 设置初始线程数量
            redisTemplate.opsForValue().set(UidTakeConstant.redisUidThreadCountKey, 1);
        } else {
            // uid 补充线程数量  
            Integer threadCount = Integer.parseInt(redisTemplate.opsForValue().get(UidTakeConstant.redisUidThreadCountKey).toString());
            threadCount  ;
            redisTemplate.opsForValue().set(UidTakeConstant.redisUidThreadCountKey, threadCount);
        }
        releaseLock();
        return true;
    }


    /**
     * 线程任务执行完毕
     */
    private boolean getLockAndSetEndThread() {
        getLock();
        if (redisTemplate.hasKey(UidTakeConstant.redisUidThreadCountKey)) {
            // 线程数量 --
            Integer threadCount = Integer.parseInt(redisTemplate.opsForValue().get(UidTakeConstant.redisUidThreadCountKey).toString());
            threadCount--;
            redisTemplate.opsForValue().set(UidTakeConstant.redisUidThreadCountKey, threadCount);
            if (threadCount <= 0) {
                // 所有线程都完成任务-- 重置线程启动标识,重置线程数量标识
                redisTemplate.delete(UidTakeConstant.redisUidThreadFlagKey);
                redisTemplate.delete(UidTakeConstant.redisUidThreadCountKey);
            }
        }
        // uid 补充完毕,second 时间戳重置
        redisTemplate.delete(UidTakeConstant.redisUidLastSecondKey);
        releaseLock();
        return true;
    }

    private void getGlobalLock() {
        boolean flag = redisTemplate.opsForValue().setIfAbsent(UidTakeConstant.redisUidListResetGlobalKey, "1", redisKeyGlobalOutTimeSecond, TimeUnit.MILLISECONDS);
        if (!flag) {
            log.debug("reset uid操作未获取到global锁...");
            for (; ; ) {
                if (redisTemplate.opsForValue().setIfAbsent(UidTakeConstant.redisUidListResetGlobalKey, "1", redisKeyGlobalOutTimeSecond, TimeUnit.MILLISECONDS)) {
                    break;
                }
            }
        }
    }

    private void releaseGlobalLock() {
        redisTemplate.delete(UidTakeConstant.redisUidListResetGlobalKey);
    }

    private void getLock() {
        boolean flag = redisTemplate.opsForValue().setIfAbsent(UidTakeConstant.redisUidListResetKey, "1", redisKeyOutTimeSecond, TimeUnit.MILLISECONDS);
        if (!flag) {
            log.debug("reset uid操作未获取到thread count 锁...");
            for (; ; ) {
                if (redisTemplate.opsForValue().setIfAbsent(UidTakeConstant.redisUidListResetKey, "1", redisKeyOutTimeSecond, TimeUnit.MILLISECONDS)) {
                    break;
                }
            }
        }
    }

    private void releaseLock() {
        redisTemplate.delete(UidTakeConstant.redisUidListResetKey);
    }
}

学新通

2.3.3 其它工具类:
UidTakeConstant.java


import org.springframework.context.ConfigurableApplicationContext;

public class UidTakeConstant {
    public  static  final String uidSizeKey = "uid.size";
    public  static  final String uidRadioKey = "uid.radio";

    /**
     * uid list size
     */
    public  static  final String redisUidSizeKey = "uidSize";
    /**
     * uid 的补充临界值
     */
    public  static  final String redisUidRadioKey = "uidRadio";
    /**
     *  uid 的补充 线程开启标识
     */
    public  static  final String redisUidThreadFlagKey = "uidThreadFlag";
    /**
     * uid 的补充 线程数量
     */
    public  static  final String redisUidThreadCountKey = "uidThreadCount";
    /**
     * uid 的补充 线程延时时间
     */
    public  static  final String redisUidLastSecondKey = "uidLastSecond";

    public  static  final String redisUidListKey = "uidListKey";
    public  static  final String redisUidList = "uidList";

    public  static  final String redisUidListResetKey = "uidListResetKey";
    public  static  final String redisUidListResetGlobalKey = "uidListResetGlobalKey";

    public static ConfigurableApplicationContext applicationContext;
}

学新通

异常信息打印:
ExceptionFormatUtil.java


/**
 * @author 罗祠云
 * @title: 构造异常信息
 * @projectName TRIP
 * @description: TODO
 * @date 2020/4/10 15:47
 */
public class ExceptionFormatUtil {
    //打印异常堆栈信息
    public static String getStackTraceString(Throwable ex){//(Exception ex) {
        StackTraceElement[] traceElements = ex.getStackTrace();

        StringBuilder traceBuilder = new StringBuilder();

        if (traceElements != null && traceElements.length > 0) {
            for (StackTraceElement traceElement : traceElements) {
                traceBuilder.append(traceElement.toString());
                traceBuilder.append("\n");
            }
        }

        return traceBuilder.toString();
    }

    //构造异常堆栈信息
    public static String buildErrorMessage(Exception ex) {

        String result;
        String stackTrace = getStackTraceString(ex);
        String exceptionType = ex.toString();
        String exceptionMessage = ex.getMessage();

        result = String.format("%s : %s \r\n %s", exceptionType, exceptionMessage, stackTrace);

        return result;
    }
}

学新通

3 git 代码参考:

https://codeup.aliyun.com/61cd21816112fe9819da8d9c/百度-uid.git

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhfibega
系列文章
更多 icon
同类精品
更多 icon
继续加载