親熱接觸Redis-第一天

引言

nosql,大規模分佈式緩存遍天下。Internet的時代在中國由其走得前沿,這一切歸功於我國特點的電商。html

所以nosql、大數據技術在中國應用的比國外還要前沿。java

從這一章開始咱們將開始進入到真正的SOA、PAAS、SAAS、互聯網的領域,所以每一篇我都會添加一小段業務的基礎知識,讓你們在學習技術的同一時候也可以瞭解一些業務,這邊的業務不是指的business logic不是讓你們去作業務人員,而是爲你們帶來IDEA,」沒有作不到僅僅有想不到「,阿里支付寶爲何發了。。。不是技術,而是它的IDEA。mysql


業務基礎知識-國內國外電商比較以及爲何電商技術在中國走得這麼前沿

14年1月時在上海參加過一個MagentoCom召集的電商峯會。會上一羣LAO WAI在那邊大談本身的電商經驗,有LV。有ADIDAS,bla...bla...bla...聽着聽着,真是認爲可笑。歇息時隨便問一個LAO WAI一個問題:linux


」大家知道什麼叫一元秒殺嗎?「git


」大家知道什麼7天無理由退貨嗎?「github


」大家知道什麼叫0體驗嗎?「web


」大家有沒有雙11,雙12。有沒有交易量過億。redis

spring

。「sql


LAO WAI 」張嘴,流哈喇子,billions of transaction? billions「


看着他這個樣子。我是硬忍住後來到WC一邊抽菸一邊笑,笑得連手上的菸頭都抖了,唉。

。。。。。


不是說國外的電商不這麼幹,而是經濟體制決定了電商的發展道路不一樣,因爲國外的電商是基於他們的」信任「式經濟的模式下的,所以國外的電商基本都是信用卡、預先衝款、預售卡、充值卡這些,這也決定了國外沒有什麼1元秒殺。

。這些哄人氣的東東。


另一點就是國外人口也少。呵呵,這算是一個理由。


還有就是國外基本無」不是名牌「這一說。記得去法國onsite交流學習時。假設你穿的一件ADIDAS或者是NIKE或者是阿瑪尼假設不是正宗名牌,是仿的話也會被警察抓住罰款,所以國外是個商品基本都有牌子。這是一種」互信「機制。


中國的電商儘管也是走線上、線下可是它多了一個」第三方資金託付「即支付寶、易寶、微信(微信是後起之秀)這種東東。所以這種體制決定了」先驗貨,後付錢「的這種遊戲規則。


加上中國人多,這下大數據、NOSQL、分佈式這些技術固然成爲了通向internet的重中之重了。


進入Redis課程

Redis是什麼

Redis是一個NOSQL,NOSQL有不少種。它們分爲:

  • 列存儲,如:Hbase、Cassandra這種
  • 文檔存儲。如:MongoDB(首推)
  • key-value存儲。如:Berkeley DB、MemcacheDB、Redis。當中Redis最強
  • 圖存儲,這塊基本不用,有:Neo4j、Versant
  • XML存儲,如:Berkeley DB Xml還有XBASE,ORACLE很是早已經支持這種存儲方式了

光知道這些NOSQL的名詞是沒實用的。關鍵在於要知道在哪一種場景下選用哪一種NOSQL纔是咱們真正要去掌握的。

咱們這邊說Redis就拿Redis說事吧,它能幹什麼呢?


Redis基礎應用場景

  • web間session共享,即多個warproject共享一個session
  • 分佈式緩存,因爲redis爲鍵值對。並且它提供了豐富的adapter可以支持到C、.net、java客戶端,所以對於異質平臺間進行數據交換起到了做用,所以它可以用做大型系統的分佈式緩存。並且其setnx的鎖常被用於」秒殺「。」搶紅包「這種電商活動場景中。


安裝Redis

我原本想在這兒寫」Redis上的‘坑‘「。最後我仍是認爲把它放到後面章節中去寫吧,因爲中國人的思惟是先有感性再有理性的一種逆向思惟。事實上這點很是像美國人。所以中國人在世界上是最聰明的民族之中的一個,因此咱們仍是先從動手搭一個Redis的環境來講起吧,老規矩。 紅色加粗很是重要。


必定要使用Linux來佈署Redis,請不要偷懶使用Redis 2.8.1 for windows那個版本號,假設你使用了這個版本號你將沒法跟上這一系列教程的步伐。

