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排行榜的资料请关注代码网其它相关文章!
发表评论