.Net Redis实战——实现文章投票并排序
本系列文章为学习Redis实战一书记录的随笔。
软件和环境版本:Redis:5.0.7 .Net 5.0
文中不会对Redis基础概念做过多介绍。
Redis数据类型和命令可在菜鸟教程学习:https://www.runoob.com/redis/redis-tutorial.html 。
示例介绍
实现一个简单的文章投票功能,并根据文章投票得分进行排序展示。
功能设计
使用散列来存储文章信息
article:92617中的冒号只是作为分隔符,可以根据个人信号替换成 | , / 等符号
使用两个有序集合来有序地存储文章:第一个有序集合的成员为文章 ID,分值为文章的发布时间;第二个有序集合的成员同样为文章 ID,而分值则为文章的评分。通过这两个有序集合,网站既可以根据文章发布的先后顺序来展示文章,又可以根据文章评分的高低来展示文章。
同时为了防止一个用户对一篇文章进行多次投票,需要为每篇文章增加一个投票用户集合存储已投票用户ID。
代码实现
代码所用Redis库为StackExchange.Redis。
具体使用参考:https://stackexchange.github.io/StackExchange.Redis/
我本机Redis安装在Windows的Linux子系统内,写数据持久化时报错“MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk”。
解决方案:https://blog.csdn.net/u014071875/article/details/103715183
1.初始化文章,模拟文章发布
[HttpGet("InitArticles")] public string InitArticles() { using (var redis = ConnectionMultiplexer.Connect(redisConnectionStr)) { var db = redis.GetDatabase(); foreach (var article in Articles) { var articleId = $"article:{article.ID}"; var hashEntiies = new [] { new HashEntry(nameof(article.Title),article.Title), new HashEntry(nameof(article.Link),article.Link), new HashEntry(nameof(article.Poster),article.Poster), new HashEntry(nameof(article.Votes),article.Votes), new HashEntry(nameof(article.UnixTime),article.UnixTime), }; //存储文章信息 db.HashSetAsync(articleId, hashEntiies); //将文章信息添加到评分和时间排序的有序集合 db.SortedSetAddAsync("score:",articleId, 0); db.SortedSetAddAsync("time:",articleId, DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()); Thread.Sleep(1000); } } return "文章初始化完成"; }
2.文章投票代码
[HttpGet("ArticleVote")] public string ArticleVote(int userId,int articleId) { using (var redis = ConnectionMultiplexer.Connect(redisConnectionStr)) { var db = redis.GetDatabase(); if (db.SetAdd($"voted:{articleId}",$"user:{userId}")) { //增加投票票数和评分 db.HashIncrementAsync($"article:{articleId}", "Votes", 1); db.SortedSetIncrementAsync("score:", $"article:{articleId}", 1); } else { return "不能重复投票"; } } return "投票成功"; }
3.获取文章列表(只列出根据评分排序)
[HttpGet("GetArticles")] public IEnumerable<Article> GetArticles(int page,int size) { var articles = new List<Article>(); using (var redis = ConnectionMultiplexer.Connect(redisConnectionStr)) { var start = (page - 1) * size; var end = start + size - 1; var db = redis.GetDatabase(); var ids = db.SortedSetRangeByRank("score:", start, end,order:Order.Descending); foreach (var id in ids) { var articleHashEntities = db.HashGetAll(id.ToString()); //这里除了用反射没有找到好的解决办法 var article = new Article { ID = int.Parse(id.ToString().Split(':')[1]), Title= articleHashEntities.FirstOrDefault(a=>a.Name=="Title").Value, Link= articleHashEntities.FirstOrDefault(a => a.Name == "Link").Value, Poster= int.Parse(articleHashEntities.FirstOrDefault(a => a.Name == "Poster").Value), UnixTime= long.Parse(articleHashEntities.FirstOrDefault(a => a.Name == "UnixTime").Value), Votes= int.Parse(articleHashEntities.FirstOrDefault(a => a.Name == "Votes").Value) }; articles.Add(article); } } return articles; }