因爲Redis爲GCC+這種東西開發出來的,它天生就是執行在LINUX/Unix環境下的。而那個windows版的Redis是一個」煙「割版。並且是一個unofficial的版本號非官方受權的哈。


先從Docker開始

假設已經有Linux/Unix環境的同協們可以直接跳過這一章。

咱們這邊要開始變態了,因爲咱們要真正開始踏上SOA、PAAS、互聯網的腳步了。


假設對於沒有Linux/Unix環境的用戶來講,我在這邊推薦使用docker。即boot2docker windows版來安裝,它下載後是一個這種文件


安裝前把你的網絡鏈接中的IPV6協議前的勾去掉

雙擊它。在安裝時記得選擇Virtual-Box選項。因爲docker本爲linux/unix下之物,所以爲了在windows下使用docker。boot2docker內嵌了一個virtualbox來虛擬docker的環境。

裝完後它會在你的桌面上生成一個藍色的圖標,雙擊它,它會打開一個綠色的字,黑色的背景像matrix電影裏的那種命令行窗體,這就是Docker。

裝完後執行:
docker@boot2docker:~$ docker run hello-world 

看到如下這些提示


Hello from Docker. 

This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps: 
1. The Docker client contacted the Docker daemon. 
2. The Docker daemon pulled the 「hello-world」 image from the Docker Hub. 
(Assuming it was not already locally available.) 
3. The Docker daemon created a new container from that image which runs the 
executable that produces the output you are currently reading. 
4. The Docker daemon streamed that output to the Docker client, which sent it 
to your terminal.

To try something more ambitious, you can run an Ubuntu container with: 
$ docker run -it ubuntu bash

For more examples and ideas, visit: http://docs.docker.com/userguide/

說明你的Docker成功安裝了。

在Docker中安裝unix環境


有了Docker咱們就用Docker虛擬一個Ubuntu(UNIX)環境吧,在這邊咱們使用的是Ubuntu14。

ubuntu14請下載這個包: 戳: 下載Ubuntu14包

下載後直接在docker下執行如下這條命令:

cat ubuntu-14.04-x86_64.tar.gz |docker import - ubuntu:ubuntu14

這個過程會很是快,完畢後查看本身的image:



成功導入了ubuntu,這樣咱們就可以在docker中執行出一個本身的ubuntu了。


docker run -i -t ubuntu:ubuntu14 /bin/bash

以上執行後,進入了該ubuntu的bash環境。


注:假設上述命令出錯。可以使用如下這條命令:

docker run -i -t ubuntu:ubuntu14 //bin/bash

兩個 「/」 

假設你能看到類似於root@ubuntu14_這種命令行界面說明你的ubuntu14也已經成功安裝了。如下咱們就要在這個docker->ubuntu14中安裝和佈署咱們的Redis了,這個過程和在linux下同樣。


在ubuntu14下先安裝SSHD。以便於咱們使用WINSCP這種SFTP工具來管理咱們的ubuntu14中的文件系統


在ubuntu14中安裝SSHD

第一步:

docker run -t -i ubuntu/mk:v1 /bin/bash


進入咱們的ubuntu環境,這邊的ubuntu/mk就是我本機的docker中ubuntu14 container(容器)的名字,假設依照上面的延續此處可以替換成ubuntu:ubuntu14這個名字吧。


第二步:

升級一下你的apt-get,它就是一個命令行IE下載工具,假設你不update,那麼你apt-get的源、內核都爲舊的,所以爲了升級apt-get請鍵入如下的命令

apt-get update


這個過程很是快(依賴於你的網絡環境)


第三步:


下載和安裝openssh組件

apt-get install openssh-server openssh-client


第四步:


改動你的rootpassword

passwd


鍵入兩次你的rootpassword,我這邊都爲6個小寫的a


第五步:

退出容器,並保存以上改動,假設docker在退出後你接着退出docker環境或者是關機那麼剛纔的4步所有不生效,你必定要commit它才幹生效,爲此:

  • 你先要知道你剛纔用docker run命令執行的ubuntu14的容器的ID。你可以使用
    docker ps -a
    來查到你latest的一次容器的ID,它是一組16進制同樣的編碼如:1edfb9aabde8890,有了這個container id咱們就可以commit咱們剛纔裝的openssh的環境了
  • commit剛纔在容器中所作的改動
    docker commit 1edfb9aabde8890 ubuntu:ssh

