親密接觸Redis-第一天

引言

nosql,大規模分佈式緩存遍天下,Internet的時代在中國由其走得前沿,這一切歸功於我國特點的電商。所以nosql、大數據技術在中國應用的比國外還要前沿。從這一章開始咱們將開始進入到真正的SOA、PAAS、SAAS、互聯網的領域,所以每一篇我都會加入一小段業務的基礎知識,讓你們在學習技術的同時也能夠了解一些業務,這邊的業務不是指的business logic不是讓你們去作業務人員,而是爲你們帶來IDEA,」沒有作不到只有想不到「,阿里支付寶爲何發了。。。不是技術,而是它的IDEA。html


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

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


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


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


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


」大家有沒有雙11,雙12,有沒有交易量過億。。。「程序員


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


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


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


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


還有就是國外基本無」不是名牌「這一說,記得去法國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共享,即多個war工程共享一個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


第四步:


修改你的root密碼

passwd


鍵入兩次你的root密碼,我這邊都爲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
用戶名:docker
密碼: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端口了)
用戶名:root
密碼: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進行一些配置。


個人博客面對的是「全棧式」工程師的,架構師只是成爲全棧式工程師中的一個起點,若是你不會搭環境那麼你就不能接觸到最新的技術,所以這就是許多程序員工做了近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這樣的名字呢?」,理由很簡單,不管是如今的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是啓動的,而後咱們啓動起這個web工程後使用:

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集羣。
相關文章
相關標籤/搜索