当前位置: 首页 > news >正文

用spring-data-redis实现类似twitter的网站(转)

1.  spring-data-redis简介 
封装了一下redis的客户端,使得使用起来更方便。 
优点是把客户端连接放到一个连接池里,从而提高性能。还有就是可以不同的客户端之间实现切换,而不用改一行代码(Spring惯用的一个手法)。 
本文写作时最新版是1.3, 
目前支持下面4种java客户端,可以自由切换而不用改代码。 
  • Jedis
  • JRedis
  • SRP
  • Lettuce


2. twitter简介 
twitter如果没用过的话,可以理解成类似于国内的新浪微博。因为微博的访问量和使用人数极大,用传统的关系型数据库支撑不了,所以有了用redis这种非关系型数据库的架构设计。 
本文我们将用spring-data-redis来实现一个类似twitter的网站。 

3. 环境 
本人亲测通过环境 
jdk 1.6.0_45 64bit 
gradle 1.7 
tomcat 7.0.35 
redis 2.8.11 win64 
jedis 2.5.1 
spring 3.2.9 
spring-data-redis 1.3.0 

4. 先动手操作 
4.1 首先git clone以下项目 
https://github.com/spring-projects/spring-data-keyvalue-examples/tree/master/retwisj 

4.2 本人做了一些修改,一些3pp改成本文写作时最新的版本 
build.gradle 
Java代码   收藏代码
  1. compile "redis.clients:jedis:2.5.1"  


gradle.properties 
Java代码   收藏代码
  1. springVersion = 3.2.9.RELEASE  
  2. springRedisVersion = 1.3.0.RELEASE  


4.3 编译 
Java代码   收藏代码
  1. gradle build  


4.4 将retwisj.war放入tomcat的webapp目录下,启动tomcat 

4.5 访问http://localhost:8080/retwisj 
花几分钟上去玩一下,熟悉一下功能。 
好了,我们已经做好了一个微博网站了。玩好了我们简单分析一下代码实现。 

5. 实现分析 

5.1 表结构设计 

5.1.1 用户(User) 

用以前的关系型数据库设计是这样的 
KeyUsernamePassword
1springrodinterface21
2costinlthis is fun


而用redis的则变成了这样 
KeyTypeValue
uid:1hash{name: springrod, pass: interface21}
uid:2hash{name: costinl, pass: secret}


将用户的名字和密码作为一个hash存入 
对应的redis原生命令是 
Java代码   收藏代码
  1. HMSET user:1 name springrod pass interface21  


uid是自增长的,可以用redis的INCR来实现,这是一个原子操作 
Java代码   收藏代码
  1. INCR global:uid  


KeyTypeValue
global:uidstring2


我们要保存username和uid的对应关系,比如一个user登录时,我们要得到他的uid。可以创建一个倒转的key来实现此功能。user:[name]:uid 
还创建了一个users用来保存所有uid的list 
KeyTypeValue
user:springrod:uidstring1
user:costinl:uidstring2
userslist{1, 2}


5.1.2 微博(Post) 

同样的,用global:pid来记录自增长的post id 
KeyTypeValue
global:pidstring2


将微博的内容、时间和作者作为一个hash存入 
KeyTypeValue
pid:1hash{content: Hello World, time: 1301931414757, uid: 1}
pid:2hash{content: Working on some cool stuff, time: 1301931414897, uid: 1}
pid:3hash{content: Checking out RetwisJ, time: 1301931454897, uid: 2}


某个用户的所有微博 
KeyTypeValue
uid:1:postslist{1, 2}
uid:2:postslist{3}


所有微博列表(Timeline) 
KeyTypeValue
timelinelist{1, 2, 3}


5.1.3 关系(粉丝(Follower),关注(Following)) 

比如user2关注了user1,则user2是user1的粉丝, 
user3也关注了user1, 
则user1被user2,user3所关注 

KeyTypeValue
uid:1:followersset{2,3}
uid:2:followingset{1}
uid:3:followingset{1}


新浪微博上姚晨的粉丝有7000万,光记录她一个人的粉丝就要7000万条记录。 
如果用传统的关系型数据库实现,压力很大。 

5.2 安全验证 
我们不用session来跟踪用户,而用cookie。 
每当用户登录后,为他生成一个随机数(令牌),发给该用户,让他作为cookie保存,这个cookie用来验证用户的身份。 
同样的存储一个倒转的key,可以根据cookie来得到uid 

KeyTypeValue
uid:2:authstring{fea5e81ac8ca77622bed1c2132a021f9}
auth:fea5e81ac8ca77622bed1c2132a021f9string{2}


5.3 包的结构 
org.springframework.data.redis.sample.retwisj.webweb层
org.springframework.data.redis.sample.retwisj.redis持久层
org.springframework.data.redis.sample.retwisj领域层


5.4 使用redisTemplate 
applicationContext-redis.xml 
Xml代码   收藏代码
  1. <beans>  
  2.   <context:property-placeholder location="classpath:redis.properties"/>  
  3.   
  4.   <!-- Redis client 这里就是不改代码实现不同客户端切换的地方了,我们用jedis-->  
  5.   <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"  
  6.     p:host-name="${redis.host}" p:port="${redis.port}" p:password="${redis.pass}"/>  
  7.   
  8.     <bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate"   
  9.         p:connection-factory-ref="connectionFactory"/>  
  10.   
  11.     <context:annotation-config />  
  12.     <context:component-scan base-package="org.springframework.data.redis.samples"/>  
  13.   
  14. </beans>  


5.5 web层 
采用spring MVC和JSP 

5.5.1 Controller层 
RetwisController 