第六步:

執行帶有openssh的ubuntu14以便於咱們使用winscp這種SFTP工具連入咱們的ubuntu14中去。依次輸入如下的命令:

docker kill $(docker ps -q)


殺掉正在執行的所有的container的進程

docker rm $(docker ps -a -q)


刪除所有在進程中的容器,以上2步又被稱爲docker大掃除


Docker是這種機制的,它可以開啓多個容器,每個容器帶着一堆的image(鏡像)。要刪一個鏡像必須先中止這個鏡像所在的容器,再把這個鏡像刪除,所以咱們使用上面這兩條命令對於Docker來一個大掃除。


接着咱們先查一下咱們眼下手頭有的鏡像

docker images
你會看到一個images列表。裏面有咱們的ubuntu:14,有咱們的ubuntu:ssh也有一個hello-world,咱們把ubuntu:14這個鏡像刪了吧(爲了保持乾淨哈)


每個image也它本身的id。即image id,所以你用docker images命令查到該鏡像的id後可以使用:


docker rmi imageid
這條命令把一個不用的鏡像給刪了。


接下去咱們要啓動咱們的ubuntu14:ssh了,可以使用如下這條命令:

docker -d -p 122:22 ubuntu:ssh //usr/sbin/sshd -D

這條命令的意思爲:

  • -d即把咱們的image啓動在後臺進程,它將會是一個daemon進程。而不會像剛纔咱們使用-t同樣,一旦exit後該image進程也本身主動退出了
  • -p爲端口映射,什麼意思呢,這邊要說一下docker的端口映射問題。

    咱們知道docker安裝後它會利用virtualbox中的vhost only的nat機制來創建一個虛擬的IP

可以打開咱們的virtualbox中在菜單」全局->設定->網絡」中進行查找


因此咱們可以知道一旦boot2docker環境執行後它的地址爲192.168.56.*這個段。通常爲192.168.56.101這個地址,你可以在boot2docker啓動後直接使用winscp邊入這個docker環境。


地址:192.168.56.101
端口:22
username:docker
password:tcuser

以上爲默認值,詳細地址依照你的virtualbox中在boot2docker安裝時本身主動給出的設置來作參考。


而,
咱們在這個docker中安裝了一個ubuntu14:ssh的image。而後用後臺進程的方式打開了這個ubuntu14:ssh,所以它本身也有一個IP(多是172也多是169段),詳細不得而知,通常來講它是每次啓動鏡像後本身變換的(可以使用動態網絡域名綁定docker中鏡像的ip來達到域名不變的目的-集羣環境下實用)。


咱們都知道ssh是以端口22來進行TCP鏈接的,所以咱們把ubuntu14的IP上的22端口映射到了咱們的docker主機192.168.56.101上的122端口。


  • 參數//usr/sbin/sshd -D表明該鏡像啓動會的entrypoint即啓動後再啓動一個什麼命令,在最後的-D(大寫的D)告訴docker這是一個啓動文件
因而,一旦該命令發出後,顯示image啓動的提示後(啓動後你會獲得一個image id)你就可以直接打開你的winscp使用:


地址:192.168.56.101
端口:122 (此處是122,不是22。因爲咱們把image的22端口映射到了192.168.56.101-docker主機上的122端口了)
username:root
password:aaaaaa

即可以連入咱們的ubuntu14環境了,假設此時你安裝了putty還可以使用putty+winscp直接進入ubuntu14的命令行環境中去,因而你就有ubuntu14的試驗環境了。






在ubuntu14下安裝redis

網上很是多在ubuntu14下安裝redis的教程都不正確的,你們看了要上當的。緣由在於例如如下,請各位看完:

  1. 網上的redis環境搭建直接使用的是apt-get update完後用wget https://github.com/ijonas/dotfiles/raw/master/etc/init.d/redis-server 這種方式來安裝的,這樣裝當然方便,可是也因爲方便因此取到的redis不是最新的redis版本號,通常爲2.8.x版或者是redis3.0.rc,這依賴於你的unit/linux所鏈接的wget庫
  2. redis爲c寫成,它的2.4-2.8版都爲不穩定版或者是缺乏功能或者是有bug,而這些bug在你假設真正使用redis做爲站點生產環境時將會因爲這些bug而沒法面對峯涌而來的巨大併發。所以當有這種redis執行了一段時間後你的生產環境會面臨着巨大的壓力
  3. 仍是redis不夠新不夠穩定的緣由,因爲在redis3前redis還不支持集羣、主備高可用方案的功能,所以不得不依靠於繁雜的打補丁式的如:linux/unix-keepalive或者是haproxy這種系統級層面而後寫一堆的複雜腳本去維護你的redis集羣。還要用外部手段(Linux/Unix Shell腳本)去維護多個redis節點間的緩存數據同步。。。這這這。。。

    不復合咱們的站點擴容、增量、運維和麪對巨大用戶(萬級併發-最高支持百萬用戶如:新浪微博、微信)的場景

