亲热接触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集群。
相关文章
相关标签/搜索