1. 程式人生 > >帶你逐步深入瞭解SSM框架——淘淘商城專案之redis快取

帶你逐步深入瞭解SSM框架——淘淘商城專案之redis快取

1.  課程計劃

1、  Redis服務搭建

2、  為功能新增快取功能

2.  redis介紹

2.1. 什麼是redis

         Redis是用C語言開發的一個開源的高效能鍵值對(key-value)資料庫。它通過提供多種鍵值資料型別來適應不同場景下的儲存需求,目前為止Redis支援的鍵值資料型別如

下:

字串型別

雜湊型別

列表型別

集合型別

有序集合型別。

2.2. redis的應用場景

快取(資料查詢、短連線、新聞內容、商品內容等等)。(最多使用)

分散式叢集架構中的session分離。

聊天室的線上好友列表。

任務佇列。(秒殺、搶購、12306等等)

應用排行榜。

網站訪問統計。

資料過期處理(可以精確到毫秒)

2.3. Redis的安裝

redis是C語言開發,建議在linux上執行,本教程使用Centos6.4作為安裝環境。

         安裝redis需要先將官網下載的原始碼進行編譯,編譯依賴gcc環境,如果沒有gcc環境,需要安裝gcc:yum install gcc-c++

n  版本說明

   本教程使用redis3.0版本。3.0版本主要增加了redis叢集功能。

n  原始碼下載

   從官網下載

   http://download.redis.io/releases/redis-3.0.0.tar.gz

    將redis-3.0.0.tar.gz拷貝到/usr/local下

n  解壓原始碼

   tar -zxvf  redis-3.0.0.tar.gz

n  進入解壓後的目錄進行編譯

   cd /usr/local/redis-3.0.0

   make

n  安裝到指定目錄,如 /usr/local/redis

   cd /usr/local/redis-3.0.0

   make PREFIX=/usr/local/redis install

n  redis.conf

redis.conf是redis的配置檔案,redis.conf在redis原始碼目錄。

注意修改port作為redis程序的埠,port預設6379。

n  拷貝配置檔案到安裝目錄下

   進入原始碼目錄,裡面有一份配置檔案 redis.conf,然後將其拷貝到安裝路徑下

   cd /usr/local/redis

   mkdir conf

   cp /usr/local/redis-3.0.0/redis.conf  /usr/local/redis/bin

n  安裝目錄bin下的檔案列表

 

redis3.0新增的redis-sentinel是redis叢集管理工具可實現高可用。

配置檔案目錄:

 

2.4.redis啟動

2.4.1.  前端模式啟動

         直接執行bin/redis-server將以前端模式啟動,前端模式啟動的缺點是ssh命令視窗關閉則redis-server程式結束,不推薦使用此方法。如下圖:

2.4.2.  後端模式啟動

修改redis.conf配置檔案,daemonize yes 以後端模式啟動。

執行如下命令啟動redis:

cd /usr/local/redis

./bin/redis-server ./redis.conf

redis預設使用6379埠。


也可更改redis.conf檔案,修改埠號:

 

2.5. 通過jedis連線redis單機

2.5.1.  jar包

pom座標:

   <dependency>

          <groupId>redis.clients</groupId>

          <artifactId>jedis</artifactId>

          <version>2.7.0</version>

      </dependency>

jar包如下:

2.5.2.  單例項連線

通過建立單例項jedis物件連線redis服務,如下程式碼:

//單例項連線redis

   @Test

   publicvoid testJedisSingle() {

       Jedisjedis =new Jedis("192.168.101.3", 6379);

       jedis.set("name","bar");

       Stringname = jedis.get("name");

       System.out.println(name);

       jedis.close();

    }

1.     外部連線不上redis的解決方法

由於linux防火牆預設開啟,redis的服務埠6379並不在開放規則之內,所有需要將此埠開放訪問或者關閉防火牆。

關閉防火牆命令:sevice iptablesstop

如果是修改防火牆規則,可以修改:/etc/sysconfig/iptables檔案

2.5.3.  使用連線池連線

         通過單例項連線redis不能對redis連線進行共享,可以使用連線池對redis連線進行共享,提高資源利用率,使用jedisPool連線redis服務,如下程式碼:

    @Test

   publicvoid pool() {

       JedisPoolConfigconfig =new JedisPoolConfig();

      //最大連線數

       config.setMaxTotal(30);

      //最大連線空閒數

       config.setMaxIdle(2);

       JedisPoolpool =new JedisPool(config,"192.168.101.3", 6379);

       Jedisjedis =null;

      try {

           jedis = pool.getResource();

           jedis.set("name","lisi");

           Stringname = jedis.get("name");

           System.out.println(name);

       }catch(Exception ex){

           ex.printStackTrace();

       }finally{

          if(jedis !=null){

             //關閉連線

              jedis.close();

           }

       }

    }