所以,我在這邊推薦你們使用如下我將要使用的「下載源代碼包結合你本機的Linux/Unix內核進行實時編譯」的安裝過程。


第一步:下載redis眼下最穩定版本號也是功能最無缺,集羣支持最好並添加了sentinel(哨兵-高可用)功能的redis3.0.7版即redis-stable版,爲此咱們需要獲取redis-stable版

就是用的這個redis-stable.tar.gz包。這是我在寫博客時眼下最新最穩定版本號,修復了大量的BUG和無缺了功能。

第二步:

下載後咱們把該包上傳到咱們的docker中的ubuntu14中,咱們把它放在/opt文件夾下

而後咱們使用tar -zxvf redis-stable.tar.gz對它進行解壓


解壓後它就會生成一個redis-stable文件夾。進入該文件夾 cd redis-stable


別急,咱們先一會編譯和安裝它


第三步:編譯安裝redis

咱們先輸入gcc -v 這個命令來查看咱們的gcc版本號,假設它低於4.2如下那麼你在編譯redis3.0.7時必定會碰到大量的出錯信息,如前面所述。redis爲gcc寫成,最新的redis需要gcc4.2-5這個版本號才幹進行編譯,而通常去年或者以前裝的linux/unix 的 gcc都爲4.0如下或者甚至是3.x版。

升級GCC先

apt-get install build-essential


所以apt-get update顯得很是重要。要否則你獲取的gcc也將不是最新的版本號。眼下個人gcc爲5.3.1爲這周剛作的升級。


升級後咱們開始編譯redis3.0.7了,爲此咱們需要在redis-stable文件夾下


鍵入例如如下命令:

make PREFIX=/usr/local/redis1 install

咱們告知咱們的GCC把redis-stable編譯並同一時候安裝在/usr/local/redis1文件夾下


這個過程很是快。可能僅僅有10秒鐘時間(根據你的機器來講,建議使用>=8gb, 4核CPU的PC機),而後咱們就可以看到everything ok了。

咱們進入/usr/local/redis1就可以看到咱們剛纔安裝的redis3.0.7穩定版了。


咱們進入咱們的redis文件夾 cd /usr/local/redis1/bin


在此文件夾下咱們即可以執行咱們的redis server了,只是請別急。在啓動前咱們需要對redis進行一些配置。


個人博客面對的是「全棧式」project師的,架構師僅僅是成爲全棧式project師中的一個起點。假設你不會搭環境那麼你就不能接觸到最新的技術,所以這就是不少程序猿工做了近5年。7年結果發覺也僅僅會一個SSH的主要緣由。

Redis3配置要領

使用winscp經過122連入docker下的ubuntu14,進行redis的配置。

咱們需要編輯的文件爲/usr/local/redis1/bin/redis.conf這個文件

daemonize yes

# When running daemonized, Redis writes a pid file in /var/run/redis.pid by
# default. You can specify a custom pid file location here.
pidfile "/var/run/redis/redis1.pid"

# Accept connections on the specified port, default is 6379.
# If port 0 is specified Redis will not listen on a TCP socket.
port 7001
咱們把:

  1. daemonize設爲yes。使得redis之後臺進程的方式來執行,你可以認爲爲「server」模式,假設redis以server模式執行的話它會生成一個pid文件 。所以咱們把它的路徑放在/var/run/redis文件夾中,並命名它爲redis1.pid文件 。爲此你需要在/var/run文件夾下創建redis這個文件夾
  2. 端口號咱們把它設爲7001。這樣好辯識。因爲未來咱們會進一步作redis集羣,因此咱們的redis都爲redis1, redis2, redis3那麼咱們的端口號也爲7001, 7002, 7003。

    。。

    這樣來延續。

    那麼很是多同協這時要問了。「爲何咱們不把它命名成master, slave1, slave2這種名字呢?」。理由很是easy,無論是現在的hadoop仍是zookeeper它們的集羣是跨機房的。多個master間也有MASTER-SLAVE模式互爲備份。因爲一些大型站點不只僅僅僅有一個IDC機房,它們通常都會有2個,3個IDC機房,或者是在同一個IDC機房中有「跨機櫃」的佈署來造成超大規模集羣,就和ALI的TAOBAO網同樣,它在北美都有機房,所以當你需要在LOCAL NATIVE建一個IDC機房。在北美再作一個機房,你不要想把一個MASTER設在中國。SLAVE設到美國去,而是多地甚至是多機櫃都有MASTER。一旦一個MASTER宕機了,這種集羣會經過一個叫「選舉策略」選出一個節點把這個節點做爲當前「羣」的MASTER,所以咱們的命名纔會是redis1, redis2, redis3...這樣來命名的。



此處把原來的:
save 900 1
save 300 10
save 60 10000
中的300 10 和60 10000凝視掉。這邊表明的是:

redis以每900秒寫一次、300秒寫10次,60秒內寫1萬次這種策略把緩存放入一個叫.rdb的磁盤文件裏。這點和ehcache或者是memcache很是像。以便於redis在從新啓動時可以從本地持久化文件裏找出關機前的數據記錄。

假設依照默認的話,此三個策略會輪流起效,在大併發環境中。這種寫策略將會對咱們的性能形成巨大的影響,所以咱們這邊僅僅保留900秒寫1次這條策略, 這邊有人會問,假設你這樣會有數據丟失怎麼辦

。。別急。這個問題咱們後面會解答,這涉及到redis的「正確」使用,假設它僅僅是一個緩存。我相信5分鐘內緩存的丟失此時程序直接訪問數據庫也不會有太大問題。又要保證數據完整性又要保證性能這自己是一個矛與盾的問題,除非你錢多了燒那我會給出你一個燒錢的配置策略,連新浪都不會這麼燒錢。呵呵。


  • dbfilename。此處咱們維持redis原有的緩存磁盤文件的原名
  • dir "/usr/local/redis1/data"爲rdb文件所在的文件夾
這邊你們要注意的是一個是僅僅能寫文件名稱,還有一個地方僅僅能寫文件夾名。
爲此咱們需要在/usr/local/redis1下創建 data文件夾。



把此處的appendonly設爲no,這樣咱們就關閉了Redis的AOF功能。


  • AOF 持久化記錄服務器執行的所有寫操做命令,並在服務器啓動時,經過又一次執行這些命令來還原數據集。AOF是redis在集羣或者是高可用環境下的一個同步策略。它會不斷的以APPEND的模式把redis的緩存中的數據從一個節點寫給還有一個節點,它對於數據的完整性保證是要高於rdb模式的。
  • RDB 是一個很是緊湊(compact)的文件,它保存了 Redis 在某個時間點上的數據集。 這種文件很是適合用於進行備份: 比方說,你可以在近期的 24 小時內,每小時備份一次 RDB 文件,並且在每個月的每一天,也備份一個 RDB 文件。 這種話,即便趕上問題。也可以隨時將數據集還原到不一樣的版本號。

    RDB 很是適用於災難恢復(disaster recovery):它僅僅有一個文件,並且內容都很是緊湊。可以(在加密後)將它傳送到別的數據中心如阿里的mysql異地機房間使用FTP傳binlog的作法。

依照官方的說法,啓用AOF功能,可以在redis高可用環境中假設發生了故障客戶的數據不會有高於2秒內的歷史數據丟失,它換來的代價爲高昂的I/O開銷,有些開發人員爲了追求緩存中的數據100%的正確有時會碰到因爲redis在AOF頻繁刷新時整個環境如死機一的狀況,並且你會看到惡夢通常的」Asynchronous AOF fsync is taking too long 「警告信息。 這是因爲redis它是單線程的,它在進行I/O操做時會堵塞住所有的操做,包含登陸。。。這個很是可怕。只是這個BUG/ISSUE已經在最新redis中進行了優化,它啓用了還有一根進程來進行AOF刷新。包含優化了RDB持久化功能, 這也是爲何我讓你們必定必定要用最新最穩定版的redis的緣由

通常默認狀況下redis內的rdb和AOF功能同爲開啓。

假設RDB的數據不實時。同一時候使用二者時服務器從新啓動也僅僅會找AOF文件。

