1. 程式人生 > >Redis應用2-Redis實現開發者頭條頁面點贊功能

Redis應用2-Redis實現開發者頭條頁面點贊功能

開發者頭條是一個資源豐富的程式猿學習網站。當網站釋出新的內容的時候,內容優秀的總是會被點贊。今天就利用Redis實現這個小小的功能。

這裡寫圖片描述

為什麼使用Redis而不是MySQL實現?

Redis 和 MySQL應用場景不同。

  • 從效率來說:
    Redis的資料存放在記憶體,所以速度快但是會受到記憶體空間限制。
    MySQL存放在硬碟,在速度上肯定沒有Redis快,但是存放的資料量要多的多。

  • 從功能來說:
    Redis是一個K-V資料庫,同時還支援List/Hash/Set/Sorted Set等幾個簡單資料結構,所以只能以這些資料結構為基礎實現功能。

Redis效能好,快,併發高,但不能處理邏輯,而且不支援事務,看具體的場合,主要做資料快取,減少MySQL資料庫的壓力。最擅長的是結構化資料的cache,計數器場景,輕量級的訊息佇列,Top排行榜等網際網路應用場景。在點贊過後要立即重新整理顯示在頁面,所以推薦使用Redis。至於併發問題,在此暫不考慮(其實是技術比較渣。。。)

Redis幾種資料結構的適用場景

  • List: 雙向列表,適用於最新列表,關注列表;
  • Set: 適用於無順序的集合,點贊點踩,抽獎,已讀,共同好友;
  • SortedSet : 具有排序加成功能,適用於排行榜,優先佇列的實現;
  • Hash:物件屬性,不定長屬性數;
  • KV : 單一數值,適用於驗證碼,快取等實現。

程式碼實現

接下來看看如何去實現。

首先必須要安裝好Redis並且啟動Redis服務,瞭解Redis的5種資料結構型別的介紹和基本命令的操作,瞭解Redis的Java客戶端Jedis的操作。這些內容在此不一一詳述,詳情參見博文:http://blog.csdn.net/noaman_wgs/article/details/59501400

瞭解了以上內容以後,接下來進入實戰演練。
(注:本文只提供後臺實現點讚的一種實現的思路與介面的實現,不提供前臺頁面的顯示與操作)。

實現思路
使用Redis的Set資料結構儲存資料。
當前使用者點讚的話,就將當前使用者id存入到對應點贊集合當中,同時判斷點反對集合中是否有此id值,有的話就移除;
當前使用者點反對的話,與上面操作相反。
頁面顯示的時候就根據當前使用者id在點贊集合和反對集合中查詢,若id值在點贊集合中有對應值,就顯示1,表示當前使用者點贊;若在反對集合中有值,反對處就顯示1.

1. MAVEN中加入jar包;

        <dependency
>
<groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.8.0</version> </dependency>

2. Jedis底層操作
實現一個具有Dao功能的類,類中可以在Jedis連線池中獲取一個Jedis連線,同時實現與Redis資料庫的get, set , remove等操作。本文的實現用到的都是Redis中的Set資料結構。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * Created by Administrator on 2017/5/1.
 */
@Service
public class JedisAdapter implements InitializingBean {

    private static final Logger logger = LoggerFactory.getLogger(JedisAdapter.class);

    private Jedis jedis = null;
    private JedisPool jedisPool = null;

    @Override
    public void afterPropertiesSet() throws Exception {
        //初始化
        jedisPool = new JedisPool("localhost", 6379);
    }

    //獲取一個Jedis
    private Jedis getJedis(){
        try{
            jedis =  jedisPool.getResource();
        }catch (Exception e){
            logger.error("獲取jedis失敗!" + e.getMessage());
        }finally {
            if(jedis != null){
                jedis.close();
            }
        }
        return jedis;
    }

    /**
     * 獲取Redis中集合中某個key值
     * @param key
     * @return
     */
    public String get(String key){
        Jedis jedis = null;
        try {
            jedis =  jedisPool.getResource();
            return jedis.get(key);
        }catch (Exception e){
            logger.error("Jedis get發生異常 " + e.getMessage());
            return null;
        }finally {
            if(jedis != null){
                jedis.close();
            }
        }
    }