詳細的連線池配置引數參考下節jedis和spring整合中applicationContext.xml的配置內容。

2.5.4.  jedis與spring整合

配置spring配置檔案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">

<!--連線池配置 -->

   <beanid="jedisPoolConfig"class="redis.clients.jedis.JedisPoolConfig">

      <!--最大連線數 -->

      <propertyname="maxTotal"value="30"/>

      <!--最大空閒連線數 -->

      <propertyname="maxIdle"value="10"/>

      <!--每次釋放連線的最大數目 -->

      <propertyname="numTestsPerEvictionRun"value="1024"/>

      <!--釋放連線的掃描間隔(毫秒) -->

      <propertyname="timeBetweenEvictionRunsMillis"value="30000"/>

      <!--連線最小空閒時間 -->

      <propertyname="minEvictableIdleTimeMillis"value="1800000"/>

      <!--連線空閒多久後釋放,當空閒時間>該值空閒連線>最大空閒連線數時直接釋放 -->

      <propertyname="softMinEvictableIdleTimeMillis"value="10000"/>

      <!--獲取連線時的最大等待毫秒數,小於零:阻塞不確定的時間,預設-1 -->

      <propertyname="maxWaitMillis"value="1500"/>

      <!--在獲取連線的時候檢查有效性,預設false -->

      <propertyname="testOnBorrow"value="true"/>

      <!--在空閒時檢查有效性,預設false -->

      <propertyname="testWhileIdle"value="true"/>

      <!--連線耗盡時是否阻塞, false報異常,ture阻塞直到超時,預設true -->

      <propertyname="blockWhenExhausted"value="false"/>

   </bean>

   <!--redis單機通過連線池 -->

   <beanid="jedisPool"class="redis.clients.jedis.JedisPool"destroy-method="close">

       <constructor-argname="poolConfig" ref="jedisPoolConfig"/>

       <constructor-argname="host" value="192.168.25.145"/>

       <constructor-argname="port" value="6379"/>

    </bean>

測試程式碼:

private ApplicationContextapplicationContext;

   @Before

   publicvoid init() {

      applicationContext =new ClassPathXmlApplicationContext(

             "classpath:applicationContext.xml");

    }

   @Test

    publicvoid testJedisPool() {

    JedisPoolpool = (JedisPool)applicationContext.getBean("jedisPool");

          try {

           jedis= pool.getResource();

           jedis.set("name","lisi");

           Stringname = jedis.get("name");

           System.out.println(name);

       }catch(Exception ex){

           ex.printStackTrace();

       }finally{

          if(jedis !=null){

             //關閉連線

              jedis.close();

           }

       }

         }

3.  redis叢集

3.1. 叢集原理

3.1.1.  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會根據節點數量大致均等的將雜湊槽對映到不同的節點

3.1.2.  redis-cluster投票:容錯

 

(1)領著投票過程是叢集中所有master參與,如果半數以上master節點與master節點通訊超過(cluster-node-timeout),認為當前master節點掛掉.

(2):什麼時候整個叢集不可用(cluster_state:fail)? 

    a:如果叢集任意master掛掉,且當前master沒有slave.叢集進入fail狀態,也可以理解成叢集的slot對映[0-16383]不完成時進入fail狀態. ps : redis-3.0.0.rc1加入cluster-require-full-coverage引數,預設關閉,開啟叢集相容部分失敗.

    b:如果叢集超過半數以上master掛掉,無論是否有slave叢集進入fail狀態.

  ps:當叢集不可用時,所有對叢集的操作做都不可用,收到((error) CLUSTERDOWN The cluster is down)錯誤

3.2.ruby環境

redis叢集管理工具redis-trib.rb依賴ruby環境,首先需要安裝ruby環境:

安裝ruby

yum install ruby

yum install rubygems

安裝ruby和redis的介面程式

拷貝redis-3.0.0.gem至/usr/local下

執行:

gem install /usr/local/redis-3.0.0.gem

3.3. 建立叢集:

3.3.1.  叢集結點規劃

這裡在同一臺伺服器用不同的埠表示不同的redis伺服器,如下:

主節點:192.168.101.3:7001 192.168.101.3:7002 192.168.101.3:7003

從節點:192.168.101.3:7004 192.168.101.3:7005 192.168.101.3:7006

在/usr/local下建立redis-cluster目錄,其下建立7001、7002。。7006目錄,如下:

 

將redis安裝目錄bin下的檔案拷貝到每個700X目錄內,同時將redis原始碼目錄src下的redis-trib.rb拷貝到redis-cluster目錄下。

