1. 创建一个与redis服务器的单连接工具类:
/**
* redis连接工具类
* 连接的实例采用延迟加载,只针对单连接
*
* @date 2020-01-02
* @since
*/
public class RedisConnection {
private static class RedisInnerClass {
private static Jedis client;
static {
// 选择自己的ip地址
client = new Jedis("*.*.*.*", 6379);
client.auth("your password");
if (!client.isConnected()) {
System.out.println("连接错误");
}
}
}
public static Jedis getInstance() {
return RedisInnerClass.client;
}
public static void close() {
getInstance().close();
}
}
2. 模拟实现博客订阅排名:
/**
* 测试阅读博客文章排名
*
* 将文章得到的支持票数乘以一个常量,然后加上文章的发布时间,得到的结果便是文章的评分
*
思考:
需要记录文章的评分,文章标题,网址,发布文章的用户,发布时间,投票数量等消息
都需要存储在redis中,所以面对多个数据组成的一个文章,我们采用hash结构来存储相关的数据
article => hash key 这里采用article:87654来区分不同的文章key
title => 标题
link => 网站链接
poster => 发布人
time => 发布时间
votes => 文章投票数
// 文章需要采用文章 + : + 编号的形式来标识,使用zset来保证文章发布时间的有序性,有序通过分值来标识的发布时间
// 因此我们需要一个zset集合来存储文章编号和发布时间
// 为了防止用户对一个文章进行多次投票,网站需要为每一篇文章记录投票的用户名单,这里可以采用set集合来进行存储
// set保证了用户的唯一性,set总数便是投票数votes
// 为了节约内存,当一篇文章发布一周,则不允许对其投票,文章的投票数会被记录,文章投票的用户信息则会被删除
*
*
*
* @date 2020-01-02
* @since
*/
public class BlogRanking {
/**
* 常量 = 864000 / 200 一天的秒数初一展示一天所需的支持票数200
* 意味着文章每获得一个支持票,则评分增加432
*/
private static final Integer CONSTANT = 432;
private static final String INIT_ZERO = "0";
/**
* 文章id
*/
private static final String ARTICLE_ID = "article:%d";
/**
* 随机的用户id
*/
private static final String USER_ID = "user:%d";
/**
* 文章所处的订阅用户信息列表
*/
private static final String ARTICLE_USERS = "%s:users";
/**
* 用于标识文章-时间集合,zset的key
*/
private static final String ARTICLE_TIME_LIST = "article_time_list";
/**
* 用于标识文章-积分集合zset的可以
*/
private static final String ARTICLE_SCORE_LIST = "article_score_list";
/**
* 定义初始值的文章数
*/
private static final String ARTICLE_NUM = "article_num";
/**
* 标题
*/
private static final String TITLE = "title";
/**
* 文章链接
*/
private static final String LINK = "link";
/**
* 发布人
*/
private static final String POSTER = "poster";
/**
* 发布时间
*/
private static final String TIME = "time";
/**
* 用户订阅数
*/
private static final String VOTES = "votes";
/**
* 一周的毫秒数
*/
private static final Integer WEEK_TIME = 7 * 24 * 60 * 60 * 1000;
/**
* 随机生成一个用户id
* 限制在1-500
*
* @date 2020-01-02
* @updateDate 2020-01-02
* @param
* @return
*/
public static String getRandomUser() {
return String.format(USER_ID, new Random().nextInt(10));
}
/**
* 发布文章,每新增一篇文章则+1,最大为200篇文章
*
* @date 2020-01-02
* @updateDate 2020-01-02
* @param
* @return
*/
public static void postArticle(Article article) {
Jedis client = RedisConnection.getInstance();
String articleId = String.format(ARTICLE_ID, client.incr(ARTICLE_NUM));
// 将新增的文章加入到zset集合中,时间为当前时间的毫秒数,用于标记文章的顺序
// 获取当前时间的毫秒数
long time = System.currentTimeMillis();
client.zadd(ARTICLE_TIME_LIST, time, articleId);
// 新增文章 + 积分的有序zset集合
client.zadd(ARTICLE_SCORE_LIST, 0, articleId);
// 新增文章的标题等相关内容
client.hset(articleId, TITLE, article.getTitle());
client.hset(articleId, LINK, article.getLink());
client.hset(articleId, POSTER, article.getPoster());
client.hset(articleId, TIME, String.valueOf(time));
client.hset(articleId, VOTES, INIT_ZERO);
// 添加文章订阅的用户信息列表集合,采用set来进行存储
client.sadd(String.format(ARTICLE_USERS, articleId), INIT_ZERO);
}
/**
* 随机投票
*
* @date 2020-01-02
* @updateDate 2020-01-02
* @param
* @return
*/
public static void vote(String articleId, String userId) {
Jedis client = RedisConnection.getInstance();
// 获取文章订阅用户列表
String articleUsers = String.format(ARTICLE_USERS, articleId);
// 校验发布时间是否超过一周,超过则拒绝投票并删除文章关联的用户投票信息
long now = System.currentTimeMillis();
long time = Long.valueOf(client.hget(articleId, TIME)) + WEEK_TIME;
// 如果当前时间大于time则代表已经过去一周
if (now > time) {
System.out.println("文章ID[" + articleId + "]发布已经超过一周,不可再进行投票");
// 判断订阅用户信息列表是否存在,存在则移除
if (client.exists(articleUsers)) {
client.del(articleUsers);
System.out.println("移除文章所投票的用户信息列表成功");
}
return;
}
// 校验当前文章是否存在,不存在则报错
if (!client.exists(articleId)) {
System.out.println("不存在此文章信息,无法进行投票");
return;
}
// 校验当前人是否已经进行过投递,存在则不允许再次投票
if (client.sismember(articleUsers, userId)) {
System.out.println("用户[" + userId + "]已经投票了该文章,不可重复投票");
return;
}
// 新增评分
client.zincrby(ARTICLE_SCORE_LIST, CONSTANT, articleId);
// 添加投票数
client.hincrBy(articleId, VOTES, 1);
// 添加用户数到用户列表中
client.sadd(articleUsers, userId);
}
/**
* 第三步:获取最新的10个排名文章的相关信息
*
* @date 2020-01-02
* @updateDate 2020-01-02
* @param
* @return
*/
public static List<Article> listRanking() {
Jedis client = RedisConnection.getInstance();
// 获取文章排名前10的信息
Set<String> set = client.zrevrange(ARTICLE_SCORE_LIST, 0, 10);
return set.stream().map(member -> {
Map<String, String> map = client.hgetAll(member);
String json = JSON.toJSONString(map);
return JSON.parseObject(json, Article.class);
}).collect(Collectors.toList());
}
/**
* 第一步:创建500篇文章
*
* @date 2020-01-02
* @updateDate 2020-01-02
* @param
* @return
*/
public static void createArticle() {
for (int i = 1; i <= 500; i++) {
Article article = new Article();
article.setLink(String.format("www.xiaofeifei%d.com", i));
article.setPoster(String.format("xiaofeifei%d", i));
String title = i % 2 == 0 ? "我爱读书%d" : "我爱学习%d";
article.setTitle(String.format(title, i));
BlogRanking.postArticle(article);
}
}
/**
* 第二步:随机为文章添加访问量和积分
*
* @date 2020-01-02
* @updateDate 2020-01-02
* @param
* @return
*/
public static void randomAddScore() {
// 第二步随机进行100次用户订阅文章
for (int i = 1; i <= 100; i++) {
BlogRanking.vote(String.format(ARTICLE_ID, i), BlogRanking.getRandomUser());
}
}
public static void main(String[] args) {
List<Article> articles = BlogRanking.listRanking();
articles.forEach(System.out::println);
System.out.println("success");
}
}
/**
* 创建一个文章类,用户记录文章的相关信息
*/
@Data
class Article {
/**
* 标题
*/
private String title;
/**
* 网址链接
*/
private String link;
/**
* 发布人
*/
private String poster;
/**
* 投票数
*/
private Integer votes;
}
测试结果如下: