本文在分布式自增序列的实现(一) ---分布式序号生成器基础上成文,因此直接上解决办法,省去问题的讨论。请先阅读分布式自增序列的实现(一) ---分布式序号生成器。
上一篇我们提到使用zookeeper的持久化序列node来自动生成分布式序列id,本文将讨论使用redis的INCR功能实现分布式自增序列的实现。redis是单线程的,它能保证生成的序列是不重复的。
Redis incr功能介绍
官方文档https://redis.io/commands/incr
Increments the number stored at key by one. If the key does not exist, it is set to 0 before performing the operation. An error is returned if the key contains a value of the wrong type or contains a string that can not be represented as integer. This operation is limited to 64 bit signed integers.
Note: this is a string operation because Redis does not have a dedicated integer type. The string stored at the key is interpreted as a base-10 64 bit signed integer to execute the operation.
Redis stores integers in their integer representation, so for string values that actually hold an integer, there is no overhead for storing the string representation of the integer.
The counter pattern is the most obvious thing you can do with Redis atomic increment operations.
可以看到最大的序列是64bit的有符号的整型, 最大值是2的63次方-1, 也就是9,223,372,036,854,775,807,基本上绝大部分项目够用了。从0开始。特别是文档中的这句,"incr可以用作计数器模式,它是原子自增操作。"
具体代码实现
我直接使用的spring-data-redis,程序基于springboot。完整代码在这里,欢迎加星,fork。 本程序启动时会自动连接redis, 请先配置好自己的redis服务器ip和端口等信息.
本示例代码使用了RedisAtomicLong ,请参考官方文档https://docs.spring.io/spring-data/redis/docs/current/api/org/springframework/data/redis/support/atomic/RedisAtomicLong.html 我们主要使用addAndGet方法。
服务层代码
package com.yq.service.impl;
import com.yq.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* Simple to Introduction
* className: RedisServiceImpl
*
* @author EricYang
* @version 2018/8/4 23:00
*/
@Service
@Slf4j
public class RedisServiceImpl implements RedisService {
@Autowired
private StringRedisTemplate template;
@Autowired
RedisAtomicLong redisAtomicLong;
private static final String LONG_KEY = "yqLong";
@Bean
public RedisAtomicLong getRedisAtomicLong() {
RedisAtomicLong counter = new RedisAtomicLong(LONG_KEY, template.getConnectionFactory());
return counter;
}
@Override
public String get(String key) {
ValueOperations ops = this.template.opsForValue();
return ops.get(key);
}
@Override
public void set(String key, String value) {
ValueOperations ops = this.template.opsForValue();
ops.set(key, value);
}
@Override
public String getHash(String key, String hashKey) {
HashOperations hashOps = this.template.opsForHash();
return hashOps.get(key, hashKey);
}
@Override
public void setHash(String key, String hashKey, String value) {
HashOperations hashOps = this.template.opsForHash();
hashOps.put(key, hashKey, value);
}
@Override
public long getRedisSequence() {
long sequence = 0L;
try {
if (redisAtomicLong.get() == 0) {
redisAtomicLong.getAndSet(0L);
}
sequence = redisAtomicLong.incrementAndGet();
} catch (Exception ex) {
log.error("Failed to get sequence.", ex);
}
return sequence;
}
}
controller层代码, 供其他服务调用
@RestController
@RequestMapping("/cache")
public class RedisController {
private Logger logger = LoggerFactory.getLogger(RedisController.class);
@Autowired
RedisService redisService;
@ApiOperation(value = "获取sequence")
@GetMapping(value = "/sequence", produces = "application/json;charset=UTF-8")
public long getSequence() {
long value = redisService.getRedisSequence();
return value;
}
。。。