转自:http://blog.csdn.net/lifetragedy/article/details/50628820
原作者写的很好,尊重作者的劳动成果。
进入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。
下载后直接在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请键入下面的命令
这个过程很快(依赖于你的网络环境)
第三步:
下载和安装openssh组件
- apt-get install openssh-server openssh-client
第四步:
修改你的root密码
键入两次你的root密码,我这边都为6个小写的a
第五步:
退出容器,并保存以上修改,如果docker在退出后你接着退出docker环境或者是关机那么刚才的4步全部不生效,你一定要commit它才能生效,为此:
- 你先要知道你刚才用docker run命令运行的ubuntu14的容器的ID,你可以使用
来查到你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来一个大扫除。
接着我们先查一下我们目前手头有的镜像
你会看到一个images列表,里面有我们的ubuntu:14,有我们的ubuntu:ssh也有一个hello-world,我们把ubuntu:14这个镜像删了吧(为了保持干净哈)
每个image也它自己的id,即image id,因此你用docker images命令查到该镜像的id后可以使用:
这条命令把一个不用的镜像给删了。
接下去我们要启动我们的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的教程都不对的,大家看了要上当的,原因在于如下,请各位看完:
- 网上的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库
- redis为c写成,它的2.4-2.8版都为不稳定版或者是缺少功能或者是有bug,而这些bug在你如果真正使用redis作为网站生产环境时将会因为这些bug而无法面对峰涌而来的巨大并发,因此当有这样的redis运行了一段时间后你的生产环境会面临着巨大的压力
- 还是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
我们把:
- daemonize设为yes,使得redis以后台进程的方式来运行,你可以认为为“server”模式,如果redis以server模式运行的话它会生成一个pid文件 ,因此我们把它的路径放在/var/run/redis目录中,并命名它为redis1.pid文件 ,为此你需要在/var/run目录下建立redis这个目录
- 端口号我们把它设为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
咦,没反映,连不上,哈哈。。。。。。
那是肯定连不上的,因为:
- 我们刚才在用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吧。
- 然后我们对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配置
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>
-
-
- <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. -->
-
- <context-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>
- classpath:/spring/redis-conf.xml
- </param-value>
- </context-param>
-
-
-
- <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-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). -->
-
- <listener>
- <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
- </listener>
-
-
- <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;
-
-
-
-
- @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;
-
-
-
-
- @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集群