day72_淘淘商城項目_05_首頁輪播圖顯示實現 + redis的安裝及使用 + redis集羣環境搭建 + redis實現緩存 + 緩存同步_匠心筆記

課程計劃:javascript

  • 第五天
    • 一、首頁輪播圖的展現
    • 二、首頁大廣告展現流程圖
    • 三、Redis的安裝及使用
    • 四、Redis集羣的搭建
    • 五、向業務邏輯中添加緩存
    • 六、Jedis的使用(redis的客戶端)
    • 七、緩存同步
    • 八、Solr服務器安裝

一、首頁輪播圖的展現

  taotao-portal-web工程中,動態展現內容信息。
  前端團隊:負責JS,html等開發。
  後端團隊:負責後臺的開發並提供數據給前端。php

1.一、功能分析

只須要動態生成一個json數據,輪播圖就能夠動態展現。
taotao-portal-web工程下的index.jsp中:
Json數據格式:css

[
    {
        "srcB""http://image.taotao.com/images/2015/03/03/2015030304360302109345.jpg",
        "height"240,
        "alt""",
        "width"670,
        "src""http://image.taotao.com/images/2015/03/03/2015030304360302109345.jpg",
        "widthB"550,
        "href""http://sale.jd.com/act/e0FMkuDhJz35CNt.html?cpdad=1DLSUE",
        "heightB"240
    },
    {
        "srcB""http://image.taotao.com/images/2015/03/03/2015030304353109508500.jpg",
        "height"240,
        "alt""",
        "width"670,
        "src""http://image.taotao.com/images/2015/03/03/2015030304353109508500.jpg",
        "widthB"550,
        "href""http://sale.jd.com/act/UMJaAPD2VIXkZn.html?cpdad=1DLSUE",
        "heightB"240
    },
    ......
]

分析:
  taotao-portal-web 須要本身自定義的POJO的類型數據的列表。
  taotao-content 是服務層公用的,能夠被其餘的系統(表現層的系統)來調用。
  爲了通用性:taotao-content 服務層中獲取tb_content的內容列表 即:list<TbContent>
  taotao-portal-web 表現層須要拿到tb_content的列表,而後進行轉換成自定義的類型的數據列表便可。
  從tb_content表中取數據,根據(葉子節點)內容分類id查詢列表(內容列表)。
  內容分類id固定,須要配置在屬性文件中
  圖片的width、height配置在屬性文件中
  alt屬性從字段sub_title中獲取。
  src --> pic
  srcB --> pic2
  href --> urlhtml


URL:/index
參數:無。
返回值:首頁頁面(數據是JSON,設置在Model中)前端

業務邏輯:
  一、根據分類的id 查詢內容列表(List<TbContent>)
  二、服務層發佈服務。
  三、表現層引入服務。
  四、調用服務,轉換成自定義的數據類型(Ad1Node)的列表。
  五、將數據列表設置到Model中,返回給頁面。java

須要建立一個pojo轉換成頁面須要的json數據格式。放入taotao-portal-web工程的com.taotao.portal.pojo中。
AD1Node.javanode

/**
 * 前臺大廣告位須要的pojo數據類型
 * @author chenmingjun
 * @date 2018年11月18日下午4:03:34
 * @version 1.0
 */

public class AD1Node implements Serializable {

    private static final long serialVersionUID = 1L;

    private String srcB;
    private Integer height;
    private String alt;
    private Integer width;
    private String src;
    private Integer widthB;
    private String href;
    private Integer heightB;

    public String getSrcB() {
        return srcB;
    }

    public void setSrcB(String srcB) {
        this.srcB = srcB;
    }

    public Integer getHeight() {
        return height;
    }

    public void setHeight(Integer height) {
        this.height = height;
    }

    public String getAlt() {
        return alt;
    }

    public void setAlt(String alt) {
        this.alt = alt;
    }

    public Integer getWidth() {
        return width;
    }

    public void setWidth(Integer width) {
        this.width = width;
    }

    public String getSrc() {
        return src;
    }

    public void setSrc(String src) {
        this.src = src;
    }

    public Integer getWidthB() {
        return widthB;
    }

    public void setWidthB(Integer widthB) {
        this.widthB = widthB;
    }

    public String getHref() {
        return href;
    }

    public void setHref(String href) {
        this.href = href;
    }

    public Integer getHeightB() {
        return heightB;
    }

    public void setHeightB(Integer heightB) {
        this.heightB = heightB;
    }

    @Override
    public String toString() {
        return "AD1Node [srcB=" + srcB + ", height=" + height + ", alt=" + alt + ", width=" + width + ", src=" + src
                + ", widthB=" + widthB + ", href=" + href + ", heightB=" + heightB + "]";
    }
}

1.二、Dao層

  從tb_content表中取出數據,根據內容分類id查詢內容列表。可使用逆向工程生成的Mapper。linux

1.三、Service層

參數:Long categoryId
返回值:List<TbContent>nginx

    @Override
    public List<TbContent> getContentListByCategoryId(Long categoryId) {
        TbContentExample contentExample = new TbContentExample();
        Criteria criteria = contentExample.createCriteria();
        criteria.andCategoryIdEqualTo(categoryId);
        List<TbContent> list = contentMapper.selectByExample(contentExample);
        return list;
    }

1.3.一、發佈服務

  在taotao-content-service工程applicationContext-service.xml中發佈服務c++

1.四、表現層

  在taotao-portal-web中實現,查詢首頁輪播圖的內容。

1.4.一、引用服務

先在taotao-protal-web項目中引入對taotao-content-interface的依賴,如圖:


在taotao-content-web工程springmvc.xml中引入服務

1.4.二、Controller

在首頁展現以前,對數據進行處理,而後展現首頁,須要在IndexController中實現。

/**
 * 前臺首頁展現Controller
 * @author chenmingjun
 * @date 2018年12月31日
 * @version V1.0
 */

@Controller
public class IndexController {
    @Autowired
    private ContentService contentService;

    @Value("${AD1_CATEGORY_ID}")
    private Long AD1_CATEGORY_ID;
    @Value("${AD1_HEIGHT}")
    private Integer AD1_HEIGHT;
    @Value("${AD1_HEIGHT_B}")
    private Integer AD1_HEIGHT_B;
    @Value("${AD1_WIDTH}")
    private Integer AD1_WIDTH;
    @Value("${AD1_WIDTH_B}")
    private Integer AD1_WIDTH_B;

