springboot+redission实现排行榜功能
一、业务需求
实现一个排行榜,要求按照分数和达成这个分数的时间排序,即相同分数下,时间早的在上面
二、redis中的zset(有序集合)
1.简介
redis 的 zset(也称为有序集合)是一种特殊的数据结构,它同时包含了集合和有序列表的特性。在 zset 中,每个成员都有一个分数(score)与之关联,这个分数可以是浮点数,用于对集合中的元素进行排序。
2.特点
- 元素唯一:就像集合一样,zset 中不允许有重复成员。
- 有序性:集合中的元素按照其关联的分数值进行升序排序。
- 操作丰富:支持添加、删除成员,获取指定范围的成员,根据分数查询成员,计算交集、并集、差集等操作。
3.常用命令
- zadd:向有序集合中添加一个或多个成员,或者更新已存在成员的分数。
- zrange:返回有序集合中指定区间内的成员,通过索引位置来获取,从0开始。
- zrangebyscore:返回有序集合中指定分数区间的成员。
- zcard:获取有序集合的成员数量。
- zrem:移除有序集合中的一个或多个成员。
- zrevrange:类似于 zrange,但返回的是从高分到低分的成员。
- zincrby:为有序集合中的成员的分数加上给定值。
- zcount:计算有序集合中指定分数区间的成员数量。
- zrank/zrevrank:获取成员在有序集合中的排名,zrank 是从低分到高分,zrevrank 是从高分到低分。
4.测试
> zadd zsetkey 1 member1 1 > zadd zsetkey 1 member2 1 > zadd zsetkey 1 member9 1 > zadd zsetkey 1 member5 1 > zrevrange zsetkey 0 10 withscores member9 1 member5 1 member2 1 member1 1
5.总结
zset可以很好的实现分数排序,但是在相同的分数下,会按照成员的名称进行排序,所以要在此基础上增加时间
三、增加时间数据
为了增加完成时间,我们可以引进一个倒计时的概念,假设一共9秒
用户a在获得1分的时候在第2秒,那可以在redis中存储1*10+(9-2) => 18
用户b在获得1分的时候在第6秒,那可以在redis中存储1*10+(9-6) => 13
这样我们在获取分数的时候,可以倒推出分数和完成时间
四、springboot代码
1.引入redis和redission依赖
<!-- redis -->
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-data-redis</artifactid>
</dependency>
<!-- redisson -->
<dependency>
<groupid>org.redisson</groupid>
<artifactid>redisson-spring-boot-starter</artifactid>
<version>3.20.1</version>
</dependency>
2.application.yml配置
--- # redis配置
spring:
redis:
# 地址
host: localhost
# 端口,默认为6379
port: 6379
# 数据库索引
database: 0
# 密码(如没有密码请注释掉)
# password: # 连接超时时间
timeout: 10s
# 是否开启ssl
ssl: false
--- # redisson配置
redisson:
# redis key前缀
keyprefix: ${spring.application.name}
# 线程池数量
threads: 4
# netty线程池数量
nettythreads: 8
# 单节点配置
singleserverconfig:
# 客户端名称
clientname: ${spring.application.name}
# 最小空闲连接数
connectionminimumidlesize: 8
# 连接池大小
connectionpoolsize: 32
# 连接空闲超时,单位:毫秒
idleconnectiontimeout: 10000
# 命令等待超时,单位:毫秒
timeout: 3000
# 发布和订阅连接池大小
subscriptionconnectionpoolsize: 50
3.java代码
constant
/**
* @author baisu
* @classname rankingconstant
* @description 排行榜常量数据
* @since 2024/5/6
*/
public class rankingconstant {
public static final long basic_quantity = 10000000000000l;
public static final long maximum_time_timit = 29991231235959l;
}
controller
import cn.hutool.core.date.datetime;
import cn.hutool.core.date.dateutil;
import com.ranking.demo.common.r;
import com.ranking.demo.common.constant.rankingconstant;
import com.ranking.demo.demain.rankingvo;
import com.ranking.demo.utils.rankingutil;
import com.ranking.demo.utils.rediskey;
import com.ranking.demo.utils.redisutil;
import org.redisson.client.protocol.scoredentry;
import org.springframework.beans.factory.annotation.value;
import org.springframework.web.bind.annotation.*;
import java.util.arraylist;
import java.util.collection;
import java.util.list;
/**
* @author baisu
* @since 2024/4/28
*/
@restcontroller
public class demorankingcontroller {
@value("${spring.application.name}")
private string applicationname;
/**
* 项目启动测试方法
*
* @return applicationname
*/
@getmapping("")
public string demo() {
return applicationname;
}
/**
* 生成测试数据
*
* @return ok
*/
@getmapping("/generate_test_data")
public r<object> generatetestdata() {
redisutil.addscorebymember(rediskey.getrankingdemokey(), 1l, "10001");
redisutil.addscorebymember(rediskey.getrankingdemokey(), 2l, "10002");
redisutil.addscorebymember(rediskey.getrankingdemokey(), 3l, "10003");
redisutil.addscorebymember(rediskey.getrankingdemokey(), 4l, "10004");
redisutil.addscorebymember(rediskey.getrankingdemokey(), 5l, "10005");
return r.ok();
}
/**
* 获取排行榜数据
*
* @param top 数量
* @return 排行榜数据
*/
@getmapping("/get_ranking")
public r<object> getranking(@requestparam("top") integer top) {
collection<scoredentry<object>> ranking = redisutil.getranking(rediskey.getrankingdemokey(), 0, top - 1);
if (ranking.size() == 0) {
return r.fail("暂无排行榜数据");
}
list<rankingvo> list = new arraylist<>();
for (scoredentry<object> entry : ranking) {
rankingvo vo = new rankingvo();
vo.setmember(entry.getvalue().tostring());
vo.setscore(rankingutil.getscore(entry.getscore()));
vo.settime(rankingutil.gettimestr(entry.getscore()));
list.add(vo);
}
return r.ok(list);
}
/**
* 增加成员分数值
*
* @param member 成员
* @return 是否增加成功
*/
@getmapping("/add_score_by_member")
public r<object> addscorebymember(@requestparam("member") string member) {
double scorebymember = redisutil.getscorebymember(rediskey.getrankingdemokey(), member);
if (scorebymember == null) {
scorebymember = 0.0;
}
redisutil.addscorebymember(rediskey.getrankingdemokey(), rankingutil.getscore(scorebymember) + 1, member);
return r.ok();
}
/**
* 获取成员分数值
*
* @param member 成员
* @return 分数值
*/
@getmapping("/get_score_by_member")
public r<object> getscorebymember(@requestparam("member") string member) {
double scorebymember = redisutil.getscorebymember(rediskey.getrankingdemokey(), member);
if (scorebymember == null) {
return r.fail("该成员不存在");
}
rankingvo vo = new rankingvo();
vo.setmember(member);
vo.setscore(rankingutil.getscore(scorebymember));
vo.settime(rankingutil.gettimestr(scorebymember));
return r.ok(vo);
}
}
domain
import lombok.data;
/**
* @author baisu
* @classname rankingvo
* @description 排行榜展示类
* @since 2024/5/6
*/
@data
public class rankingvo {
/**
* 成员
*/
private string member;
/**
* 分数值
*/
private long score;
/**
* 时间
*/
private string time;
}
/**
* @author baisu
* @classname rediskey
* @description redis索引
* @since 2024/5/6
*/
public class rediskey {
private static final string ranking_demo_key = "ranking_demo";
public static string getrankingdemokey() {
return ranking_demo_key;
}
}
import cn.hutool.core.date.datetime;
import cn.hutool.core.date.dateutil;
import cn.hutool.extra.spring.springutil;
import com.ranking.demo.common.constant.rankingconstant;
import org.redisson.api.rscoredsortedset;
import org.redisson.api.redissonclient;
import org.redisson.client.protocol.scoredentry;
import java.util.collection;
/**
* @author baisu
* @classname redisutil
* @description redis工具类
* @since 2024/5/6
*/
public class redisutil {
private static final redissonclient redisson_client = springutil.getbean(redissonclient.class);
/**
* 向有序集合中添加指定分数的成员
*
* @param key 有序集索引
* @param score 分数
* @param member 成员
* @return 是否成功
*/
public static boolean addscorebymember(string key, long score, string member) {
rscoredsortedset<string> rscoredsortedset = redisson_client.getscoredsortedset(key);
double v = score * rankingconstant.basic_quantity + (rankingconstant.maximum_time_timit - long.parselong(dateutil.format(datetime.now(), rankingutil.format)));
return rscoredsortedset.add(v, member);
}
/**
* 返回有序集中成员的分数值
*
* @param key 有序集索引
* @param member 成员
* @return 分数值(double)
*/
public static double getscorebymember(string key, string member) {
rscoredsortedset<object> scoredsortedset = redisson_client.getscoredsortedset(key);
return scoredsortedset.getscore(member);
}
/**
* 返回有序集中指定位置的成员集合
*
* @param key 有序集索引
* @param start 开始索引
* @param end 结束索引
* @return 成员集合
*/
public static collection<scoredentry<object>> getranking(string key, int start, int end) {
rscoredsortedset<object> rscoredsortedset = redisson_client.getscoredsortedset(key);
return rscoredsortedset.entryrangereversed(start, end);
}
}
import cn.hutool.core.date.dateutil;
import com.ranking.demo.common.constant.rankingconstant;
/**
* @author baisu
* @classname rankingutil
* @description 排行榜工具类
* @since 2024/5/7
*/
public class rankingutil {
public static final string format = "yyyymmddhhmmss";
public static long getscore(double score) {
return math.round(math.floor(score / rankingconstant.basic_quantity));
}
public static string gettimestr(double score) {
return string.valueof(dateutil.parse(string.valueof(rankingconstant.maximum_time_timit - math.round(math.floor(score)) % rankingconstant.basic_quantity)));
}
}
4、接口文档
以上就是springboot+redission实现排行榜功能的示例代码的详细内容,更多关于springboot redission排行榜的资料请关注代码网其它相关文章!
发表评论