修改每個700X目錄下的redis.conf配置檔案:

port XXXX

#bind 192.168.101.3

cluster-enabled yes

3.3.2.  啟動每個結點redis服務

分別進入7001、7002、...7006目錄,執行:

./redis-server ./redis.conf

檢視redis程序:

3.3.3.  執行建立叢集命令

執行redis-trib.rb,此指令碼是ruby指令碼,它依賴ruby環境。

./redis-trib.rb create --replicas 1 192.168.101.3:7001 192.168.101.3:7002192.168.101.3:7003 192.168.101.3:7004 192.168.101.3:7005  192.168.101.3:7006

./redis-trib.rb create --replicas 1 192.168.131.102:7001 192.168.131.102:7002 192.168.131.102:7003 192.168.131.102:7004 192.168.131.102:7005  192.168.131.102:7006


說明:

redis叢集至少需要3個主節點,每個主節點有一個從節點總共6個節點

replicas指定為1表示每個主節點有一個從節點

注意:

如果執行時報如下錯誤:

[ERR]Node XXXXXX is not empty. Either the node already knows other nodes (check withCLUSTER NODES) or contains some key in database 0

解決方法是刪除生成的配置檔案nodes.conf,如果不行則說明現在建立的結點包括了舊叢集的結點資訊,需要刪除redis的持久化檔案後再重啟redis,比如:appendonly.aof、dump.rdb

建立叢集輸出如下:

>>> Creating cluster

Connecting to node 192.168.101.3:7001: OK

Connecting to node 192.168.101.3:7002: OK

Connecting to node 192.168.101.3:7003: OK

Connecting to node 192.168.101.3:7004: OK

Connecting to node 192.168.101.3:7005: OK

Connecting to node 192.168.101.3:7006: OK

>>> Performing hash slotsallocation on 6 nodes...

Using 3 masters:

192.168.101.3:7001

192.168.101.3:7002

192.168.101.3:7003

Adding replica 192.168.101.3:7004 to192.168.101.3:7001

Adding replica 192.168.101.3:7005 to192.168.101.3:7002

Adding replica 192.168.101.3:7006 to192.168.101.3:7003

M: cad9f7413ec6842c971dbcc2c48b4ca959eb5db4192.168.101.3:7001

  slots:0-5460 (5461 slots) master

M: 4e7c2b02f0c4f4cfe306d6ad13e0cfee90bf5841192.168.101.3:7002

  slots:5461-10922 (5462 slots) master

M: 1a8420896c3ff60b70c716e8480de8e50749ee65192.168.101.3:7003

  slots:10923-16383 (5461 slots) master

S: 69d94b4963fd94f315fba2b9f12fae1278184fe8192.168.101.3:7004

  replicates cad9f7413ec6842c971dbcc2c48b4ca959eb5db4

S: d2421a820cc23e17a01b597866fd0f750b698ac5192.168.101.3:7005

  replicates 4e7c2b02f0c4f4cfe306d6ad13e0cfee90bf5841

S: 444e7bedbdfa40714ee55cd3086b8f0d5511fe54192.168.101.3:7006

  replicates 1a8420896c3ff60b70c716e8480de8e50749ee65

Can I set the above configuration? (type'yes' to accept): yes

>>> Nodes configuration updated

>>> Assign a different configepoch to each node

>>> Sending CLUSTER MEET messagesto join the cluster

Waiting for the cluster to join...

>>> Performing Cluster Check(using node 192.168.101.3:7001)

M: cad9f7413ec6842c971dbcc2c48b4ca959eb5db4192.168.101.3:7001

  slots:0-5460 (5461 slots) master

M: 4e7c2b02f0c4f4cfe306d6ad13e0cfee90bf5841192.168.101.3:7002

  slots:5461-10922 (5462 slots) master

M: 1a8420896c3ff60b70c716e8480de8e50749ee65192.168.101.3:7003

  slots:10923-16383 (5461 slots) master

M: 69d94b4963fd94f315fba2b9f12fae1278184fe8192.168.101.3:7004

  slots: (0 slots) master

  replicates cad9f7413ec6842c971dbcc2c48b4ca959eb5db4

M: d2421a820cc23e17a01b597866fd0f750b698ac5192.168.101.3:7005

  slots: (0 slots) master

  replicates 4e7c2b02f0c4f4cfe306d6ad13e0cfee90bf5841

M: 444e7bedbdfa40714ee55cd3086b8f0d5511fe54192.168.101.3:7006

  slots: (0 slots) master

  replicates 1a8420896c3ff60b70c716e8480de8e50749ee65

[OK] All nodes agree about slotsconfiguration.

>>> Check for open slots...