    /**
     * 展現前臺首頁,並展現首頁輪播圖(大廣告)+展現小廣告......
     * @return
     */

    @RequestMapping("/index")
    public String showIndex(Model model) {
        // 展現首頁輪播圖
        // 一、引入服務
        // 二、注入服務
        // 三、調用方法查詢TbContent的列表
        List<TbContent> contentList = contentService.getContentListByCategoryId(AD1_CATEGORY_ID);
        // 四、把TbContent的列表轉換爲AD1Node列表
        List<AD1Node> aD1NodeList = new ArrayList<>();
        for (TbContent tbContent : contentList) {
            AD1Node aD1Node = new AD1Node();
            aD1Node.setAlt(tbContent.getSubTitle());
            aD1Node.setHref(tbContent.getUrl());
            aD1Node.setSrc(tbContent.getPic());
            aD1Node.setSrcB(tbContent.getPic2());
            aD1Node.setHeight(AD1_HEIGHT);
            aD1Node.setHeightB(AD1_HEIGHT_B);
            aD1Node.setWidth(AD1_WIDTH);
            aD1Node.setWidthB(AD1_WIDTH_B);
            aD1NodeList.add(aD1Node);
        }
        // 五、把AD1Node列表轉換爲JSON數據
        String json = JsonUtils.objectToJson(aD1NodeList);
        // 六、把JSON數據設置到request域中去(Model)
        model.addAttribute("ad1", json);
        // 展現小廣告......等等省略了,之後實現
        return "index";
    }
}

屬性文件所在位置:


在taotao-portal-web中的springmvc.xml中還須要配置:
    <!-- 配置加載屬性文件 -->
    <context:property-placeholder location="classpath:resource/resource.properties"/>

1.五、測試

後臺數據以下圖所示:


前臺瀏覽器顯示界面:

其餘廣告位同理。

二、首頁大廣告展現流程圖

  首頁是系統的門戶,也就是系統的入口。因此首頁的訪問量是這個系統最大的。若是每次展現首頁都從數據庫中查詢首頁的內容信息,那麼勢必會對數據庫形成很大的壓力,因此須要使用緩存來減輕數據庫壓力。
  實現緩存的工具備不少,如今比較流行的是redis。
  首頁大廣告展現流程:
  展現流程:先從緩存取,若是不存在,從數據庫取出來,寫入緩存,再返回頁面;若是存在key,直接從緩存中取出來,展現到頁面。
  同步緩存:當事務提交(更新,刪除,插入)後,須要同步緩存,直接根據Key刪除redis的key(清空緩存),再展現時,由上邊的流程展現。

三、Redis的安裝及使用

3.一、redis的下載

  官網地址:http://redis.io/
  下載地址:http://download.redis.io/releases/redis-3.0.0.tar.gz

3.二、redis的安裝

  安裝redis須要c語言的編譯環境,若是沒有gcc須要在線安裝。以下命令:

[root@itheima ~]# yum -y install gcc-c++

  檢測是否有gcc環境,只需輸入命令:

[root@itheima ~]# gcc
[root@itheima ~]# gcc
gcc: 致命錯誤:沒有輸入文件
編譯中斷。
[root@itheima ~]

出現如上所述內容,表示gcc安裝成功。
安裝步驟:
  第一步:將redis的源碼包上傳到linux系統。
  第二步:解壓縮redis的源碼包。
  第三步:進行編譯。cd到解壓後的目錄,輸入命令:make
  第四步:進行安裝。輸入命令:make install PREFIX=/usr/local/redis
  第五步:檢查目錄是否存在。在/usr/local/redis下有bin目錄,則說明安裝成功。

3.三、鏈接redis

3.3.一、redis的啓動

前端啓動:在redis的安裝目錄下直接啓動redis-server

[root@itheima bin]# pwd
/usr/local/redis/bin
[root@itheima bin]# ./redis-server

後臺啓動:
把/root/redis-3.0.0/redis.conf複製到/usr/local/redis/bin目錄下

[root@itheima redis-3.0.0]# cp redis.conf /usr/local/redis/bin/

修改配置文件:將daemonize由no改成yes
再次啓動redis:

[root@itheima bin]# ./redis-server redis.conf 

查看redis進程:

[root@itheima bin]# ps aux | grep redis
root       5845  0.4  0.2 140888  2104 ?        Ssl  22:51   0:00 ./redis-server *:6379
root       5858  0.0  0.0 112720   980 pts/1    R+   22:51   0:00 grep --color=auto redis
[root@itheima bin]

前端啓動,不能更換終端,影響下一步操做。然後臺啓動,只在進程中悄悄啓動。推薦使用後臺啓動。

3.3.二、使用redis自帶的客戶端redis-cli鏈接redis

使用redis-cli創建鏈接:

[root@itheima bin]# ./redis-cli 
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> 

默認鏈接localhost運行在6379端口的redis服務。
鏈接其餘ip和端口:

[root@itheima bin]# ./redis-cli -h 192.168.25.153 -p 6379
192.168.25.153:6379> ping
PONG
192.168.25.153:6379> 

-h:鏈接的服務器的地址
-p:服務的端口號
退出redis鏈接的三種方式
第一種方式

[root@itheima bin]# ./redis-cli 
127.0.0.1:6379> quit
[root@itheima bin]

第二種方式

[root@itheima bin]# ./redis-cli 
127.0.0.1:6379> exit
[root@itheima bin]

第三種方式

Ctrl+C

3.3.三、使用redis的桌面客戶端軟件鏈接redis

先安裝:redis-desktop-manager-0.7.9.809.exe,注意:該軟件只能鏈接redis單機版。


安裝好後,雙擊打開軟件

redis默認有16個庫,若是不指定庫,默認存儲在索引爲0的庫中。

3.3.四、關閉redis服務

第一種:經過使用redis自帶的客戶端redis-cli進行關閉,使用 shutdown 命令。

[root@itheima bin]# ./redis-cli 
127.0.0.1:6379> shutdown
not connected> 

第二種:使用 kill 命令。
找到對應的redis的進程id 而後使用命令:(pid爲進程id)

[root@itheima bin]# kill -9 pid

