来自http://blog.csdn.net/lifetragedy/article/details/50628820
引言
nosql,大规模分布式缓存遍天下,Internet的时代在中国由其走得前沿,这一切归功于我国特色的电商。因此nosql、大数据技术在中国应用的比国外还要前沿。从这一章开始我们将开始进入到真正的SOA、PAAS、SAAS、互联网的领域,因此每一篇我都会加入一小段业务的基础知识,让大家在学习技术的同时也可以了解一些业务,这边的业务不是指的business logic不是让大家去做业务人员,而是为大家带来IDEA,”没有做不到只有想不到“,阿里支付宝为什么发了。。。不是技术,而是它的IDEA。
业务基础知识-国内国外电商比较以及为什么电商技术在中国走得这么前沿
14年1月时在上海参加过一个MagentoCom召集的电商峰会。会上一群LAO WAI在那边大谈自己的电商经验,有LV,有ADIDAS,bla...bla...bla...听着听着,真是觉得好笑。休息时随便问一个LAO WAI一个问题:
”你们知道什么叫一元秒杀吗?“
”你们知道什么7天无理由退货吗?“
”你们知道什么叫0体验吗?“
”你们有没有双11,双12,有没有交易量过亿。。。“
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共享,即多个war工程共享一个session
- 分布式缓存,因为redis为键值对,而且它提供了丰富的adapter可以支持到C、.net、java客户端,因此对于异质平台间进行数据交换起到了作用,因此它可以用作大型系统的分布式缓存,并且其setnx的锁常被用于”秒杀“,”抢红包“这种电商活动场景中。
安装Redis
先从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环境
- 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
第二步:
升级一下你的apt-get,它就是一个命令行IE下载工具,如果你不update,那么你apt-get的源、内核都为旧的,因此为了升级apt-get请键入下面的命令
- apt-get update
第三步:
下载和安装openssh组件
- apt-get install openssh-server openssh-client
修改你的root密码
- passwd
第五步:
退出容器,并保存以上修改,如果docker在退出后你接着退出docker环境或者是关机那么刚才的4步全部不生效,你一定要commit它才能生效,为此:
- 你先要知道你刚才用docker run命令运行的ubuntu14的容器的ID,你可以使用
- docker ps -a
- commit刚才在容器中所做的修改
- docker commit 1edfb9aabde8890 ubuntu:ssh
运行带有openssh的ubuntu14以便于我们使用winscp这样的SFTP工具连入我们的ubuntu14中去,依次输入下面的命令:
- docker kill $(docker ps -q)
- docker rm $(docker ps -a -q)
Docker是这样的机制的,它可以开启多个容器,每个容器带着一堆的image(镜像),要删一个镜像必须先停止这个镜像所在的容器,再把这个镜像删除,因此我们使用上面这两条命令对于Docker来一个大扫除。
接着我们先查一下我们目前手头有的镜像
- docker images
每个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
- 参数//usr/sbin/sshd -D代表该镜像启动会的entrypoint即启动后再启动一个什么命令,在最后的-D(大写的D)告诉docker这是一个启动文件
在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节点间的缓存数据同步。。。这这这。。。不复合我们的网站扩容、增量、运维和面对巨大用户(万级并发-最高支持百万用户如:新浪微博、微信)的场景
就是用的这个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
升级后我们开始编译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配置要领
- 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
- dbfilename,此处我们维持redis原有的缓存磁盘文件的原名
- dir "/usr/local/redis1/data"为rdb文件所在的目录
- AOF 持久化记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集。AOF是redis在集群或者是高可用环境下的一个同步策略,它会不断的以APPEND的模式把redis的缓存中的数据从一个节点写给另一个节点,它对于数据的完整性保证是要高于rdb模式的。
- RDB 是一个非常紧凑(compact)的文件,它保存了 Redis 在某个时间点上的数据集。 这种文件非常适合用于进行备份: 比如说,你可以在最近的 24 小时内,每小时备份一次 RDB 文件,并且在每个月的每一天,也备份一个 RDB 文件。 这样的话,即使遇上问题,也可以随时将数据集还原到不同的版本。RDB 非常适用于灾难恢复(disaster recovery):它只有一个文件,并且内容都非常紧凑,可以(在加密后)将它传送到别的数据中心如阿里的mysql异地机房间使用FTP传binlog的做法。
因为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文件,载入较新的那个。新浪微博就选用了这种架构。
- 我们刚才在用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配置
- <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>
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
- 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);
- 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;
- }
- });