>>> Check slots coverage...

[OK] All 16384 slots covered.

3.4. 查詢叢集資訊

叢集建立成功登陸任意redis結點查詢叢集中的節點情況。

客戶端以叢集方式登陸:

 

說明:

./redis-cli -c -h192.168.101.3 -p 7001,其中-c表示以叢集方式連線redis-h指定ip地址,-p指定埠號

cluster nodes 查詢叢集結點資訊

cluster info 查詢叢集狀態資訊

 

3.5. 新增主節點

叢集建立成功後可以向叢集中新增節點,下面是新增一個master主節點

新增7007結點,參考叢集結點規劃章節新增一個“7007”目錄作為新節點。

執行下邊命令:

./redis-trib.rb add-node  192.168.101.3:7007 192.168.101.3:7001

 

檢視叢集結點發現7007已新增到叢集中:

 

3.5.1.  hash槽重新分配

新增完主節點需要對主節點進行hash槽分配這樣該主節才可以儲存資料。

redis叢集有16384個槽,叢集中的每個結點分配自已槽,通過檢視叢集結點可以看到槽佔用情況。

 

給剛新增的7007結點分配槽:

第一步:連線上叢集

./redis-trib.rb reshard 192.168.101.3:7001(連線叢集中任意一個可用結點都行)

第二步:輸入要分配的槽數量

 

輸入 500表示要分配500個槽

第三步:輸入接收槽的結點id


這裡準備給7007分配槽,通過cluster nodes檢視7007結點id為15b809eadae88955e36bcdbb8144f61bbbaf38fb

輸入:15b809eadae88955e36bcdbb8144f61bbbaf38fb

第四步:輸入源結點id


這裡輸入all

第五步:輸入yes開始移動槽到目標結點id

3.6. 新增從節點

叢集建立成功後可以向叢集中新增節點,下面是新增一個slave從節點。

新增7008從結點,將7008作為7007的從結點。

./redis-trib.rbadd-node --slave --master-id主節點id新增節點的ip和埠叢集中已存在節點ip和埠

執行如下命令:

./redis-trib.rb add-node --slave--master-id cad9f7413ec6842c971dbcc2c48b4ca959eb5db4  192.168.101.3:7008 192.168.101.3:7001

cad9f7413ec6842c971dbcc2c48b4ca959eb5db4  是7007結點的id,可通過cluster nodes檢視。

 

注意:如果原來該結點在叢集中的配置資訊已經生成cluster-config-file指定的配置檔案中(如果cluster-config-file沒有指定則預設為nodes.conf),這時可能會報錯:

[ERR]Node XXXXXX is not empty. Either the node already knows other nodes (check withCLUSTER NODES) or contains some key in database 0

解決方法是刪除生成的配置檔案nodes.conf,刪除後再執行./redis-trib.rb add-node指令

檢視叢集中的結點,剛新增的7008為7007的從節點:

 

3.7. 刪除結點:

./redis-trib.rb del-node 127.0.0.1:70054b45eb75c8b428fbd77ab979b85080146a9bc017

刪除已經佔有hash槽的結點會失敗,報錯如下:

[ERR] Node 127.0.0.1:7005 is not empty!Reshard data away and try again.

需要將該結點佔用的hash槽分配出去(參考hash槽重新分配章節)。

3.8. jedisCluster

3.8.1.  測試程式碼

//連線redis叢集

   @Test

   publicvoid testJedisCluster() {

       JedisPoolConfigconfig =new JedisPoolConfig();

      //最大連線數

       config.setMaxTotal(30);

      //最大連線空閒數

       config.setMaxIdle(2);

      //叢集結點

       Set<HostAndPort>jedisClusterNode =new HashSet<HostAndPort>();

       jedisClusterNode.add(new HostAndPort("192.168.101.3", 7001));

       jedisClusterNode.add(new HostAndPort("192.168.101.3", 7002));

       jedisClusterNode.add(new HostAndPort("192.168.101.3", 7003));

       jedisClusterNode.add(new HostAndPort("192.168.101.3", 7004));

       jedisClusterNode.add(new HostAndPort("192.168.101.3", 7005));

       jedisClusterNode.add(new HostAndPort("192.168.101.3", 7006));

       JedisClusterjc =new JedisCluster(jedisClusterNode, config);

       JedisClusterjcd =new JedisCluster(jedisClusterNode);

       jcd.set("name","zhangsan");

       Stringvalue = jcd.get("name");

       System.out.println(value);

    }

3.8.2.  使用spring

配置applicationContext.xml

<!--連線池配置 -->

   <beanid="jedisPoolConfig"class="redis.clients.jedis.JedisPoolConfig">

      <!--最大連線數 -->

      <property