3.四、redis五種數據類型

3.4.一、String:key-value

redis命令不區分大小寫,可是key區分大小寫的。
redis中全部的數據都是字符串
redis是單線程的,因此不適合存儲比較大的數據,最好要存小且能快速處理完的東西。
使用incr命令,若是key不存在,會自動建立key並自動+1。
  set key value   設置值
  get key   獲取值
  incr key   加一
  decr key   減一

127.0.0.1:6379set key1 2
OK
127.0.0.1:6379set key2 value2
OK
127.0.0.1:6379get key1
"2"
127.0.0.1:6379get key2
"value2"
127.0.0.1:6379Get key2
"value2"
127.0.0.1:6379incr key1
(integer) 3
127.0.0.1:6379decr key1
(integer) 2
127.0.0.1:6379

3.4.二、Hash: key-field-value

至關於一個key對應一個map (map中又是key-value),通常應用於歸類。
  hset key field value
  hget key field
  hincrby key field num

127.0.0.1:6379hset hkey1 filed1 value1
(integer) 1
127.0.0.1:6379hset hkey1 filed2 2
(integer) 1
127.0.0.1:6379hget hkey1 filed1
"value1"
127.0.0.1:6379hget hkey1 filed2
"2"
127.0.0.1:6379hincrby hkey1 filed2 1
(integer) 3
127.0.0.1:6379hincrby hkey1 filed2 -1
(integer) 2
127.0.0.1:6379

3.4.三、List

List是有順序可重複(數據結構中的:雙鏈表,隊列)
可做爲鏈表,能夠從左添加元素,也能夠從右添加元素。
  lpush list a b c d   (從左添加元素)
  rpush list 1 2 3 4   (從右邊添加元素)
  lrange list 0 -1   (從 0 到 -1 元素查看:也就表示查看全部)
  lpop list   (從左邊取,刪除)
  rpop list   (從右邊取,刪除)

127.0.0.1:6379> lpush list1 a b c d
(integer) 4
127.0.0.1:6379> lrange list1 0 -1
1"d"
2"c"
3"b"
4"a"
127.0.0.1:6379> rpush list1 1 2 3 4
(integer) 8
127.0.0.1:6379> lrange list1 0 -1
1"d"
2"c"
3"b"
4"a"
5"1"
6"2"
7"3"
8"4"
127.0.0.1:6379> lpop list1
"d"
127.0.0.1:6379> lrange list1 0 -1
1"c"
2"b"
3"a"
4"1"
5"2"
6"3"
7"4"
127.0.0.1:6379> rpop list1
"4"
127.0.0.1:6379> lrange list1 0 -1
1"c"
2"b"
3"a"
4"1"
5"2"
6"3"
127.0.0.1:6379> 

3.4.四、Set

Set無順序,不能重複。
  sadd set1 a b c d d   (向set1中添加元素)元素不重複
  smembers set1   (查詢元素)
  srem set1 a   (刪除元素)

127.0.0.1:6379sadd set1 a a a b b c
(integer) 3
127.0.0.1:6379smembers set1
1) "c"
2) "b"
3) "a"
127.0.0.1:6379srem set1 a
(integer) 1
127.0.0.1:6379smembers set1
1) "c"
2) "b"
127.0.0.1:6379

3.4.五、SortedSet(zset)

SortedSet有順序,不能重複。
適合作排行榜 排序須要一個分數屬性。
  zadd zset1 8 a 4 b 5 c 1 d   (添加元素:zadd key score member)
  zrange key start stop [WITHSCORES]   (查看全部元素:zrange key 0 -1 withscores)
    若是要查看分數,須要加上withscores
  zrange zset1 0 -1   (從小到大)
  zrevrange zset1 0 -1   (從大到小)
  zincrby zset1 score member   (對元素 member 增長 score)

127.0.0.1:6379> zadd zset1 8 a 4 b 5 c 1 d
(integer) 4
127.0.0.1:6379> zrange zset1 0 -1
1"d"
2"b"
3"c"
4"a"
127.0.0.1:6379> zadd zset1 9 a
(integer) 0
127.0.0.1:6379> zrange zset1 0 -1
1"d"
2"b"
3"c"
4"a"
127.0.0.1:6379> zrange zset1 0 -1 withscores
1"d"
2"1"
3"b"
4"4"
5"c"
6"5"
7"a"
8"9"
127.0.0.1:6379> zrevrange zset1 0 -1
1"a"
2"c"
3"b"
4"d"
127.0.0.1:6379> zrevrange zset1 0 -1 withscores
1"a"
2"9"
3"c"
4"5"
5"b"
6"4"
7"d"
8"1"
127.0.0.1:6379> 

3.五、key 命令

expire key second   設置key的過時時間
ttl key             查看剩餘時間(-2 表示不存在,-1 表示已被持久化(永不過時),正數表示剩餘的時間)
persist key         清除過時時間,也即持久化,持久化成功體提示 1,不成功提示 0
del key             刪除key  
exists key          若key存在,返回1,不然返回0
select 0            表示選擇0號數據庫。默認是0號數據庫。

redis 默認有16個庫 select num 從0開始換庫

keys *              獲取redis裏面全部的key
dbsize              查看當前數據庫的key的數量
flushdb             清空當前庫的全部key
flushall            清空全部庫的key
exists key          是否存在該key
move key db         把當前庫的key移到db號庫中
type key            查看key的類型

3.六、redis持久化方案

redis 數據都放在內存中。若是機器掛掉,內存的數據就不存在。
須要作持久化處理,將內存中的數據保存在磁盤,下一次啓動的時候就能夠恢復數據到內存中。
  方案1:RDB 快照形式(按期將當前時刻的數據保存磁盤中)會產生一個dump.rdb文件。
    特色:會存在數據丟失,性能較好,數據備份。
  方案2:AOF:append only file (全部對redis的操做命令記錄在aof文件中),恢復數據,從新執行一遍便可。
    特色:每秒保存(也可能會存在數據丟失),數據比較完整,耗費性能。其實也能夠設置成每次保存,可是此就失去了緩存的意義了。
redis默認開啓RDB。以下圖:


redis.conf 中默認設置了保存規則及時間間隔。
save 開頭的一行就是持久化配置,能夠配置多個條件(每行配置一個條件),每一個條件之間是「或」的關係。
    save 900 1      表示15分鐘(900秒)內至少1個鍵被更改則進行快照。
    save 300 10     表示5分鐘(300秒)內至少10個鍵被更改則進行快照。
    save 60 10000   表示1分鐘(60秒)內至少10000個鍵被更改則進行快照。

AOF開啓設置,修改 redis.conf 文件,以下圖:
將appendonly設置爲yes便可。


同時開啓兩個持久化方案,則按照 AOF的持久化放案恢復數據
默認是按照RDB的方式恢復數據,若是開啓了AOF,就是用AOF恢復數據,數據是存在於/usr/local/redis/bin/appendonly.aof文件中。

四、Redis集羣的搭建

4.一、redis-cluster架構圖


架構細節詳解以下:
架構細節:
    (1) 全部的 redis 節點彼此互聯(`PING-PONG機制`),內部使用`二進制協議`優化傳輸速度和帶寬。
    (2) 節點的 fail(失敗) 是經過集羣中`超過半數的節點檢測失效`時才生效。即`集羣搭建中主機的個數爲奇數`
    (3) 客戶端與 redis 節點直連,不須要中間 proxy層,客戶端不須要鏈接集羣全部節點,鏈接集羣中任何一個可用節點便可。
    (4) redis-cluster 把全部的物理節點映射到 [0-16383]slot(槽) 上,cluster 負責維護 node<-->slot<-->value

Redis 集羣中內置了 `16384 個哈希槽`,當須要在 Redis 集羣中放置一個 key-value 時,redis 先對 key 使用 crc16 算法算出一個結果,而後把結果`對 16384 求餘數`,這樣每一個 key 都會對應一個編號在 0-16383 之間的哈希槽,redis 會根據節點數量`大體均等`的將哈希槽映射到不一樣的節點。以下圖所示:

哈希槽的圖解:


redis-cluster投票,容錯機制圖解:

4.二、redis集羣的搭建

  由於有投票容錯機制,因此須要至少3個節點(奇數),爲了集羣的高可用,爲每個節點增長一個備份機(共6臺服務器)。
  搭建僞分佈式集羣方案:在一臺機器裏面運行6個redis實例。端口須要不一樣便可(7001-7006)。

4.2.一、集羣搭建環境

4.2.二、搭建步驟

參考文章連接:http://www.javashuo.com/article/p-ocgjabwp-cc.html
建立集羣命令:

[root@itheima redis-cluster]# ./redis-trib.rb create --replicas 1 192.168.25.153:7001 192.168.25.153:7002 192.168.25.153:7003 192.168.25.153:7004 192.168.25.153:7005 192.168.25.153:7006

4.三、集羣的使用方法

redis-cli鏈接集羣。鏈接主節點或者從節點均可以。

[root@itheima redis-cluster]# 7001/redis-cli -h 192.168.25.153 -p 7006 -c
192.168.25.153:7006> set key1 123
-> Redirected to slot [9189] located at 192.168.25.153:7002
OK
192.168.25.153:7002> keys *
1"key1"
192.168.25.153:7002> set key2 abc
-> Redirected to slot [4998] located at 192.168.25.153:7001
OK
192.168.25.153:7001> keys *
1"key2"
192.168.25.153:7001> set key3 bbbb
OK
192.168.25.153:7001> set key4 cccc
-> Redirected to slot [13120] located at 192.168.25.153:7003
OK
192.168.25.153:7003> 

-c:表明鏈接的是redis集羣
使用命令操做redis集羣是和單機版的redids是同樣的。

192.168.25.153:7003cluster info   #查詢集羣的信息
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:3
cluster_stats_messages_sent:1623
cluster_stats_messages_received:1623
192.168.25.153:7003cluster nodes  #查詢集羣的節點
ecb5aafd409740004ac3bf298318e2243f562e4e 192.168.25.153:7004 slave b207611daffa174990c64499495fc42ce3978767 0 1542619629901 4 connected
30775ef3b509604b604b76a4407334fe1fb6304f 192.168.25.153:7006 slave c6514cdac6977ef8a8fbee8c7d5c3e4edd7df585 0 1542619634936 6 connected
fd369b55daa7cdd8640e1e1bfa8fa2ab9cfe39cd 192.168.25.153:7002 master - 0 1542619633928 2 connected 5461-10922
c6514cdac6977ef8a8fbee8c7d5c3e4edd7df585 192.168.25.153:7003 myself,master - 0 0 3 connected 10923-16383
75a701f4cd545ee704ded294c17bbfa3f02eaee1 192.168.25.153:7005 slave fd369b55daa7cdd8640e1e1bfa8fa2ab9cfe39cd 0 1542619631916 5 connected
b207611daffa174990c64499495fc42ce3978767 192.168.25.153:7001 master - 0 1542619632921 1 connected 0-5460
192.168.25.153:7003

五、Java客戶端Jedis的使用方法

須要把jedis依賴的jar包添加到工程中。Maven工程中須要把jedis的座標添加到依賴。
推薦添加到服務層。本例中是:taotao-content-service工程中。
第一步:添加jar包的依賴


在taotao-content-service中添加測試方法,測試以下:

5.一、經過單實例鏈接redis服務器單機版

    @Test
    public void jedisClientTest() throws Exception 
{
        // 一、建立一個Jedis的鏈接對象(單實例),須要指定服務端的ip及端口
        Jedis jedis = new Jedis("192.168.25.153"6379);
        // 二、選擇要操做的數據庫
        jedis.select(2);
        // 三、執行redis命令,即便用Jedis對象操做數據庫,每一個redis命令對應一個方法
        jedis.set("s4""曉藝,你還好嗎?");
        // 從redis中取值
        String result = jedis.get("s4");
        // 打印結果
        System.out.println(result);
        // 四、關閉鏈接
        jedis.close();
    }

經過單實例鏈接redis服務器,咱們發現每一次鏈接都須要建立鏈接,建立鏈接是比較浪費資源的,因此咱們須要使用鏈接池來鏈接redis數據庫。

5.二、經過鏈接池鏈接redis服務器單機版