    /**
     * 給Redis中Set集合中某個key值設值
     * @param key
     * @param value
     */
    public void set(String key, String value){
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.set(key, value);
        }catch (Exception e){
            logger.error("Jedis set 異常" + e.getMessage());
        }finally {
            if(jedis != null){
                jedis.close();
            }
        }
    }

    /**
     * 向Redis中Set集合新增值:點贊
     * @return
     */
    public long sadd(String key, String value){
        Jedis jedis = null;
        try{
            jedis =  jedisPool.getResource();
            return jedis.sadd(key, value);
        }catch (Exception e){
            logger.error("Jedis sadd 異常 :" + e.getMessage());
            return 0;
        }finally {
            if (jedis != null){
                jedis.close();
            }
        }
    }

    /**
     * 移除:取消點贊
     * @param key
     * @param value
     * @return
     */
    public long srem(String key, String value){
        Jedis jedis = null;
        try{
            jedis =  jedisPool.getResource();
            return jedis.srem(key, value);
        }catch (Exception e){
            logger.error("Jedis srem 異常:" + e.getMessage());
            return 0;
        }finally {
            if (jedis != null){
                jedis.close();
            }
        }
    }

    /**
     *判斷key,value是否是集合中值
     * @param key
     * @param value
     * @return
     */
    public boolean sismember(String key, String value){
        Jedis jedis = null;
        try{
            jedis =  jedisPool.getResource();
            return jedis.sismember(key, value);
        }catch (Exception e){
            logger.error("Jedis sismember 異常:" + e.getMessage());
            return false;
        }finally {
            if (jedis != null){
                try{
                    jedis.close();
                }catch (Exception e){
                    logger.error("Jedis關閉異常" + e.getMessage());
                }
            }
        }
    }

    /**
     * 獲取集合大小
     * @param key
     * @return
     */
    public long scard(String key){
        Jedis jedis = null;
        try{
            jedis =  jedisPool.getResource();
            return jedis.scard(key);
        }catch (Exception e){
            logger.error("Jedis scard 異常:" + e.getMessage());
            return 0;
        }finally {
            if (jedis != null){
                jedis.close();
            }
        }
    }

}

3. 生成點贊與反對鍵值:
點選點贊或者反對的時候,都會根據所在資訊的id生成一個對應的點贊(likeKey) 或者 反對(disLikeKey)的Set集合key值, 將對應點贊或點反對的使用者id當中value值存入Set集合。

/**
 * Created by Administrator on 2017/5/1.
 */
public class RedisKeyUtil {

    private static String SPLIT = ":";
    private static String BIZ_LIKE = "LIKE";
    private static String BIZ_DISLIKE = "DISLIKE";

    /**
     * 產生key:如在newsId為2上的諮詢點贊後會產生key: LIKE:ENTITY_NEWS:2
     * @param entityId
     * @param entityType
     * @return
     */
    public static String getLikeKey(int entityId, int entityType){
        return BIZ_LIKE + SPLIT + String.valueOf(entityType) + SPLIT + String.valueOf(entityId);
    }
    /**
     * 取消贊:如在newsId為2上的資訊取消點贊後會產生key: DISLIKE:ENTITY_NEWS:2
     * @param entityId
     * @param entityType
     * @return
     */
    public static String getDisLikeKey(int entityId, int entityType){
        return BIZ_DISLIKE + SPLIT + String.valueOf(entityType) + SPLIT + String.valueOf(entityId);
    }
}

4. LikeService實現
主要實現的邏輯就是:使用者點贊,就將此使用者id存入對應的點贊集合中,同時從點反對集合中移除;同理點反對就將此使用者id存入對應的點反對集合中,同時從點贊集合中移除此使用者id。
點贊業務操作程式碼:

package com.nowcoder.service;

import com.nowcoder.util.JedisAdapter;
import com.nowcoder.util.RedisKeyUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * Created by Administrator on 2017/5/1.
 */
@Service
public class LikeService {

    @Autowired
    JedisAdapter jedisAdapter;

    /**
     * 判斷是點贊還是點反對
     * @param userId
     * @param entityType
     * @param entityId
     * @return
     */
    public int getLikeStatus(int userId, int entityType, int entityId) {
        //根據當前使用者的userid分別生成一個likeKey 和 disLikeKey,再分別判斷這兩個值是否在對應的Like集合中和disLikeKey集合中
        //比如如果在likeKey集合中,就返回一個1,否則返回-1
        String likeKey = RedisKeyUtil.getLikeKey(entityId, entityType);
        //判斷值為userId 的使用者是否在key為listKey 的集合中
        if(jedisAdapter.sismember(likeKey, String.valueOf(userId))){
            return 1;
        }
        String disLikeKey = RedisKeyUtil.getDisLikeKey(entityId, entityType);
        return jedisAdapter.sismember(disLikeKey, String.valueOf(userId)) ? -1: 0;
    }