5.5.2 CookieInterceptor 
对于用户验证,采用cookie的方式,写了一个Spring MVC的拦截器 
Java代码   收藏代码
  1. public class CookieInterceptor extends HandlerInterceptorAdapter  


并在retwisj-servlet.xml里配置 
Xml代码   收藏代码
  1. <mvc:interceptors>  
  2.     <bean class="org.springframework.data.redis.samples.retwisj.web.CookieInterceptor" />  
  3. </mvc:interceptors>  

执行效果就是在客户端保存了一个叫"retwisauth"的cookie,保存了用户的令牌。 

5.5.3 i18 

CookieLocaleResolver把语言作为cookie存在客户端 
Xml代码   收藏代码
  1. <bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"/>  


执行效果就是在客户端保存了一个叫"org.springframework.web.servlet.i18n.CookieLocaleResolver.LOCALE"的cookie,保存了语言。 

LocaleChangeInterceptor可以用来变更语言 
Xml代码   收藏代码
  1. <mvc:interceptors>  
  2.     <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" p:paramName="lang"/>  
  3. </mvc:interceptors>  


这样就可以通过http://localhost:8080/retwisj/?lang=cn来变更语言 

5.6 持久层 
RetwisRepository 

如下代码示例了如何用intersect来找出2个用户相同的粉丝 

Java代码   收藏代码
  1. private RedisSet<String> following(String uid) {  
  2.     return new DefaultRedisSet<String>(KeyUtils.following(uid), template);  
  3. }  
  4.   
  5. public List<String> commonFollowers(String uid, String targetUid) {  
  6.     RedisSet<String> tempSet = following(uid).intersectAndStore(following(targetUid),  
  7.     KeyUtils.commonFollowers(uid, targetUid));  
  8.   
  9.     tempSet.expire(5, TimeUnit.SECONDS);  
  10.   
  11.     return covertUidsToNames(tempSet.getKey());  
  12. }  


如下代码示例了用RedisAtomicLong实现自增长的id,BoundHashOperations来存储用户的用户名和密码 

Java代码   收藏代码
  1. public String addUser(String name, String password) {  
  2.     String uid = String.valueOf(userIdCounter.incrementAndGet());  
  3.   
  4.     // save user as hash  
  5.     // uid -> user  
  6.     BoundHashOperations<String, String, String> userOps = template.boundHashOps(KeyUtils.uid(uid));  
  7.     userOps.put("name", name);  
  8.     userOps.put("pass", password);  
  9.     valueOps.set(KeyUtils.user(name), uid);  
  10.   
  11.     users.addFirst(name);  
  12.     return addAuth(name);  
  13. }  
 http://xpenxpen.iteye.com/blog/2082966

 

相关文章:

  • [javaSE] GUI(事件监听机制)
  • android WebViewClient和WebChromeClient
  • vim列模式编辑
  • kafka集群发送消息报错
  • apache2 启用php7.0
  • Android 仿网易新闻v3.5:上下滑动的引导页
  • C语言基础
  • angularjs与require的集成摘抄
  • rsync远程数据备份配置之再次总结
  • HP DL580 G7设置IPMI
  • 面试题与答案
  • Gluon公布完整的Java 9 Mobile创新举措
  • Linux运维(数据库专题)面试题
  • 生产环境提升ssh安全的10种方式
  • 重提敏捷已死
  • 《网管员必读——网络组建》(第2版)电子课件下载
  • Android 控件背景颜色处理
  • CSS3 聊天气泡框以及 inherit、currentColor 关键字
  • JS 面试题总结
  • Linux链接文件
  • maven工程打包jar以及java jar命令的classpath使用
  • NSTimer学习笔记
  • STAR法则
  • TiDB 源码阅读系列文章(十)Chunk 和执行框架简介
  • ViewService——一种保证客户端与服务端同步的方法
  • 案例分享〡三拾众筹持续交付开发流程支撑创新业务
  • 给新手的新浪微博 SDK 集成教程【一】
  • 如何抓住下一波零售风口?看RPA玩转零售自动化
  •  一套莫尔斯电报听写、翻译系统
  • 阿里云重庆大学大数据训练营落地分享
  • #!/usr/bin/python与#!/usr/bin/env python的区别
  • #1015 : KMP算法
  • #AngularJS#$sce.trustAsResourceUrl
  • #大学#套接字
  • #我与Java虚拟机的故事#连载11: JVM学习之路
  • (读书笔记)Javascript高级程序设计---ECMAScript基础
  • (附源码)ssm教材管理系统 毕业设计 011229
  • (万字长文)Spring的核心知识尽揽其中
  • (状压dp)uva 10817 Headmaster's Headache
  • *p=a是把a的值赋给p,p=a是把a的地址赋给p。
  • .net core 调用c dll_用C++生成一个简单的DLL文件VS2008
  • .net6Api后台+uniapp导出Excel
  • .Net下使用 Geb.Video.FFMPEG 操作视频文件
  • /run/containerd/containerd.sock connect: connection refused
  • @AutoConfigurationPackage的使用
  • @modelattribute注解用postman测试怎么传参_接口测试之问题挖掘
  • @软考考生,这份软考高分攻略你须知道
  • [20160807][系统设计的三次迭代]
  • [28期] lamp兄弟连28期学员手册,请大家务必看一下
  • [android学习笔记]学习jni编程
  • [ANT] 项目中应用ANT
  • [bbk5179]第66集 第7章 - 数据库的维护 03
  • [C++] 多线程编程-thread::yield()-sleep_for()
  • [Docker]十一.Docker Swarm集群raft算法,Docker Swarm Web管理工具
  • [HTML]Web前端开发技术30(HTML5、CSS3、JavaScript )JavaScript基础——喵喵画网页