從零開始搭建博客03----本周熱議處理(redis 有序列表處理)
本周熱議,本周發(fā)表并且評(píng)論最多的文章排行,如果直接查詢數(shù)據(jù)庫(kù)的話很快就可以實(shí)現(xiàn),只需要限定一下文章創(chuàng)建時(shí)間,然后根據(jù)評(píng)論數(shù)量倒敘取前幾篇即可搞定。
但這里我們使用redis來(lái)完成。之前上課時(shí)候我們說(shuō)過,排行榜功能,我們可以使用redis的有序集合zset來(lái)完成。現(xiàn)在我們就這個(gè)數(shù)據(jù)結(jié)構(gòu)來(lái)完成本周熱議的功能。
在編碼之前,我們需要先來(lái)回顧一下zset的幾個(gè)基本命令。
zrange key start stop [WITHSCORES]
withscores代表的是否顯示順序號(hào) start和stop代表所在的位置的索引??梢赃@樣理解:將集合元素依照順序值升序排序再輸出,start和stop限制遍歷的限制范圍
zincrby key increment member
為有序集 key 的成員 member 的 score 值加上增量 increment 。
ZUNIONSTORE destination numkeys key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX]
計(jì)算給定的一個(gè)或多個(gè)有序集的并集,其中給定 key 的數(shù)量必須以 numkeys 參數(shù)指定,并將該并集(結(jié)果集)儲(chǔ)存到 destination 。
默認(rèn)情況下,結(jié)果集中某個(gè)成員的 score 值是所有給定集下該成員 score 值之 和
實(shí)現(xiàn)步驟
查庫(kù)獲取最近7天的所有評(píng)論數(shù)量大于 0文章
把文章的評(píng)論數(shù)量作為有序集合的分?jǐn)?shù),文章id作為id存儲(chǔ)到zset中
緩存文章到set中,評(píng)論數(shù)量作為排行標(biāo)準(zhǔn)
設(shè)置有效期為7天,因?yàn)槌^了7天也就失去了時(shí)效性
具體代碼實(shí)現(xiàn)
com.fly.service.impl.PostServiceImpl#initIndexWeekRank
@Override
public void initIndexWeekRank() {
// 查庫(kù)獲取最近7天的所有文章
List last7DayPosts = this.list(new QueryWrapper()
.ge(“created”, DateUtil.offsetDay(new Date(), -7).toJdkDate())
.gt(“comment_count”, 0)
.select(“id, title, user_id, comment_count, view_count, created”));
// 然后把文章的評(píng)論數(shù)量作為有序集合的分?jǐn)?shù),文章id作為ID存儲(chǔ)到zset中。
for (Post post : last7DayPosts) {
String key = “day_rank” + DateUtil.format(post.getCreated(), DatePattern.PURE_DATE_PATTERN);
// 設(shè)置有效期,7天之內(nèi)有效
long between = DateUtil.between(new Date(), post.getCreated(), DateUnit.DAY);
long expireTime = (7 - between) * 24 * 60 * 60;
// 緩存文章到set中,評(píng)論數(shù)量作為排行標(biāo)準(zhǔn)
redisUtil.zSet(key, post.getId(), post.getCommentCount());
//設(shè)置有效期
redisUtil.expire(key, expireTime);
// 緩存文章基本信息(hash結(jié)構(gòu))
this.hashCachePostIdAndTitle(post);
}
// 7天閱讀相加
this.zUnionAndStroreLast7DaysForWeekRand();
}
因?yàn)橐@示文章的標(biāo)題等基本信息
/**
* hash結(jié)構(gòu)緩存文章標(biāo)題和id
*
* @param post
*/
private void hashCachePostIdAndTitle(Post post) {
boolean isExist = redisUtil.hasKey(“rank_post_” + post.getId());
if (!isExist) {
long between = DateUtil.between(new Date(), post.getCreated(), DateUnit.DAY);
long expireTime = (7 - between) * 24 * 60 * 60;
// 緩存文章基本信息
redisUtil.hset(“rank_post_” + post.getId(), “post:id”, post.getId(), expireTime);
redisUtil.hset(“rank_post_” + post.getId(), “post:title”, post.getTitle(), expireTime);
}
}
/**
* 把最近7天的文章評(píng)論數(shù)量統(tǒng)計(jì)一下
* 用于首頁(yè)的7天評(píng)論排行榜
*/
public void zUnionAndStroreLast7DaysForWeekRand() {
String prifix = "day_rank";
List<String> keys = new ArrayList<>();
String key = prifix + DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN);
for (int i = -7; i < 0; i++) {
Date date = DateUtil.offsetDay(new Date(), i).toJdkDate();
keys.add(prifix + DateUtil.format(date, DatePattern.PURE_DATE_PATTERN));
}
redisUtil.zUnionAndStore(key, keys, "last_week_rank");
}
我們?cè)陧?xiàng)目啟動(dòng)之時(shí)初始化,代碼如下:
增加評(píng)論數(shù)量
用戶發(fā)表評(píng)論之后應(yīng)該要數(shù)據(jù)庫(kù)中的comment_count 加1,也要讓緩存中數(shù)量加1,這樣才能保證數(shù)據(jù)的一致性,在com.fly.controller.PostController#commentAdd 中
@Override
public void incrZsetValueAndUnionForLastWeekRank(Long postId) {
String dayRank = "day_rank" + DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN);
// 文章閱讀加一
redisUtil.zIncrementScore(dayRank, postId, 1);
this.hashCachePostIdAndTitle(this.getById(postId));
// 重新union最近7天
this.zUnionAndStroreLast7DaysForWeekRand();
}
其實(shí)邏輯也和初始化差不多,首先給文章數(shù)量加一。集合名稱是對(duì)應(yīng)當(dāng)天的。比如今天是12月21,對(duì)應(yīng)的key就是day_rank:20181221。在這個(gè)上面給對(duì)應(yīng)的文章id加一。這樣每天都有評(píng)論這篇文章的話,我們?cè)僮鼋患幚?,把每天的評(píng)論數(shù)量都加起來(lái)。得到的就是總的評(píng)論數(shù)量了。
前端引用的代碼
common/templates.html
<!--本周熱議-->
<div th:fragment="weekPopular">
<dl class="fly-panel fly-list-one" id="post-hots">
<dt class="fly-panel-title">本周熱議</dt>
<script id="hostDd" type="text/html">
{{# layui.each(d.list, function(index, item){ }}
<dd>
<a href="/post/{{item.id}}">{{item.title}}</a>
<span><i class="iconfont icon-pinglun1"></i>{{item.comment_count}}</span>
</dd>
{{# }); }}
{{# if(d.list.length === 0){ }}
<div class="fly-none">沒有相關(guān)數(shù)據(jù)</div>
{{# } }}
</script>
</dl>
<script>
layui.use(['laytpl'], function () {
var laytpl = layui.laytpl;
var post_hosts = document.getElementById("post-hots");
var tpl = document.getElementById("hostDd").innerHTML;
var data = {};
$.ajax({
url: "/post/hosts",
async: false,
success: function (res) {
if (res.code == 0) {
data.list = res.data;
}
}
});
laytpl(tpl).render(data, function (html) {
post_hosts.innerHTML += html;
});
});
</script>
</div>
作者:碼農(nóng)飛哥
微信公眾號(hào):碼農(nóng)飛哥