測試代碼以下:

    /**
     * 經過鏈接池鏈接redis服務器單機版
     */

    @Test
    public void jedisPoolTest() throws Exception 
{
        // 一、建立一個鏈接池JedisPool對象(單實例),須要指定服務端的ip及端口
        JedisPool jedisPool = new JedisPool("192.168.25.153"6379);
        // 二、從鏈接池JedisPool中獲取jedis會話對象
        Jedis jedis = jedisPool.getResource();
        // 三、使用redis命令操做redis數據庫(方法級別使用:用完以後就要關閉)
        String result = jedis.get("s11");
        System.out.println(result);
        // 四、關閉鏈接,操做完畢後關閉jedis對象,鏈接池要回收資源。
        jedis.close();
        // 五、關閉鏈接池對象JedisPool
        jedisPool.close();
    }

5.三、使用spring整合JedisPool鏈接redis服務器單機版

添加spring的jar包
配置spring配置文件applicationContext.xml
applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 
        http://www.springframework.org/schema/mvc 
        http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd 
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.2.xsd 
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-3.2.xsd 
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx-3.2.xsd "
>


    <!-- 鏈接池配置 -->
    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <!-- 最大鏈接數 -->
        <property name="maxTotal" value="30" />
        <!-- 最大空閒鏈接數 -->
        <property name="maxIdle" value="10" />
        <!-- 每次釋放鏈接的最大數目 -->
        <property name="numTestsPerEvictionRun" value="1024" />
        <!-- 釋放鏈接的掃描間隔(毫秒) -->
        <property name="timeBetweenEvictionRunsMillis" value="30000" />
        <!-- 鏈接最小空閒時間 -->
        <property name="minEvictableIdleTimeMillis" value="1800000" />
        <!-- 鏈接空閒多久後釋放, 當空閒時間>該值 且 空閒鏈接>最大空閒鏈接數 時直接釋放 -->
        <property name="softMinEvictableIdleTimeMillis" value="10000" />
        <!-- 獲取鏈接時的最大等待毫秒數,小於零:阻塞不肯定的時間,默認-1 -->
        <property name="maxWaitMillis" value="1500" />
        <!-- 在獲取鏈接的時候檢查有效性, 默認false -->
        <property name="testOnBorrow" value="false" />
        <!-- 在空閒時檢查有效性, 默認false -->
        <property name="testWhileIdle" value="true" />
        <!-- 鏈接耗盡時是否阻塞, false報異常,ture阻塞直到超時, 默認true -->
        <property name="blockWhenExhausted" value="false" />
    </bean>

    <!-- redis單機 經過鏈接池鏈接redis服務器 -->
    <bean id="jedisPool" class="redis.clients.jedis.JedisPool" destroy-method="close">
        <constructor-arg name="poolConfig" ref="jedisPoolConfig" />
        <constructor-arg name="host" value="192.168.5.128" />
        <constructor-arg name="port" value="6379" />
    </bean>
</beans>

測試代碼以下:

    /**
     * 經過鏈接池鏈接redis服務器單機版(整合spring)
     */

    @Test
    public void jedisPoolTest() 
{
        JedisPool pool = (JedisPool) applicationContext.getBean("jedisPool");
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            jedis.set("s5""lisi");
            String name = jedis.get("s5");
            System.out.println(name);
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            if (jedis != null) {
                // 關閉鏈接
                jedis.close();
            }
        }
    }

5.四、使用JedisCluster類鏈接redis服務器集羣版

第一步:使用JedisCluster對象。須要使用Set<HostAndPort>參數。表示redis節點的列表。
第二步:直接使用JedisCluster對象操做redis。該對象在系統中是單例存在的。
第三步:打印結果。
第四步:在系統關閉前,關閉JedisCluster對象。

    /**
     * 使用JedisCluster類鏈接redis集羣版
     */

    @Test
    public void jedisClusterTest() throws Exception 
{
        // 須要使用 Set<HostAndPort> 參數。表示redis節點的列表。
        Set<HostAndPort> nodes = new HashSet<>();
        nodes.add(new HostAndPort("192.168.25.153"7001));
        nodes.add(new HostAndPort("192.168.25.153"7002));
        nodes.add(new HostAndPort("192.168.25.153"7003));
        nodes.add(new HostAndPort("192.168.25.153"7004));
        nodes.add(new HostAndPort("192.168.25.153"7005));
        nodes.add(new HostAndPort("192.168.25.153"7006));
        // 建立JedisCluster對象,該對象在系統中是單例存在的
        JedisCluster jedisCluster = new JedisCluster(nodes);
        // 執行JedisCluster對象中的方法,方法和redis中的一一對應
        jedisCluster.set("cluster-test""Jedis connects redis cluster test");
        String result = jedisCluster.get("cluster-test");
        System.out.println(result);
        // 程序結束時須要關閉JedisCluster對象
        jedisCluster.close();
    }

5.五、使用spring整合JedisCluster鏈接redis服務器集羣版

配置applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 
        http://www.springframework.org/schema/mvc 
        http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd 
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.2.xsd 
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-3.2.xsd 
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx-3.2.xsd "
>

    <!-- 鏈接池配置 -->
    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <!-- 最大鏈接數 -->
        <property name="maxTotal" value="30" />
        <!-- 最大空閒鏈接數 -->
        <property name="maxIdle" value="10" />
        <!-- 每次釋放鏈接的最大數目 -->
        <property name="numTestsPerEvictionRun" value="1024" />
        <!-- 釋放鏈接的掃描間隔(毫秒) -->
        <property name="timeBetweenEvictionRunsMillis" value="30000" />
        <!-- 鏈接最小空閒時間 -->
        <property name="minEvictableIdleTimeMillis" value="1800000" />
        <!-- 鏈接空閒多久後釋放, 當空閒時間>該值 且 空閒鏈接>最大空閒鏈接數 時直接釋放 -->
        <property name="softMinEvictableIdleTimeMillis" value="10000" />
        <!-- 獲取鏈接時的最大等待毫秒數,小於零:阻塞不肯定的時間,默認-1 -->
        <property name="maxWaitMillis" value="1500" />
        <!-- 在獲取鏈接的時候檢查有效性, 默認false -->
        <property name="testOnBorrow" value="true" />
        <!-- 在空閒時檢查有效性, 默認false -->
        <property name="testWhileIdle" value="true" />
        <!-- 鏈接耗盡時是否阻塞, false報異常,ture阻塞直到超時, 默認true -->
        <property name="blockWhenExhausted" value="false" />
    </bean>
    <!-- redis集羣 -->
    <bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">
        <constructor-arg index="0">
            <set>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg index="0" value="192.168.5.128"></constructor-arg>
                    <constructor-arg index="1" value="7001"></constructor-arg>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg index="0" value="192.168.5.128"></constructor-arg>
                    <constructor-arg index="1" value="7002"></constructor-arg>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg index="0" value="192.168.5.128"></constructor-arg>
                    <constructor-arg index="1" value="7003"></constructor-arg>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg index="0" value="192.168.5.128"></constructor-arg>
                    <constructor-arg index="1" value="7004"></constructor-arg>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg index="0" value="192.168.5.128"></constructor-arg>
                    <constructor-arg index="1" value="7005"></constructor-arg>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg index="0" value="192.168.5.128"></constructor-arg>
                    <constructor-arg index="1" value="7006"></constructor-arg>
                </bean>
            </set>
        </constructor-arg>
        <constructor-arg index="1" ref="jedisPoolConfig"></constructor-arg>
    </bean>
