分布式全局唯一id实现-2.1 springCloud-MyBatis-Plus集成百度分布式全局iduid-generator--优化版
实现方式思考:
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
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
excel下划线不显示怎么办
PHP中文网 06-23 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
photoshop蒙版画笔没反应怎么办
PHP中文网 06-24