就这?分布式 ID 发号器实战 (2)

其原理就是利用 redis 的 incr 命令实现 ID 的原子性自增,众所周知,redis 的性能是非常好的,而且本身就是单线程的,没有线程安全问题。但是使用 redis 做分布式 id 解决方案,需要考虑持久化问题,不然重启 redis 过后可能会导致 id 重复的问题,建议采用 RDB + AOF 的持久化方式。

分析到这里,我觉得 Redis 的方式非常适用于目前的场景,公司系统原本就用到了 redis,而且也正是采用的 RDB + AOF 的持久化方式,这就非常好接入了,只需少量编码就能实现一个发号器功能。

话不多说,直接开始干吧。

img

本案例基于 Spring Boot 2.5.3 版本

首先在 pom 中引入 redis 依赖

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- lettuce客户端连接需要这个依赖 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency>

application.yml 中配置 redis 连接

spring: redis: port: 6379 host: 127.0.0.1 timeout: 5000 lettuce: pool: # 连接池大连接数(使用负值表示没有限制) max-active: 8 # 连接池中的大空闲连接 max-idle: 8 # 连接池中的小空闲连接 min-idle: 0 # 连接池大阻塞等待时间(使用负值表示没有限制) max-wait: 1000 # 关闭超时时间 shutdown-timeout: 100

将 RedisTemplate 注入 Spring 容器中

@Configuration public class RedisConfig{ @Bean @ConditionalOnMissingBean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(connectionFactory); // 使用Jackson2JsonRedisSerializer来序列化/反序列化redis的value值 Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY); objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(objectMapper); // value redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); // 使用StringRedisSerializer来序列化/反序列化redis的key值 RedisSerializer<?> redisSerializer = new StringRedisSerializer(); // key redisTemplate.setKeySerializer(redisSerializer); redisTemplate.setHashKeySerializer(redisSerializer); redisTemplate.afterPropertiesSet(); return redisTemplate; } }

使用 redis 依赖中的 RedisAtomicLong 类来实现 redis 自增序列,从类名就可以看出它是原子性的。

看一下 RedisAtomicLong 的部分源码

// RedisAtomicLong 的部分源码 public class RedisAtomicLong extends Number implements Serializable, BoundKeyOperations<String> { private static final long serialVersionUID = 1L; //redis 中的 key,用 volatile 修饰,获得原子性 private volatile String key; //当前的 key-value 对象,根据传入的 key 获取 value 值 private ValueOperations<String, Long> operations; //传入当前 redisTemplate 对象,为 RedisTemplate 对象的顶级接口 private RedisOperations<String, Long> generalOps; public RedisAtomicLong(String redisCounter, RedisConnectionFactory factory) { this(redisCounter, (RedisConnectionFactory)factory, (Long)null); } private RedisAtomicLong(String redisCounter, RedisConnectionFactory factory, Long initialValue) { Assert.hasText(redisCounter, "a valid counter name is required"); Assert.notNull(factory, "a valid factory is required"); //初始化一个 RedisTemplate 对象 RedisTemplate<String, Long> redisTemplate = new RedisTemplate(); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new GenericToStringSerializer(Long.class)); redisTemplate.setExposeConnection(true); //设置当前的 redis 连接工厂 redisTemplate.setConnectionFactory(factory); redisTemplate.afterPropertiesSet(); //设置传入的 key this.key = redisCounter; //设置当前的 redisTemplate this.generalOps = redisTemplate; //获取当前的 key-value 集合 this.operations = this.generalOps.opsForValue(); //设置默认值,如果传入为 null,则 key 获取 operations 中的 value,如果 value 为空,设置默认值为0 if (initialValue == null) { if (this.operations.get(redisCounter) == null) { this.set(0L); } //不为空则设置为传入的值 } else { this.set(initialValue); } } //将传入 key 的 value + 1并返回 public long incrementAndGet() { return this.operations.increment(this.key, 1L); } }

看完源码,我们继续自己的编码

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zysjyj.html