</beans>

測試代碼:

    private ApplicationContext applicationContext;
    @Before
    public void init() 
{
        applicationContext = new ClassPathXmlApplicationContext(
                "classpath:applicationContext.xml");
    }

    /**
    * 使用spring實現Jedis鏈接Redis集羣
   */

    @Test
    public void testJedisCluster2() 
{
        JedisCluster jedisCluster = (JedisCluster) applicationContext.getBean("jedisCluster");

        jedisCluster.set("name""xiaoyi");
        String value = jedisCluster.get("name");
        System.out.println(value);
    }

六、向業務邏輯中添加緩存

  由於集羣是比較消耗成本的,因此在公司中,通常生產環境使用集羣開發環境使用單機版
  咱們在項目整合中都須要有。使用設計模式之策略模式:接口+實現類。
  能夠開發一個接口,有單機版的實現類集羣版的實現類。使用時能夠面向接口開發,不影響業務邏輯,使用spring管理實現類,部署時切換實現類便可。

6.一、接口封裝

  經常使用的操做redis的方法提取出一個接口,分別對應單機版和集羣版建立兩個實現類。

6.1.一、接口定義

package com.taotao.jedis;

import java.util.Map;

public interface JedisClient {

    String set(String key, String value);

    String get(String key);

    Boolean exists(String key);

    Long expire(String key, int seconds);

    Long ttl(String key);

    Long incr(String key);

    Long hset(String key, String field, String value);

    String hget(String key, String field);

    Long hdel(String key, String... field); // 刪除hkey

    Map<StringString> hgetAll(String key); // 查詢全部的hash類型的列表
}

6.1.二、redis單機版實現類

package com.taotao.jedis;

import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

public class JedisClientPool implements JedisClient {

    @Autowired
    private JedisPool jedisPool;

    @Override
    public String set(String key, String value) {
        Jedis jedis = jedisPool.getResource();
        String result = jedis.set(key, value);
        jedis.close();
        return result;
    }

    @Override
    public String get(String key) {
        Jedis jedis = jedisPool.getResource();
        String result = jedis.get(key);
        jedis.close();
        return result;
    }

    @Override
    public Boolean exists(String key) {
        Jedis jedis = jedisPool.getResource();
        Boolean result = jedis.exists(key);
        jedis.close();
        return result;
    }

    @Override
    public Long expire(String key, int seconds) {
        Jedis jedis = jedisPool.getResource();
        Long result = jedis.expire(key, seconds);
        jedis.close();
        return result;
    }

    @Override
    public Long ttl(String key) {
        Jedis jedis = jedisPool.getResource();
        Long result = jedis.ttl(key);
        jedis.close();
        return result;
    }

    @Override
    public Long incr(String key) {
        Jedis jedis = jedisPool.getResource();
        Long result = jedis.incr(key);
        jedis.close();
        return result;
    }

    @Override
    public Long hset(String key, String field, String value) {
        Jedis jedis = jedisPool.getResource();
        Long result = jedis.hset(key, field, value);
        jedis.close();
        return result;
    }

    @Override
    public String hget(String key, String field) {
        Jedis jedis = jedisPool.getResource();
        String result = jedis.hget(key, field);
        jedis.close();
        return result;
    }

    @Override
    public Long hdel(String key, String... field) {
        Jedis jedis = jedisPool.getResource();
        Long result = jedis.hdel(key, field);
        jedis.close();
        return result;
    }

    @Override
    public Map<String, String> hgetAll(String key) {
        Jedis jedis = jedisPool.getResource();
        Map<String, String> map = jedis.hgetAll(key);
        jedis.close();
        return map;
    }
}

applicationContext-redis.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-4.2.xsd
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-4.2.xsd 
    http://www.springframework.org/schema/tx 
    http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
    http://code.alibabatech.com/schema/dubbo 
    http://code.alibabatech.com/schema/dubbo/dubbo.xsd
    http://www.springframework.org/schema/util 
    http://www.springframework.org/schema/util/spring-util-4.2.xsd"
>

    <!-- 注意:咱們已經在 applicationContext-service.xml 配置了包掃描器,該配置作了兩件事:既開啓註解,又掃描包 -->
    <!-- <context:annotation-config></context:annotation-config> 表示開啓註解,可是不能掃描包。因此這句配置能夠不用了。-->
    <!-- 若是使用方式二,咱們在加載配置文件的時候,要加載全部的配置文件才能夠。(本案例中使用的是方式一) -->
    <!-- 若是使用方式一,咱們在加載配置文件的時候,要加載單個的配置文件便可。 -->

    <!-- 配置對redis單機版的鏈接 -->
    <bean id="jedisPool" class="redis.clients.jedis.JedisPool">
        <constructor-arg name="host" value="192.168.25.153"></constructor-arg>
        <constructor-arg name="port" value="6379"></constructor-arg>
    </bean>
    <!-- 手動配置的jedis單機版客戶端實現類bean:會在spring容器中加載 ,緣由是:咱們已經在 applicationContext-service.xml 配置了包掃描器,該配置作了兩件事:既開啓註解,又掃描包-->
    <bean id="jedisClientPool" class="com.taotao.jedis.手動配置的bean"/> 
</beans>