因爲RDB文件僅僅用做後備用途,建議僅僅在Slave上持久化RDB文件,並且僅僅要15分鐘備份一次就夠了,因此我僅僅保留save 900 1這條規則。


假設Enalbe AOF:

  • 優勢是在最惡劣狀況下也僅僅會丟失不超過兩秒數據。啓動腳本較簡單僅僅load本身的AOF文件就可以了。

  • 代價一是帶來了持續的IO。二是AOF rewrite的最後將rewrite過程當中產生的新數據寫到新文件形成的堵塞差點兒是不可避免的。僅僅要硬盤許可,應該儘可能下降AOF rewrite的頻率。AOF重寫的基礎大小默認值64M過小了,可以設到5G以上。

    默認超過原大小100%大小時重寫。這邊可以設定一個適當的數值。



假設不Enable AOF ,僅靠Master-Slave Replication 實現高可用性也可以。能省掉極大的IO也下降了rewrite時帶來的系統波動。代價是假設Master/Slave同一時候倒掉(那你的站點基本也就歇了)。會丟失十幾分鐘的數據,啓動腳本也要比較兩個Master/Slave中的RDB文件,加載較新的那個。新浪微博就選用了這種架構。





最後咱們不要忘了設一個redis的log文件,在此咱們把它設到了/var/log/redis文件夾,爲此咱們需要在/var/log文件夾下創建一個redis文件夾。

好了,保存後咱們來啓動咱們的redis吧。

咱們使用如下這條命令來啓動咱們的redis server。




而後咱們在咱們的windows機上裝一個windows版的redis 2.8.1 for windows(僅僅用它來做爲redis的client端)

而後咱們在windows環境下使用:

redis-cli -p 7001 -h 192.168.56.101

咦,沒反映。連不上。哈哈。。

。。。。


那是確定連不上的,因爲:

  1. 咱們剛纔在用docker啓動ubuntu14時使用docker -d -p 122:22 ubuntu:ssh //usr/sbin/sshd -D來啓動的,這邊咱們並未把redis服務的7001端口映射到192.168.56.101這臺docker主機上。怎麼可以經過windows主機(可能windows的ip爲169.188.xx.xx)來訪問docker內的進程服務呢?對吧,爲此咱們:先把剛纔作了這麼多的更改docker commit成一個新的image如:redis:basic吧。
  2. 而後咱們對docker進行一次大掃除,而後咱們啓動redis:basic這個image並使用如下命令:
docker -d -p 122:22 -p 7001:7001 redis:basic //usr/sbin/sshd -D

看,此處咱們可以使用多個-p來做docker內容器的多端口映射策略(它事實上使用的就是iptables命令)。

好了,用putty連入這個image的進程並啓動redis服務。而後咱們拿windows中的redis-cli命令來連。


假設在linux環境下仍是沒有連通(可能的哦),那是因爲你沒有禁用linux下的防火牆。咱們可以使用iptables -F來禁用linux的防火牆或者使用:


vi /etc/selinux/config

而後把

SELINUX=enforcing     這句用」#「凝視掉
添加一句: SELINUX=disabled  #添加

這樣每次啓動後linux都不會有iptables的困擾了(這是在本機環境下這麼幹哦,假設你是生產環境請自行加iptables策略以贊成redis服務端口可以被訪問)。


看到如下這個PONG即表明你的redis服務已經在網絡環境中起效了。



如下咱們要開始使用JAVA客戶端來連咱們的Redis Service了。


使用Spring Data + JEDIS來鏈接Redis Service

Spring+Session+Redis

pom.xml