    /**
     * 點贊:即當前使用者點贊後,被點贊使用者的like集合中就會加上一個該點讚的使用者資訊
     * @param userId
     * @param entityType
     * @param entityId
     * @return
     */
    public long like(int userId, int entityType, int entityId){
        //在當前news上點贊後獲取key:   LIKE:ENTITY_NEWS:2
       String likeKey = RedisKeyUtil.getLikeKey(entityId, entityType);
       //在喜歡集合中添加當前操作使用者的userId(即當前使用者點贊後,被點贊使用者的like集合中就會加上一個點讚的使用者資訊)
       jedisAdapter.sadd(likeKey, String.valueOf(userId));

       String disLikeKey = RedisKeyUtil.getDisLikeKey(entityId, entityType);
       jedisAdapter.srem(disLikeKey, String.valueOf(userId));

       //返回點贊數量
        return jedisAdapter.scard(likeKey);
    }

    /**
     * 反對 :即當前使用者點反對後,被點反對使用者的like集合中就會加上一個該點反對的使用者資訊
     * @param userId
     * @param entityType
     * @param entityId
     * @return
     */
    public long disLike(int userId, int entityType, int entityId){

        //誰點選反對,誰就出現在key為dislikeKey的Set集合中
        String disLikeKey = RedisKeyUtil.getDisLikeKey(entityId, entityType);
        jedisAdapter.sadd(disLikeKey, String.valueOf(userId));

        //從贊中刪除
        String likeKey = RedisKeyUtil.getLikeKey(entityId, entityType);
        jedisAdapter.srem(likeKey, String.valueOf(userId));

        return jedisAdapter.scard(likeKey);
    }
}

5. 點贊介面的實現:

package com.nowcoder.controller;

import com.nowcoder.model.EntityType;
import com.nowcoder.model.HostHolder;
import com.nowcoder.model.News;
import com.nowcoder.service.LikeService;
import com.nowcoder.service.NewsService;
import com.nowcoder.util.ToutiaoUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * Created by Administrator on 2017/5/1.
 */
@Controller
public class LikeController {

    @Autowired
    LikeService likeService;
    @Autowired
    NewsService newsService;
    @Autowired
    HostHolder hostHolder;

    @RequestMapping(path = {"/like"}, method = {RequestMethod.GET, RequestMethod.POST})
    @ResponseBody
    public String like(@RequestParam("newsId") int newsId){
        //在likeKey對應的集合中加入當前使用者的id
        long likeCount = likeService.like(hostHolder.getUser().getId(), EntityType.ENTITY_NEWS, newsId);

        //資訊上更新點贊數
        newsService.updateLikeCount(newsId, (int)likeCount);
        return ToutiaoUtil.getJSONString(0, String.valueOf(likeCount));
    }

