亲热接触Redis-第一天
引言
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共享,即多个warproject共享一个session
- 分布式缓存,由于redis为键值对。而且它提供了丰富的adapter能够支持到C、.net、java客户端,因此对于异质平台间进行数据交换起到了作用,因此它能够用作大型系统的分布式缓存。而且其setnx的锁常被用于”秒杀“。”抢红包“这种电商活动场景中。
安装Redis
由于Redis为GCC+这种东西开发出来的,它天生就是执行在LINUX/Unix环境下的。而那个windows版的Redis是一个”烟“割版。而且是一个unofficial的版本号,非官方授权的哈。
先从Docker開始
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />
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:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />
成功导入了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
- 參数//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
因此apt-get update显得非常重要。要不然你获取的gcc也将不是最新的版本号。眼下我的gcc为5.3.1为这周刚做的升级。
升级后我们開始编译redis3.0.7了,为此我们须要在redis-stable文件夹下
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />
键入例如以下命令:
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稳定版了。
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />
我们进入我们的redis文件夹 cd /usr/local/redis1/bin
在此文件夹下我们即能够执行我们的redis server了,只是请别急。在启动前我们须要对redis进行一些配置。
我的博客面对的是“全栈式”project师的,架构师仅仅是成为全栈式project师中的一个起点。假设你不会搭环境那么你就不能接触到最新的技术,因此这就是很多程序猿工作了近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这种名字呢?”。理由非常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的“正确”使用,假设它仅仅是一个缓存。我相信5分钟内缓存的丢失此时程序直接訪问数据库也不会有太大问题。又要保证数据完整性又要保证性能这本身是一个矛与盾的问题,除非你钱多了烧那我会给出你一个烧钱的配置策略,连新浪都不会这么烧钱。呵呵。
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />
- dbfilename。此处我们维持redis原有的缓存磁盘文件的原名
- dir "/usr/local/redis1/data"为rdb文件所在的文件夹
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />
- AOF 持久化记录服务器执行的全部写操作命令,并在服务器启动时,通过又一次执行这些命令来还原数据集。AOF是redis在集群或者是高可用环境下的一个同步策略。它会不断的以APPEND的模式把redis的缓存中的数据从一个节点写给还有一个节点,它对于数据的完整性保证是要高于rdb模式的。
- RDB 是一个非常紧凑(compact)的文件,它保存了 Redis 在某个时间点上的数据集。 这种文件非常适合用于进行备份: 比方说,你能够在近期的 24 小时内,每小时备份一次 RDB 文件,而且在每一个月的每一天,也备份一个 RDB 文件。 这种话,即使遇上问题。也能够随时将数据集还原到不同的版本号。
RDB 非常适用于灾难恢复(disaster recovery):它仅仅有一个文件,而且内容都非常紧凑。能够(在加密后)将它传送到别的数据中心如阿里的mysql异地机房间使用FTP传binlog的做法。
假设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>
这个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
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集群。
亲热接触Redis-第一天的更多相关文章
- 秒杀多线程第二篇 多线程第一次亲热接触 CreateThread与_beginthreadex本质差别
本文将带领你与多线程作第一次亲热接触,并深入分析CreateThread与_beginthreadex的本质差别,相信阅读本文后你能轻松的使用多线程并能流畅准确的回答CreateThread与_beg ...
- 亲热接触Redis-第二天(Redis Sentinel)
简单介绍 经过上次轻松搭建了一个Redis的环境并用Java代码调通后.这次我们要来看看Redis的一些坑以及Redis2.8以后带来的一个新的特性即支持高可用特性功能的Sentinel(哨兵). R ...
- 15天玩转redis —— 第一篇 开始入手
双十一终于还是过去了,我负责的mongodb由于做了副本集,最终还是挺过去了,同事负责的redis,还是遗憾的在早上8点左右宕机了,然后大家就是马不停 蹄的赶往公司解决问题,因为我对redis也不是很 ...
- redis第一讲【redis的描述,linux和docker下的安装使用】
Redis(REmote DIctionary Server):是什么 redis(远程字典服务器),是完全开源免费的,高性能的k/v分布式内存数据,热门的Nosql数据库 Redis可以干什么: 内 ...
- Redis | 第一部分:数据结构与对象 上篇《Redis设计与实现》
目录 前言 1. 简单动态字符串 1.1 SDS的定义 1.2 空间预分配与惰性空间释放 1.3 SDS的API 2. 链表 2.1 链表与节点的定义 2.2 链表的API 3. 字典 3.1 哈希表 ...
- Redis | 第一部分:数据结构与对象 下篇《Redis设计与实现》
目录 前言 1. Redis对象概述 1.1 对象的定义 2. 字符串对象 3. 列表对象 3.1 quicklist 快速链表 4. 哈希对象 5. 集合对象 6. 有序集合对象 7. Redis对 ...
- Redis | 第一部分:数据结构与对象 中篇《Redis设计与实现》
目录 前言 1. 跳跃表 1.1 跳跃表与其节点的定义 1.2 跳跃表的API 2. 整数集合 2.1 整数集合的实现 2.2 整数集合的类型升级 2.3 整数集合的API 3. 压缩列表 3.1 压 ...
- Redis第一篇(Redis单机版本安装及启动)
安装: 1 2 3 4 5 [root@M2_Redis1 ~]# yum install gcc gcc-c++ (安装依赖) [root@M2_Redis1 tools]# wget ht ...
- redis第一篇--综述
1 redis里边有数据库的概念.可分为1-255这些表.在存储或者查找的时候要指明. redis_sentinel 集群里边封装成了namespace这样的概念.与db是不一样的.
随机推荐
- 无线AP和无线路由器区别wifi热点
转自:http://network.51cto.com/art/201310/413327.htm 就像很多用户很容易混淆无线上网卡和无线网卡一样,很多用户也分不清无线AP和无线路由,小峰便是其中的一 ...
- 如何修改容器内的/etc/resolv.conf
源由不表,暂且略过. 直接说workaround. 因为openshift的模式,/etc/resolv.conf是在pod生成的时候插入的,写入的是宿主机的ip作为dns的寻址,如果需要修改的化,需 ...
- Liunx下文件权限详解
刚接触Linux时对Linux下的文件权限的概念一直很模糊,观念还一直停留在windows下,所以有很多操作一直提示权限不够.为了弄懂文件权限问题我查找了很多资料整理如下,我把这些学习笔记贴出来和大家 ...
- android 模拟器无法ping通主机
很多时候我们通过adb 连接 android 模拟器调试网络程序,也许你能直接访问浏览器,浏览网站,但是却无法ping同局网的一个机器,比如: # ping www.sina.com ...
- centos7 selenium.common.exceptions.WebDriverException: Message: 'chromedriver' executable needs to be in PATH
1.查看安装的chrome浏览器版本 2.查看版本对应的驱动 https://sites.google.com/a/chromium.org/chromedriver/downloads 下载后拷贝到 ...
- [Functional Programming] Use Task/Async for Asynchronous Actions
We refactor a standard node callback style workflow into a composed task-based workflow. Original Co ...
- (剑指Offer)面试题13:在O(1)时间内删除链表结点
题目: 在给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间内删除该结点.链表结点与函数的定义如下: struct ListNode{ int val; ListNode* next; } ...
- 最短路径算法(Dijkstra)
1.建立矩阵,记录任意两点间的直接距离: 2.两个集合,一个集合记录到每个点的最短路径,一个记录前驱节点: 3.主循环,每次找当前点与其他点的距离,记录下最短距离和前驱节点,然后看看通过前驱节点和最短 ...
- Python 默认参数
定义默认参数 定义函数的时候,还可以有默认参数. 例如Python自带的 int() 函数,其实就有两个参数,我们既可以传一个参数,又可以传两个参数: >>> int('123') ...
- bootstrap model弹出框的使用
之前,我们讲解了bootstrap tab的使用,今天我们来了解下bootstrap 中model弹出窗的使用. 效果: 代码:<input id="btntext" typ ...