在此咱們需要使用spring data和jedis,如下給出相關的maven配置

	<dependencies>
		<!-- poi start -->
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi</artifactId>
			<version>${poi_version}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi-ooxml-schemas</artifactId>
			<version>${poi_version}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi-scratchpad</artifactId>
			<version>${poi_version}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi-ooxml</artifactId>
			<version>${poi_version}</version>
		</dependency>
		<!-- poi end -->
		<!-- active mq start -->
		<dependency>
			<groupId>org.apache.activemq</groupId>
			<artifactId>activemq-all</artifactId>
			<version>5.8.0</version>
		</dependency>

		<dependency>
			<groupId>org.apache.activemq</groupId>
			<artifactId>activemq-pool</artifactId>
			<version>${activemq_version}</version>
		</dependency>

		<dependency>
			<groupId>org.apache.xbean</groupId>
			<artifactId>xbean-spring</artifactId>
			<version>3.16</version>
		</dependency>
		<!-- active mq end -->

		<!-- servlet start -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>${javax.servlet-api.version}</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet.jsp</groupId>
			<artifactId>jsp-api</artifactId>
			<version>2.1</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
		<!-- servlet end -->

		<!-- redis start -->
		<dependency>
			<groupId>redis.clients</groupId>
			<artifactId>jedis</artifactId>
			<version>2.7.2</version>
		</dependency>
		<dependency>
			<groupId>org.redisson</groupId>
			<artifactId>redisson</artifactId>
			<version>1.0.2</version>
		</dependency>
		<!-- redis end -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>jcl-over-slf4j</artifactId>
			<version>${slf4j.version}</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>${slf4j.version}</version>
		</dependency>

		<!-- spring conf start -->
		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-redis</artifactId>
			<version>1.6.2.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.version}</version>
			<exclusions>
				<exclusion>
					<groupId>commons-logging</groupId>
					<artifactId>commons-logging</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context-support</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-redis</artifactId>
			<version>1.6.2.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jms</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.session</groupId>
			<artifactId>spring-session</artifactId>
			<version>${spring.session.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<!-- spring conf end -->
	</dependencies>


redis-config.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:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="  
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

	<context:property-placeholder location="classpath:/spring/redis.properties" />
	<context:component-scan base-package="org.sky.redis">
	</context:component-scan>

	<bean id="jedisConnectionFactory"
		class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
		<property name="hostName" value="${redis.host.ip}" />
		<property name="port" value="${redis.host.port}" />
		<property name="poolConfig" ref="jedisPoolConfig" />
	</bean>

	<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
		<property name="maxTotal" value="${redis.maxTotal}" />
		<property name="maxIdle" value="${redis.maxIdle}" />
		<property name="maxWaitMillis" value="${redis.maxWait}" />
		<property name="testOnBorrow" value="${redis.testOnBorrow}" />
		<property name="testOnReturn" value="${redis.testOnReturn}" />
	</bean>
	<bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
		<property name="connectionFactory" ref="jedisConnectionFactory" />
	</bean>

	<!--將session放入redis -->
	<bean id="redisHttpSessionConfiguration"
		class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
		<property name="maxInactiveIntervalInSeconds" value="1800" />
	</bean>
	<bean id="customExceptionHandler" class="sample.MyHandlerExceptionResolver" />
</beans> 


redis.properties

redis.host.ip=192.168.0.101
redis.host.port=6379
  

redis.maxTotal=1000  
redis.maxIdle=100
redis.maxWait=2000
redis.testOnBorrow=false
redis.testOnReturn=true


web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
	<!-- - Location of the XML file that defines the root application context 
		- Applied by ContextLoaderListener. -->
	<!-- tag::context-param[] -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
			classpath:/spring/redis-conf.xml
		</param-value>
	</context-param>
	<!-- end::context-param[] -->

	<!-- tag::springSessionRepositoryFilter[] -->
	<filter>
		<filter-name>springSessionRepositoryFilter</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>springSessionRepositoryFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	<session-config>
		<session-timeout>30</session-timeout>
	</session-config>
	<!-- end::springSessionRepositoryFilter[] -->
	<filter>
		<filter-name>encodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
		<init-param>
			<param-name>forceEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>

	<filter-mapping>
		<filter-name>encodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

	<servlet>
		<servlet-name>dispatcher</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:/spring/spring-mvc.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>dispatcher</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>

	<!-- - Loads the root application context of this web app at startup. - 
		The application context is then available via - WebApplicationContextUtils.getWebApplicationContext(servletContext). -->
	<!-- tag::listeners[] -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	<!-- end::listeners[] -->

	<servlet>
		<servlet-name>sessionServlet</servlet-name>
		<servlet-class>sample.SessionServlet</servlet-class>
	</servlet>

	<servlet-mapping>
		<servlet-name>sessionServlet</servlet-name>
		<url-pattern>/servlet/session</url-pattern>
	</servlet-mapping>

	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>

</web-app>

這邊主要是一個:

        <filter>
		<filter-name>springSessionRepositoryFilter</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>springSessionRepositoryFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	<session-config>
		<session-timeout>30</session-timeout>
	</session-config>
這個filter必定要寫在一切filter以前


SessionController

package sample;

import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

/**
 * Created by mk on 15/1/7.
 */
@Controller
@EnableRedisHttpSession
public class SessionController {
	@RequestMapping("/mySession")
	public String index(final Model model, final HttpServletRequest request) {
		if (request.getSession().getAttribute("testSession") == null) {
			System.out.println("session is null");
			request.getSession().setAttribute("testSession", "yeah");
		} else {
			System.out.println("not null");
		}
		return "showSession";
	}

}

showSession.jsp文件

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>showSession</title>
</head>
<body>
<%
	String sessionValue=(String)session.getAttribute("testSession");
%>

<h1>Session Value From Servlet is: <%=sessionValue%></h1>
</body>
</html>

測試

保證咱們的redise-server是啓動的。而後咱們啓動起這個webproject後使用:

http://localhost:8080/webpoc/mySession訪問一下這個controller

此時咱們使用redis客戶端工具連入查看spring session是否已經進入到了redis中去。

在redis客戶端工具連入後咱們可以在redis console中使用keys *來查看存入的key。LOOK,spring的session存入了redis中去了。


再來看咱們的eclipse後臺,因爲咱們是第一次訪問這個controller。所以這個session爲空。所以它顯演示樣例如如下:


咱們在IE中再次訪問該controller


因爲以前的session已經存在於redis了,所以當用戶在1800秒(30分鐘)內再次訪問controller。它會從session中獲取該session的key testSession的值,所以eclipse後臺打印爲not null。

SpringRedisTemplate + Redis

講過了spring session+redis咱們來說使用spring data框架提供的redisTemplate來訪問redis service吧。說實話。spring這個東西真強,什麼都可以集成,cassandra, jms, jdbc...jpa...bla...bla...bla...Spring集成Barack Hussein Obama? LOL :)


