记录一下基于redis来实现博客订阅量排名

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;

}

测试结果如下:

发布了40 篇原创文章 · 获赞 17 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_38796327/article/details/103813176