1. 概述
Redis是一个开源的key-value存储系统,支持多种数据类型,常用于做高速缓存和存储多样的持久化数据。本文主要介绍SpringBoot2.3整合Redis实现基本的数据操作,包括5中常用的数据类型和3中新型数据类型。
2. 引入核心依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
3. yml配置信息
spring:
application:
name: springboot-redis
redis:
host: xx.xx.xx.xx
port: 6379
database: 0
timeout: 1800000 #连接超时时间 毫秒
lettuce:
pool:
max-active: 8 #连接池最大连接数 负数表示没有限制
max-wait: -1m #最大阻塞等待时间 负数表示没有限制
max-idle: 5 #连接池最大空闲连接
min-idle: 0 #连接池最小空闲连接
4. 配置类
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(mapper);
template.setConnectionFactory(factory);
//key序列化方式
template.setKeySerializer(redisSerializer);
//value序列化
template.setValueSerializer(jackson2JsonRedisSerializer);
//value hashmap序列化
template.setHashValueSerializer(jackson2JsonRedisSerializer);
return template;
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常问题
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(mapper);
//配置序列化(解决乱码问题),过期时间600秒
RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(600))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(factory).cacheDefaults(configuration).build();
return cacheManager;
}
}
5. 接口封装
public interface RedisService {
/**
* 查看所有key
* @param pattern key *
* @return
*/
Set<String> keys(String pattern);
/**
* 查询key是否存在
* @param key
* @return
*/
Boolean existsKey(String key);
/**
* 查看key类型
* @param key
* @return
*/
DataType type(String key);
/**
* 删除key
* @param key
* @return
*/
Boolean delete(String key);
/**
* 根据value选择非阻塞删除
* @param key
* @return
*/
Boolean unlink(String key);
/**
* 给指定key设置过期时间
* @param key
* @param times
* @param timeUnit
* @return
*/
Boolean expireKey(String key, long times, TimeUnit timeUnit);
/**
* 查看还是多少秒过期
* @param key
* @return -1表示不过期,-2表示已过期
*/
Long ttl(String key);
/**
* 设置key-value
* @param key
* @param value
*/
void set(String key, String value);
/**
* 设置带过期时间的key-value
* @param key
* @param value
* @param times
*/
void set(String key, String value, long times);
/**
* 获取key的值
* @param key
* @return
*/
String get(String key);
/**
* 将给定的value追加到原值末尾
* @param key
* @param value
* @return
*/
Integer append(String key, String value);
/**
* 获取值的长度
* @param key
* @return
*/
Long strlen(String key);
/**
* 当key不存在时,设置key的值
* @param key
* @param value
* @return
*/
Boolean setnx(String key, String value);
/**
* 将key中的数字增1
* @param key
* @return
*/
Long incr(String key);
/**
* 将key中数字减1
* @param key
* @return
*/
Long decr(String key);
/**
* 将key中数字增自定义步长
* @param key
* @param step
* @return
*/
Long incrby(String key, long step);
/**
* 将key中数字减自定义步长
* @param key
* @param step
* @return
*/
Long decrby(String key, long step);
/**
* 同时设置一个或多个值
* @param map
*/
void mset(Map<String, String> map);
/**
* 同时获取一个或多个值
* @param keys
* @return
*/
List<String> mget(List<String> keys);
/**
* 同时设置一个或多个值,仅当key都不存在
* @param map
* @return
*/
Boolean msetnx(Map<String, String> map);
/**
* 获取值的范围,前后都包括
* @param key
* @param start
* @param stop
* @return
*/
String getrange(String key, long start, long stop);
/**
* 设置键值的同时,设置过期时间
* @param key
* @param expire
* @param value
*/
void setex(String key, int expire, String value);
/**
* 设置新值同时获取旧值
* @param key
* @param value
* @return
*/
String getset(String key, String value);
/**
* 获取分布式锁
* @return
*/
Long getDistributedLock();
/**
* 简单限流 period秒最大执行maxCount次
* @param key
* @param period
* @param maxCount
* @return
*/
Boolean SimpleRateLimiter(String key, long period, int maxCount);
/**
* 漏洞限流
* @param key
* @param capacity
* @param leakingRate
* @return
*/
Boolean FunnelRateLimiter(String key, int capacity, float leakingRate);
/**
* 向list中左边插入一个值
* @param key
* @param value
* @return
*/
Long lpush(String key, String value);
/**
* 向list中左边插入多个值
* @param key
* @param value
* @return
*/
Long lpush(String key, String... value);
/**
* 向list右边插入一个值
* @param key
* @param value
* @return
*/
Long rpush(String key, String value);
/**
* 向list右边插入多个值
* @param key
* @param value
* @return
*/
Long rpush(String key, String... value);
/**
* 从list左边取出一个值
* @param key
* @return
*/
String lpop(String key);
/**
* 从list右边取出一个值
* @param key
* @return
*/
String rpop(String key);
/**
* 从list key1右边取出一个值,插到key2左边
* @param key1
* @param key2
* @return
*/
String rpoplpush(String key1, String key2);
/**
* 按照索引下标获取元素 当start=0/stop=-1时表示获取所有
* @param key
* @param start
* @param stop
* @return
*/
List<String> lrange(String key, int start, int stop);
/**
* 按照索引下标获取元素
* @param key
* @param index
* @return
*/
String lindex(String key, int index);
/**
* 获取key长度
* @param key
* @return
*/
Long llen(String key);
/**
* 在oldValue后面插入newValue
* @param key
* @param oldValue
* @param newValue
* @return
*/
Long linsert(String key, String oldValue, String newValue);
/**
* 从左边删除n个value
* @param key
* @param num
* @param value
* @return
*/
Long lrem(String key, int num, String value);
/**
* 将key下标为index的值替换成value
* @param key
* @param index
* @param value
*/
void lset(String key, int index, String value);
/**
* 将一个或多个member元素加入set集合,已经存在的member元素将被忽略
* @param key
* @param value
* @return
*/
Long sadd(String key, String... value);
/**
* 取出set集合中所有值
* @param key
* @return
*/
Set<String> smembers(String key);
/**
* 判断set集合中是否含有value值
* @param key
* @param value
* @return
*/
Boolean sismember(String key, String value);
/**
* 返回set集合元素个数
* @param key
* @return
*/
Long scard(String key);
/**
* 删除集合中某个元素
* @param key
* @param value
* @return
*/
Long srem(String key, String... value);
/**
* 随机从set集合取出一个值
* @param key
* @return
*/
String spop(String key);
/**
* 随机从set集合取出n个值,不会删除
* @param key
* @param num
* @return
*/
List<String> srandmember(String key, int num);
/**
* 把set集合中一个值从一个集合移动到另一个集合
* @param sourceKey
* @param value
* @param destKey
* @return
*/
Boolean smove(String sourceKey, String value, String destKey);
/**
* 返回两个集合的交集
* @param key1
* @param key2
* @return
*/
Set<String> sinter(String key1, String key2);
/**
* 返回两个集合的并集
* @param key1
* @param key2
* @return
*/
Set<String> sunion(String key1, String key2);
/**
* 返回两个集合的差集
* @param key1
* @param key2
* @return
*/
Set<String> sdiff(String key1, String key2);
/**
* 给hash集合中field赋值value
* @param key
* @param field
* @param value
*/
void hset(String key, String field, String value);
/**
* 获取hash集合field的值
* @param key
* @param field
* @return
*/
Object hget(String key, String field);
/**
* 批量设置hash的值
* @param key
* @param map
*/
void hmset(String key, Map<String, Object> map);
/**
* 查看hash表key中,给定field是否存在
* @param key
* @param field
* @return
*/
Boolean hexists(String key, String field);
/**
* 列出该hash集合所有field
* @param key
* @return
*/
Set<Object> hkeys(String key);
/**
* 列出hash集合中所有value
* @param key
* @return
*/
List<Object> hvals(String key);
/**
* 为hash key中field的值加上增量1 或 -1
* @param key
* @param field
* @param increment
* @return
*/
Long hincrby(String key, String field, int increment);
/**
* 将hash表key中的field的值设为value,当且仅当field不存在
* @param key
* @param field
* @param value
* @return
*/
Boolean hsetnx(String key, String field, String value);
/**
* 将一个或多个member元素及其score值加入到zset中
* @param key
* @param value
* @param score
* @return
*/
Boolean zadd(String key, String value, double score);
/**
* 返回下标在start和stop之间的元素
* @param key
* @param start
* @param stop
* @return
*/
Set<String> zrange(String key, long start, long stop);
/**
* 返回下标在start和stop之间的元素包括分数
* @param key
* @param start
* @param stop
* @return
*/
Set<ZSetOperations.TypedTuple<String>> zrangewithscore(String key, long start, long stop);
/**
* 返回所有score值介于min和max之间的元素,并按score递增排序
* @param key
* @param min
* @param max
* @return
*/
Set<String> zrangebyscore(String key, double min, double max);
/**
* 返回所有score值介于min和max之间的元素,并按score递减排序
* @param key
* @param min
* @param max
* @return
*/
Set<String> zrevrangebyscore(String key, double min, double max);
/**
* 为zset的score加增量
* @param key
* @param value
* @param increment
* @return
*/
Double zincrby(String key, String value, int increment);
/**
* 删除zset下指定元素
* @param key
* @param value
* @return
*/
Long zrem(String key, String value);
/**
* 统计zset分数区间内元素个数
* @param key
* @param min
* @param max
* @return
*/
Long zcount(String key, double min, double max);
/**
* 返回在zset中排名,从0开始
* @param key
* @param value
* @return
*/
Long zrank(String key, String value);
/********************Bitmaps命令操作********************/
/**
* 设置bitmaps中偏移量offset的值
* @param key
* @param offset 偏移量
* @param value 值 1:true;0:false
* @return
*/
boolean setbit(String key, long offset, boolean value);
/**
* 获取bitmaps中偏移量的值
* @param key
* @param offset 从0开始
* @return
*/
Boolean getbit(String key, long offset);
/**
* 统计key从start字节到end字节比特值为1的数量
* @param key
* @param start
* @param end
* @return
*/
Long bigcount(String key, long start, long end);
/**
* 获取多个bitmaps的交集/并集/非/异或并保存结果到destkey中
* @param operation 操作符 AND, OR, XOR, NOT
* @param destKey 结果key
* @param keys
*/
void bitop(RedisStringCommands.BitOperation operation, String destKey, String... keys);
/********************HyperLogLog命令操作********************/
/**
* 添加元素到hyperloglog
* @param key
* @param value
* @return
*/
Long pfadd(String key, String... value);
/**
* 统计多个值
* @param keys
* @return
*/
Long pfcount(String... keys);
/**
* 将一个或多个hll合并后保存到destkey
* @param destKey
* @param sourceKeys
* @return
*/
Long pfmerge(String destKey, String... sourceKeys);
/********************Geospatial命令操作********************/
/**
* 添加地理位置
* @param key
* @param longitude 经度
* @param latitude 纬度
* @param member 名称
* @return
*/
Long geoadd(String key, double longitude, double latitude, String member);
/**
* 获取指定地区坐标值
* @param key
* @param members
* @return
*/
List<Point> geopos(String key, String... members);
/**
* 获取两个位置的直线距离
* @param key
* @param member1
* @param member2
* @return
*/
Distance geodist(String key, String member1, String member2);
/**
* 给定经纬度为中心,找出某半径内的值
* @param key
* @param longitude
* @param latitude
* @param radius
*/
void georadius(String key, double longitude, double latitude, double radius);
/**
* 给定地理位置为中心,找出某一半径内的值
* @param key
* @param member
* @param radius
*/
void georadius(String key, String member, double radius);
}
6. 接口实现
@Service
public class RedisServiceImpl implements RedisService {
@Autowired
private StringRedisTemplate redisTemplate;
@Override
public void set(String key, String value) {
redisTemplate.opsForValue().set(key, value);
}
@Override
public void set(String key, String value, long times) {
redisTemplate.opsForValue().set(key, value, times);
}
@Override
public String get(String key) {
String value = redisTemplate.opsForValue().get(key);
return value;
}
@Override
public Integer append(String key, String value) {
return redisTemplate.opsForValue().append(key, value);
}
@Override
public Long strlen(String key) {
return redisTemplate.opsForValue().size(key);
}
@Override
public Boolean setnx(String key, String value) {
return redisTemplate.opsForValue().setIfAbsent(key, value);
//return redisTemplate.opsForValue().setIfAbsent(key, value, 3, TimeUnit.SECONDS);
}
@Override
public Long incr(String key) {
return redisTemplate.opsForValue().increment(key);
}
@Override
public Long decr(String key) {
return redisTemplate.opsForValue().decrement(key);
}
@Override
public Long incrby(String key, long step) {
return redisTemplate.opsForValue().increment(key, step);
}
@Override
public Long decrby(String key, long step) {
return redisTemplate.opsForValue().decrement(key, step);
}
@Override
public void mset(Map<String, String> map) {
redisTemplate.opsForValue().multiSet(map);
}
@Override
public List<String> mget(List<String> keys) {
return redisTemplate.opsForValue().multiGet(keys);
}
@Override
public Boolean msetnx(Map<String, String> map) {
return redisTemplate.opsForValue().multiSetIfAbsent(map);
}
@Override
public String getrange(String key, long start, long stop) {
return redisTemplate.opsForValue().get(key, start, stop);
}
@Override
public void setex(String key, int expire, String value) {
redisTemplate.opsForValue().set(key, value, expire, TimeUnit.SECONDS);
}
@Override
public String getset(String key, String value) {
return redisTemplate.opsForValue().getAndSet(key, value);
}
@Override
public Boolean expireKey(String key, long times, TimeUnit timeUnit) {
Boolean expire = redisTemplate.expire(key, times, timeUnit);
return expire;
}
@Override
public Long ttl(String key) {
return redisTemplate.getExpire(key);
//return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
@Override
public Set<String> keys(String pattern) {
return redisTemplate.keys(pattern);
}
@Override
public Boolean existsKey(String key) {
Boolean hasKey = redisTemplate.hasKey(key);
return hasKey;
}
@Override
public DataType type(String key) {
return redisTemplate.type(key);
}
@Override
public Boolean delete(String key) {
Boolean delete = redisTemplate.delete(key);
return delete;
}
@Override
public Boolean unlink(String key) {
return redisTemplate.unlink(key);
}
@Override
public Long getDistributedLock() {
String uuid = UUID.randomUUID().toString();
String lockKey = "lock:100";
Boolean lock = redisTemplate.opsForValue().setIfAbsent(lockKey, uuid, 3, TimeUnit.SECONDS);
Long execute = -1L;
if (lock) {
//执行业务逻辑
//todo
//定义lua脚本
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.cal('del', KEYS[1]) else return 0 end";
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(script);
redisScript.setResultType(Long.class);
execute = redisTemplate.execute(redisScript, Arrays.asList(lockKey), uuid);
} else {
try {
Thread.sleep(1000);
getDistributedLock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return execute;
}
@Override
public Boolean SimpleRateLimiter(String key, long period, int maxCount) {
key = "hist:" + key;
long milli = Instant.now().toEpochMilli();
redisTemplate.multi();
redisTemplate.opsForZSet().add(key, String.valueOf(milli), Double.valueOf(milli));
redisTemplate.opsForZSet().removeRangeByScore(key, 0, milli - period * 1000);
Long count = redisTemplate.opsForZSet().zCard(key);
redisTemplate.expire(key, period, TimeUnit.SECONDS);
redisTemplate.exec();
return count <= maxCount;
}
@Override
public Boolean FunnelRateLimiter(String key, int capacity, float leakingRate) {
Map<String, Funnel> funnels = new HashMap<>();
Funnel funnel = funnels.get(key);
if (funnel == null) {
funnel = new Funnel(capacity, leakingRate);
funnels.put(key, funnel);
}
return funnel.watering(1);
}
@Override
public Long lpush(String key, String value) {
Long push = redisTemplate.opsForList().leftPush(key, value);
return push;
}
@Override
public Long lpush(String key, String... value) {
Long pushAll = redisTemplate.opsForList().leftPushAll(key, value);
return pushAll;
}
@Override
public Long rpush(String key, String value) {
return redisTemplate.opsForList().rightPush(key, value);
}
@Override
public Long rpush(String key, String... value) {
return redisTemplate.opsForList().rightPushAll(key, value);
}
@Override
public String lpop(String key) {
return redisTemplate.opsForList().leftPop(key);
}
@Override
public String rpop(String key) {
return redisTemplate.opsForList().rightPop(key);
}
@Override
public String rpoplpush(String key1, String key2) {
return redisTemplate.opsForList().rightPopAndLeftPush(key1, key2);
}
@Override
public List<String> lrange(String key, int start, int stop) {
return redisTemplate.opsForList().range(key, start, stop);
}
@Override
public String lindex(String key, int index) {
return redisTemplate.opsForList().index(key, index);
}
@Override
public Long llen(String key) {
return redisTemplate.opsForList().size(key);
}
@Override
public Long linsert(String key, String oldValue, String newValue) {
return redisTemplate.opsForList().leftPush(key, oldValue, newValue);
}
@Override
public Long lrem(String key, int num, String value) {
return redisTemplate.opsForList().remove(key, num, value);
}
@Override
public void lset(String key, int index, String value) {
redisTemplate.opsForList().set(key, index, value);
}
@Override
public Long sadd(String key, String... value) {
return redisTemplate.opsForSet().add(key, value);
}
@Override
public Set<String> smembers(String key) {
return redisTemplate.opsForSet().members(key);
}
@Override
public Boolean sismember(String key, String value) {
return redisTemplate.opsForSet().isMember(key, value);
}
@Override
public Long scard(String key) {
return redisTemplate.opsForSet().size(key);
}
@Override
public Long srem(String key, String... value) {
return redisTemplate.opsForSet().remove(key, value);
}
@Override
public String spop(String key) {
return redisTemplate.opsForSet().pop(key);
}
@Override
public List<String> srandmember(String key, int num) {
return redisTemplate.opsForSet().randomMembers(key, num);
}
@Override
public Boolean smove(String sourceKey, String value, String destKey) {
return redisTemplate.opsForSet().move(sourceKey, value, destKey);
}
@Override
public Set<String> sinter(String key1, String key2) {
return redisTemplate.opsForSet().intersect(key1, key2);
}
@Override
public Set<String> sunion(String key1, String key2) {
return redisTemplate.opsForSet().union(key1, key2);
}
@Override
public Set<String> sdiff(String key1, String key2) {
return redisTemplate.opsForSet().difference(key1, key2);
}
@Override
public void hset(String key, String field, String value) {
redisTemplate.opsForHash().put(key, field, value);
}
@Override
public Object hget(String key, String field) {
return redisTemplate.opsForHash().get(key, field);
}
@Override
public void hmset(String key, Map<String, Object> map) {
redisTemplate.opsForHash().putAll(key, map);
}
@Override
public Boolean hexists(String key, String field) {
return redisTemplate.opsForHash().hasKey(key, field);
}
@Override
public Set<Object> hkeys(String key) {
return redisTemplate.opsForHash().keys(key);
}
@Override
public List<Object> hvals(String key) {
return redisTemplate.opsForHash().values(key);
}
@Override
public Long hincrby(String key, String field, int increment) {
return redisTemplate.opsForHash().increment(key, field, increment);
}
@Override
public Boolean hsetnx(String key, String field, String value) {
return redisTemplate.opsForHash().putIfAbsent(key, field, value);
}
@Override
public Boolean zadd(String key, String value, double score) {
return redisTemplate.opsForZSet().add(key, value, score);
}
@Override
public Set<String> zrange(String key, long start, long stop) {
return redisTemplate.opsForZSet().range(key, start, stop);
}
@Override
public Set<ZSetOperations.TypedTuple<String>> zrangewithscore(String key, long start, long stop) {
return redisTemplate.opsForZSet().rangeWithScores(key, start, stop);
}
@Override
public Set<String> zrangebyscore(String key, double min, double max) {
return redisTemplate.opsForZSet().rangeByScore(key, min, max);
//return redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max);
}
@Override
public Set<String> zrevrangebyscore(String key, double min, double max) {
return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max);
//return redisTemplate.opsForZSet().reverseRangeByScoreWithScores(key, min, max);
}
@Override
public Double zincrby(String key, String value, int increment) {
return redisTemplate.opsForZSet().incrementScore(key, value, increment);
}
@Override
public Long zrem(String key, String value) {
return redisTemplate.opsForZSet().remove(key, value);
}
@Override
public Long zcount(String key, double min, double max) {
return redisTemplate.opsForZSet().count(key, min, max);
}
@Override
public Long zrank(String key, String value) {
return redisTemplate.opsForZSet().rank(key, value);
}
@Override
public boolean setbit(String key, long offset, boolean value) {
return redisTemplate.opsForValue().setBit(key, offset, value).booleanValue();
}
@Override
public Boolean getbit(String key, long offset) {
return redisTemplate.opsForValue().getBit(key, offset);
}
@Override
public Long bigcount(String key, long start, long end) {
return redisTemplate.execute((RedisCallback<Long>) count -> count.bitCount(key.getBytes(), start, end));
//return redisTemplate.execute((RedisCallback<Long>) count -> count.bitCount(key.getBytes()));
}
@Override
public void bitop(RedisStringCommands.BitOperation operation, String destKey, String... keys) {
redisTemplate.execute((RedisCallback<Long>) top -> top.bitOp(operation, destKey.getBytes(), Arrays.stream(keys).map(key -> key.getBytes()).collect(Collectors.toList()).toArray(new byte[keys.length][])));
}
@Override
public Long pfadd(String key, String... value) {
return redisTemplate.opsForHyperLogLog().add(key, value);
}
@Override
public Long pfcount(String... keys) {
return redisTemplate.opsForHyperLogLog().size(keys);
}
@Override
public Long pfmerge(String destKey, String... sourceKeys) {
return redisTemplate.opsForHyperLogLog().union(destKey, sourceKeys);
}
@Override
public Long geoadd(String key, double longitude, double latitude, String member) {
Point point = new Point(longitude, longitude);
return redisTemplate.opsForGeo().add(key, point, member);
}
@Override
public List<Point> geopos(String key, String... members) {
return redisTemplate.opsForGeo().position(key, members);
}
@Override
public Distance geodist(String key, String member1, String member2) {
return redisTemplate.opsForGeo().distance(key, member1, member2);
//return redisTemplate.opsForGeo().distance(key, member1, member2, Metrics.KILOMETERS);
}
@Override
public void georadius(String key, double longitude, double latitude, double radius) {
Point point = new Point(longitude, latitude);
Circle circle = new Circle(point, radius);
redisTemplate.opsForGeo().radius(key, circle);
RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().limit(2).sortAscending();
redisTemplate.opsForGeo().radius(key, circle, args);
}
@Override
public void georadius(String key, String member, double radius) {
redisTemplate.opsForGeo().radius(key, member, radius);
Distance distance = new Distance(radius);
redisTemplate.opsForGeo().radius(key, member, distance);
RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().limit(2).sortAscending();
redisTemplate.opsForGeo().radius(key, member, distance, args);
}
private class Funnel {
int capacity;
float leakingRate;
int leftQuota;
long leakingTs;
public Funnel(int capacity, float leakingRate) {
this.capacity = capacity;
this.leakingRate = leakingRate;
this.leftQuota = capacity;
this.leakingTs = Instant.now().toEpochMilli();
}
void makeSpace() {
long nowTs = Instant.now().toEpochMilli();
long deltaTs = nowTs - leakingTs;
int deltaQuota = (int) (deltaTs * leakingRate);
if (deltaQuota < 0) {
this.leftQuota = capacity;
this.leakingTs = nowTs;
return;
}
if (deltaQuota < 1) {
return;
}
this.leftQuota += deltaQuota;
this.leakingTs = nowTs;
if (this.leftQuota > this.capacity) {
this.leftQuota = this.capacity;
}
}
boolean watering(int quota) {
makeSpace();
if (this.leftQuota >= quota) {
this.leftQuota -= quota;
return true;
}
return false;
}
}
}