pom.xml

不用列了。上面有了

redis-conf.xml

不用列了,上面有了

web.xml

也不用列了,上面也有了

SentinelController.java

咱們就先用這個名字吧,後面咱們會用它來作咱們的redis sentinel(哨兵)的高可用(HA)集羣測試

package sample;

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisSentinelPool;
import util.CountCreater;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

/**
 * Created by xin on 15/1/7.
 */
@Controller
public class SentinelController {

	@Autowired
	private StringRedisTemplate redisTemplate;

	@RequestMapping("/sentinelTest")
	public String sentinelTest(final Model model,
			final HttpServletRequest request, final String action) {
		return "sentinelTest";
	}

	@ExceptionHandler(value = { java.lang.Exception.class })
	@RequestMapping("/setValueToRedis")
	public String setValueToRedis(final Model model,
			final HttpServletRequest request, final String action)
			throws Exception {
		CountCreater.setCount();
		String key = String.valueOf(CountCreater.getCount());
		Map mapValue = new HashMap();
		for (int i = 0; i < 1000; i++) {
			mapValue.put(String.valueOf(i), String.valueOf(i));
		}
		try {
			BoundHashOperations<String, String, String> boundHashOperations = redisTemplate
					.boundHashOps(key);
			boundHashOperations.putAll(mapValue);
			System.out.println("put key into redis");
		} catch (Exception e) {
			e.printStackTrace();
			throw new Exception(e);
		}
		
		return "sentinelTest";
	}


}

打開IE,輸入:http://localhost:8080/webpoc/setValueToRedis

觀察咱們的後臺


而後使用redis client連入後進行查看


看。。。這個值key=1的,就是咱們經過spring的redisTemplate存入進去的值,即便用如下這段代碼進行存入的值:

 for (int i = 0; i < 1000; i++) {
	mapValue.put(String.valueOf(i), String.valueOf(i));
}
try {
	BoundHashOperations<String, String, String> boundHashOperations = redisTemplate.boundHashOps(key);
	boundHashOperations.putAll(mapValue);


怎樣你要存入一個簡單的如key=test value=hello。你可以這樣使用你的redisTemplate

redisTemplate.execute(new RedisCallback<Object>() {

			@Override
			public Object doInRedis(RedisConnection connection)
					throws DataAccessException {
				connection.set(
						redisTemplate.getStringSerializer().serialize(
								"test"), redisTemplate
								.getStringSerializer()
								.serialize("hello"));
				return null;
			}
});
是否是很是方便的哈?結束第一天的教程,明天開始搭建redis集羣。
相關文章
相關標籤/搜索