注意:在spring中要使用註解,咱們先要開啓註解,開啓註解有兩種方式
  方式一:配置<context:annotation-config></context:annotation-config>註解,表示開啓註解,可是不能掃描包。
  方式二:配置包掃描器<context:component-scan base-package="com.taotao.content.service"></context:component-scan>註解,表示掃描全部須要帶@Service註解的類,該配置作了兩件事:既開啓註解,又掃描包。
因此咱們只要配置了包掃描器,那麼咱們手動配置的bean就會在spring容器中加載。
特別注意:
  若是使用方式二,咱們在加載配置文件的時候,要加載全部的配置文件才能夠。(本案例中使用的是方式一)
  若是使用方式一,咱們在加載配置文件的時候,要加載單個的配置文件便可。

6.1.三、redis集羣版實現類

package com.taotao.jedis;

import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;

import redis.clients.jedis.JedisCluster;

public class JedisClientCluster implements JedisClient {

    @Autowired
    private JedisCluster jedisCluster;

    @Override
    public String set(String key, String value) {
        return jedisCluster.set(key, value);
    }

    @Override
    public String get(String key) {
        return jedisCluster.get(key);
    }

    @Override
    public Boolean exists(String key) {
        return jedisCluster.exists(key);
    }

    @Override
    public Long expire(String key, int seconds) {
        return jedisCluster.expire(key, seconds);
    }

    @Override
    public Long ttl(String key) {
        return jedisCluster.ttl(key);
    }

    @Override
    public Long incr(String key) {
        return jedisCluster.incr(key);
    }

    @Override
    public Long hset(String key, String field, String value) {
        return jedisCluster.hset(key, field, value);
    }

    @Override
    public String hget(String key, String field) {
        return jedisCluster.hget(key, field);
    }

    @Override
    public Long hdel(String key, String... field) {
        return jedisCluster.hdel(key, field);
    }

    @Override
    public Map<String, String> hgetAll(String key) {
        return jedisCluster.hgetAll(key);
    }
}

添加此配置到applicationContext-redis.xml中

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-4.2.xsd
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-4.2.xsd 
    http://www.springframework.org/schema/tx 
    http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
    http://code.alibabatech.com/schema/dubbo 
    http://code.alibabatech.com/schema/dubbo/dubbo.xsd
    http://www.springframework.org/schema/util 
    http://www.springframework.org/schema/util/spring-util-4.2.xsd"
>

    <!-- 注意:咱們已經在 applicationContext-service.xml 配置了包掃描器,該配置作了兩件事:既開啓註解,又掃描包 -->
    <!-- <context:annotation-config></context:annotation-config> 表示開啓註解,可是不能掃描包。因此這句配置能夠不用了。-->
    <!-- 若是使用方式二,咱們在加載配置文件的時候,要加載全部的配置文件才能夠。(本案例中使用的是方式一) -->
    <!-- 若是使用方式一,咱們在加載配置文件的時候,要加載單個的配置文件便可。 -->

    <!-- 配置對redis單機版的鏈接 -->
    <!-- 單機版和集羣版不能共存,使用單機版時註釋集羣版的配置。使用集羣版,把單機版註釋。   
    <bean id="jedisPool" class="redis.clients.jedis.JedisPool">
        <constructor-arg name="host" value="192.168.25.153"></constructor-arg>
        <constructor-arg name="port" value="6379"></constructor-arg>
    </bean>
        手動配置的jedis單機版客戶端實現類bean:會在spring容器中加載 
    <bean id="jedisClientPool" class="com.taotao.jedis.JedisClientPool"/>    
    -->

    <!-- 配置對redis集羣版的鏈接 -->
    <bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">
        <constructor-arg>
            <set>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg name="host" value="192.168.25.153"></constructor-arg>
                    <constructor-arg name="port" value="7001"></constructor-arg>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg name="host" value="192.168.25.153"></constructor-arg>
                    <constructor-arg name="port" value="7002"></constructor-arg>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg name="host" value="192.168.25.153"></constructor-arg>
                    <constructor-arg name="port" value="7003"></constructor-arg>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg name="host" value="192.168.25.153"></constructor-arg>
                    <constructor-arg name="port" value="7004"></constructor-arg>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg name="host" value="192.168.25.153"></constructor-arg>
                    <constructor-arg name="port" value="7005"></constructor-arg>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg name="host" value="192.168.25.153"></constructor-arg>
                    <constructor-arg name="port" value="7006"></constructor-arg>
                </bean>
            </set>
        </constructor-arg>
    </bean>
    <!-- 手動配置的jedis集羣版客戶端實現類bean:會在spring容器中加載 -->
    <bean id="jedisClientCluster" class="com.taotao.jedis.JedisClientCluster"/> 
</beans>

注意:單機版和集羣版不能共存,使用單機版時註釋集羣版的配置。使用集羣版,把單機版註釋。

6.二、封裝代碼測試

策略設計模式的優點,只要切換實現類(即簡單註釋下spring配置文件),不需修改測試類。
測試代碼:

public class JedisSpringTest {

    @Test
    public void testJedisClient() throws Exception 
{
        // 初始化Spring容器,咱們已經在 applicationContext-service.xml 配置了包掃描器,須要咱們在加載配置文件的時候,要加載全部的配置文件才能夠。
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-*.xml"); // 加載全部配置文件
        // 從容器中得到JedisClient對象
        JedisClient jedisClient = applicationContext.getBean(JedisClient.class);
        jedisClient.set("first""100");
        String result = jedisClient.get("first");
        System.out.println(result);             
    }
}

命令行中進行測試以下:沒有問題

[root@itheima bin]# ./redis-cli -p 7001 -c
127.0.0.1:7001> get first
-> Redirected to slot [11149] located at 192.168.25.153:7003
"100"
192.168.25.153:7003> 

6.三、向業務邏輯中添加緩存

因爲咱們處於開發階段,因此咱們先將咱們的鏈接方式切換爲單機版。

6.3.一、功能分析

查詢內容列表時添加緩存。
  一、查詢數據庫以前先查詢緩存。
  二、查詢到結果,直接響應結果。
  三、查詢不到,緩存中沒有須要查詢數據庫。
  四、把查詢結果添加到緩存中。
  五、返回結果。