    @RequestMapping(path = {"/dislike"}, method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    public String disLike(@RequestParam("newsId") int newsId){

        //在disLikeKey對應的集合中加入當前使用者
        long likeCount = likeService.disLike(hostHolder.getUser().getId(), EntityType.ENTITY_NEWS, newsId);
        if(likeCount <= 0){
            likeCount = 0;
        }

        //資訊上更新喜歡數
        newsService.updateLikeCount(newsId, (int)likeCount);
        return ToutiaoUtil.getJSONString(0, String.valueOf(likeCount));
    }


}

相關推薦

Redis應用2-Redis實現開發者頭條頁面功能

開發者頭條是一個資源豐富的程式猿學習網站。當網站釋出新的內容的時候,內容優秀的總是會被點贊。今天就利用Redis實現這個小小的功能。 為什麼使用Redis而不是MySQL實現? Redis 和 MySQL應用場景不同。 從效率來說: Redis

Redis + DB +訊息佇列 實現高效的文章功能

需求說明 使用者可點贊或踩,每贊一次,“贊”數量+1,每踩一次,“踩”數量+1,“點贊”和“點踩” 當天內二選一當天內有效 場景:使用者A 點贊 文章a,文章a 點贊量+1 ,同一使用者,同一文章 當天再次點選無效,贊與踩二選一,隔天再次點選有效 表設計 文章的

.NET 實現頁面功能

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm2.aspx.cs" Inherits="WebTest.WebForm2" %> <!DOCTYPE html>

AccessibilityService——實現自動遍歷功能

/** * Created by jiangzn on 17/2/6. */ public class MyAccessibilityService extends AccessibilityService { @Override protected void onServiceConn

Redis學習-2 redis配置

Redis 查詢配置命令格式如下: redis 127.0.0.1:6379> CONFIG GET CONFIG_SETTING_NAME 查詢所有配置格式如下: redis 127.0.0.1:6379> CONFIG GET * Redis設定配

尚矽谷redis學習2-redis的安裝和HelloWorld

  Reids: remote dictionary server   redis特點:支援持久化,支援複雜資料型別,支援備份   下載:   解壓:   執行make, make install   可能會需要安裝make, 可以用 apt-get install make命令

帶你實現開發者頭條(一) 啟動頁實現

對於很多初學者或者剛工作的Android新手來說,我們的專案經驗還停留在做demo的階段,有沒有一種很low的感覺,並且當你真正上手做大專案的時候又不知道把自己學到的東西用上去。。有一種自己家裡有座金山卻不知道可以用來換錢換吃的。 經過345大神的提醒,說覺得

Spring Boot + Redis + MQ實現高併發功能:專案實戰

開心一笑 【最後一次警告看我CSDN部落格的人,你們都他媽給老子小心點,首先我不管你是什麼身份,什麼背景,混的有多牛逼,是不是老闆,是不是社會人,這些我都不知道,我也都不在意,你們給我記住,都給我認真點看清楚:最近天氣實在很涼,大家多穿衣服,彆著涼了,知道不!

Andorid簡單應用理財工具-實現新增賬單頁面

教程連結:新增賬單頁面資料庫有變更,測試之前先更新資料庫,修改了DBHelper.java中 public static void createTable_bills(SQLiteDatabase db) { try {

Redis快取 + 定時寫入資料庫實現高效能功能

本文基於 SpringCloud, 使用者發起點贊、取消點贊後先存入 Redis 中,再每隔兩小時從 Redis 讀取點贊資料寫入資料庫中做持久化儲存。 點贊功能在很多系統中都有,但別看功能小,想要做好需要考慮的東西還挺多的。 點贊、取消點贊是高頻次的操作,若每

Java使用SSM框架實現登錄頁面記住密碼功能

turn 頁面 賦值 ++ mage login ring 實現 member 最終效果展示: 1、登錄頁面JSP代碼 <label class="checkbox"> <input type="checkbox" name="remembe

Java秒殺系統方案優化 2 --第2實現使用者登入以及分散式session功能

第2章 實現使用者登入以及分散式session功能 1. 明文密碼兩次md5入庫 分別使用簽名如1a2b3c4d,分別用簽名和密碼使用MD5加密兩次後(一次是最原始密碼加密,一次是加密後再使用MD5和簽名加密)才存入資料庫,每個使用者對應都有一個欄位,例如本案例中的sa

基於redis功能設計(張高偉)

前言 點贊其實是一個很有意思的功能。基本的設計思路有大致兩種, 一種自然是用mysql等 資料庫直接落地儲存, 另外一種就是利用點讚的業務特徵來扔到redis(或memcache)中, 然後離線刷回mysql等。 直接寫入Mysql 直接寫入Mysql是最簡單的做法。 做兩

spring 整合 quertz 並實現定時器頁面動態配置功能,支持腳本參數配置

nts implement cti () xtu ng- man con exists 背景 Java定時任務的實現技術主流的有三種,Java自帶的Java.util.Time類、Quartz、Spring3.0以後自帶的task。下面我們使用的是Quartz。 項

php + ajax實現 帖子功能

data inpu 局部刷新 瀏覽器 query 實現 script 函數 syn 知識: 一、首先頁面需要加載jquery框架 二、ajax常用參數解釋: ①、type:傳輸數據方式,get或者post ②、url:處理數據的PHP腳本 ③、data:傳輸的數據索引及值,

css簡單實現五角星評分、收藏、展示評分(半顆星、1/3顆星)

效果 詳細講解 pad nic bsp img () 之前 技術分享 1.前言 之前做的好幾個項目中,都會遇到打分,評分,點贊這樣的需求,寫了很多次,每次需要再寫的時候,就會翻出之前寫過的代碼,然後copy過來。總覺得這樣的話沒有進步,沒有把知識放進腦袋裏,所以,自己花了2

mongodb 用戶功能理論實現[轉載]

mongod div select AC 數組 pan exist class TP 在 posts(文章) 集合中儲存對該文章點贊的用戶的 _id 的數組,例如: // posts { _id: ObjectID(‘4e7020cb7cac81af7136236

微信小程式實現、取消功能

最近接觸到小程式,發現很有意思,在專案中遇到了一點小問題,就是點贊+取消點贊有些衝突,還有就是多項的點選,話不多說咱們直接上程式碼! 效果圖 wxml ? 1

WordPress非外掛實現文章功能

功能豐富的WordPress點贊外掛不少,但對於要在主題中整合簡單文章點贊功能的需求,外掛就顯得不合適,於是乎非外掛實現文章點贊功能的方法就誕生,實現思路是:可以通過ajax實時顯示點贊數量,自定義欄位儲存贊數量,Cookies禁止重新點贊。 具體操作步驟。 1、在當前主

基於EasyNVR攝像機無外掛直播流媒體伺服器實現類似於單登入功能的免登入直播功能

提出問題 EasyNVR是一套攝像機無外掛直播的流媒體伺服器軟體,他可以接入各種各樣的攝像機,再經過轉化統一輸出無外掛化直播的RTMP、HLS、HTTP-FLV流,同時,EasyNVR為了資料安全,提供了鑑權功能,可以配置成只有登入後才能直播觀看: 但這個時候問題又來了,很多企