向redis中添加緩存,添加緩存咱們使用什麼樣的數據類型呢?
  key:categoryId 字段(field),即咱們的查詢條件
  value:內容列表。因爲redis中不能存java對象,因此須要把java對象轉換成json格式的數據(即json字符串),可使用工具類。
推薦使用hash類型,由於hash類型能夠對key進行歸類。
HASH_KEY:HASH
      |--KEY:VALUE
      |--KEY:VALUE
      |--KEY:VALUE
      |--KEY:VALUE
注意:添加緩存不能影響正常業務邏輯。

6.3.二、代碼實現

在ContentServiceImpl實現類中添加緩存。
部分代碼以下:

    // 經過spring容器注入JedisClient對象
    @Autowired
    private JedisClient jedisClient;

    @Value("${INDEX_CONTENT_KEY}")
    private String INDEX_CONTENT_KEY;

    @Override
    public List<TbContent> getContentListByCategoryId(Long categoryId) {
        // 查詢數據庫以前先查詢緩存
        // 注意:添加了緩存不能影響正常業務邏輯,因此咱們使用try...catch,將異常捕獲,其他的向下繼續執行
        try {
            // 從緩存中查詢到結果,須要把json格式的數據轉成List返回
            String json = jedisClient.hget(INDEX_CONTENT_KEY, categoryId + "");
            if (StringUtils.isNotBlank(json)) { // 若是json不是空纔去轉換
                List<TbContent> list = JsonUtils.jsonToList(json, TbContent.class);
                return list;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 第一次查詢沒有,緩存中沒有命中,則去查詢數據庫
        TbContentExample contentExample = new TbContentExample();
        Criteria criteria = contentExample.createCriteria();
        criteria.andCategoryIdEqualTo(categoryId);
        List<TbContent> list = contentMapper.selectByExample(contentExample);

        // 把從數據庫中查詢到的結果添加到緩存
        // 注意:添加了緩存不能影響正常業務邏輯,因此咱們使用try...catch,將異常捕獲,其他的向下繼續執行
        try {
            // 添加緩存咱們使用什麼樣的數據類型呢?答:使用String或者Hash都可,推薦使用Hash類型,由於Hash類型能夠對key進行歸類。
            jedisClient.hset(INDEX_CONTENT_KEY, categoryId + "", JsonUtils.objectToJson(list));
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 返回結果
        return list;
    }

屬性文件所在的位置:


要想能加載還須要在taotao-content-service工程applicationContext-dao.xml中配置:
    <!-- 加載配置文件 -->
    <context:property-placeholder location="classpath:properties/*.properties" />

6.四、緩存同步

在對內容信息作增刪改操做後只須要把對應緩存key刪除便可。
咱們以【添加內容】爲例,能夠根據categoryId刪除。

    @Override
    public TaotaoResult saveContent(TbContent content) {
        // 注入Mapper
        // 補全屬性
        content.setCreated(new Date());
        content.setUpdated(content.getCreated());
        // 將內容數據保存至內容表
        contentMapper.insert(content);
        try {
            // 同步緩存:在對內容信息作【增刪改】操做後只須要把對應緩存key刪除便可。
            jedisClient.hdel(INDEX_CONTENT_KEY, content.getCategoryId().toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return TaotaoResult.ok();
    }

【更新內容】和【刪除內容】加入緩存同步代碼以下:

    @Override
    public TaotaoResult updateContent(TbContent content) {
        // 設置建立時間
        content.setCreated(new Date());
        // 設置更新時間
        content.setUpdated(new Date());
        // 將內容數據更新至內容表(全字段更新)
        contentMapper.updateByPrimaryKeyWithBLOBs(content);
        try {
            // 同步緩存:在對內容信息作【增刪改】操做後只須要把對應緩存key刪除便可。
            jedisClient.hdel(INDEX_CONTENT_KEY, content.getCategoryId().toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return TaotaoResult.ok();
    }

    @Override
    public TaotaoResult getContentText(Long id) {
        TbContent content = contentMapper.selectByPrimaryKey(id);
        return TaotaoResult.ok(content);
    }

    @Override
    public TaotaoResult deleteContent(List<Long> ids) {
        for (Long id : ids) {
            TbContent content = contentMapper.selectByPrimaryKey(id);
            contentMapper.deleteByPrimaryKey(id);
            try {
                // 同步緩存:在對內容信息作【增刪改】操做後只須要把對應緩存key刪除便可。
                jedisClient.hdel(INDEX_CONTENT_KEY, content.getCategoryId().toString());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return TaotaoResult.ok();
    }

七、附錄

咱們在搭建redis集羣的時候,會寫一些自定義的批處理文件,以下圖所示:


具體內容以下:
redis_start_all.sh

 

cd redis01/
./redis-server redis.conf
cd ..

cd redis02/
./redis-server redis.conf
cd ..

cd redis03/
./redis-server redis.conf
cd ..

cd redis04/
./redis-server redis.conf
cd ..

cd redis05/
./redis-server redis.conf
cd ..

cd redis06/
./redis-server redis.conf
cd ..

redis_shutdown_all.sh

cd redis01/
./redis-cli -h 192.168.25.153 -p 7001 shutdown
cd ..

cd redis02/
./redis-cli -h 192.168.25.153 -p 7002 shutdown
cd ..

cd redis03/
./redis-cli -h 192.168.25.153 -p 7003 shutdown
cd ..

cd redis04/
./redis-cli -h 192.168.25.153 -p 7004 shutdown
cd ..

cd redis05/
./redis-cli -h 192.168.25.153 -p 7005 shutdown
cd ..

cd redis06/
./redis-cli -h 192.168.25.153 -p 7006 shutdown
cd ..

redis_delete_aof_rdb_nodes.sh

cd redis01/
rm -rf appendonly.aof dump.rdb nodes.conf
cd ..

cd redis02/
rm -rf appendonly.aof dump.rdb nodes.conf
cd ..

cd redis03/
rm -rf appendonly.aof dump.rdb nodes.conf
cd ..

cd redis04/
rm -rf appendonly.aof dump.rdb nodes.conf
cd ..

cd redis05/
rm -rf appendonly.aof dump.rdb nodes.conf
cd ..

cd redis06/
rm -rf appendonly.aof dump.rdb nodes.conf
cd ..
相關文章
相關標籤/搜索