1. 简介 封装了一下redis的客户端,使得使用起来更方便。 优点是把客户端连接放到一个连接池里,从而提高性能。还有就是可以不同的客户端之间实现切换,而不用改一行代码(Spring惯用的一个手法)。 本文写作时最新版是1.3, 目前支持下面4种java客户端,可以自由切换而不用改代码。 gradle.properties 4.3 编译 4.4 将retwisj.war放入tomcat的webapp目录下,启动tomcat 4.5 访问http://localhost:8080/retwisj 花几分钟上去玩一下,熟悉一下功能。 好了,我们已经做好了一个微博网站了。玩好了我们简单分析一下代码实现。 5. 实现分析 5.1 表结构设计 5.1.1 用户(User) 用以前的关系型数据库设计是这样的
而用redis的则变成了这样
将用户的名字和密码作为一个hash存入 对应的redis原生命令是 uid是自增长的,可以用redis的INCR来实现,这是一个原子操作
我们要保存username和uid的对应关系,比如一个user登录时,我们要得到他的uid。可以创建一个倒转的key来实现此功能。user:[name]:uid 还创建了一个users用来保存所有uid的list
5.1.2 微博(Post) 同样的,用global:pid来记录自增长的post id
将微博的内容、时间和作者作为一个hash存入
某个用户的所有微博
所有微博列表(Timeline)
5.1.3 关系(粉丝(Follower),关注(Following)) 比如user2关注了user1,则user2是user1的粉丝, user3也关注了user1, 则user1被user2,user3所关注
新浪微博上姚晨的粉丝有7000万,光记录她一个人的粉丝就要7000万条记录。 如果用传统的关系型数据库实现,压力很大。 5.2 安全验证 我们不用session来跟踪用户,而用cookie。 每当用户登录后,为他生成一个随机数(令牌),发给该用户,让他作为cookie保存,这个cookie用来验证用户的身份。 同样的存储一个倒转的key,可以根据cookie来得到uid
5.3 包的结构
5.4 使用redisTemplate applicationContext-redis.xml 5.5 web层 采用spring MVC和JSP 5.5.1 Controller层 RetwisController 5.5.2 CookieInterceptor 对于用户验证,采用cookie的方式,写了一个Spring MVC的拦截器 并在retwisj-servlet.xml里配置 执行效果就是在客户端保存了一个叫"retwisauth"的cookie,保存了用户的令牌。 5.5.3 i18 CookieLocaleResolver把语言作为cookie存在客户端 执行效果就是在客户端保存了一个叫"org.springframework.web.servlet.i18n.CookieLocaleResolver.LOCALE"的cookie,保存了语言。 LocaleChangeInterceptor可以用来变更语言 这样就可以通过http://localhost:8080/retwisj/?lang=cn来变更语言 5.6 持久层 RetwisRepository 如下代码示例了如何用intersect来找出2个用户相同的粉丝 如下代码示例了用RedisAtomicLong实现自增长的id,BoundHashOperations来存储用户的用户名和密码
- compile "redis.clients:jedis:2.5.1"
- springVersion = 3.2.9.RELEASE
- springRedisVersion = 1.3.0.RELEASE
- gradle build
Key | Username | Password |
1 | springrod | interface21 |
2 | costinl | this is fun |
Key | Type | Value |
uid:1 | hash | {name: springrod, pass: interface21} |
uid:2 | hash | {name: costinl, pass: secret} |
- HMSET user:1 name springrod pass interface21
- INCR global:uid
Key | Type | Value |
global:uid | string | 2 |
Key | Type | Value |
user:springrod:uid | string | 1 |
user:costinl:uid | string | 2 |
users | list | {1, 2} |
Key | Type | Value |
global:pid | string | 2 |
Key | Type | Value |
pid:1 | hash | {content: Hello World, time: 1301931414757, uid: 1} |
pid:2 | hash | {content: Working on some cool stuff, time: 1301931414897, uid: 1} |
pid:3 | hash | {content: Checking out RetwisJ, time: 1301931454897, uid: 2} |
Key | Type | Value |
uid:1:posts | list | {1, 2} |
uid:2:posts | list | {3} |
Key | Type | Value |
timeline | list | {1, 2, 3} |
Key | Type | Value |
uid:1:followers | set | {2,3} |
uid:2:following | set | {1} |
uid:3:following | set | {1} |
Key | Type | Value |
uid:2:auth | string | {fea5e81ac8ca77622bed1c2132a021f9} |
auth:fea5e81ac8ca77622bed1c2132a021f9 | string | {2} |
org.springframework.data.redis.sample.retwisj.web | web层 |
org.springframework.data.redis.sample.retwisj.redis | 持久层 |
org.springframework.data.redis.sample.retwisj | 领域层 |
- <beans>
- <context:property-placeholder location="classpath:redis.properties"/>
- <!-- Redis client 这里就是不改代码实现不同客户端切换的地方了,我们用jedis-->
- <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
- p:host-name="${redis.host}" p:port="${redis.port}" p:password="${redis.pass}"/>
- <bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate"
- p:connection-factory-ref="connectionFactory"/>
- <context:annotation-config />
- <context:component-scan base-package="org.springframework.data.redis.samples"/>
- </beans>
- public class CookieInterceptor extends HandlerInterceptorAdapter
- <mvc:interceptors>
- <bean class="org.springframework.data.redis.samples.retwisj.web.CookieInterceptor" />
- </mvc:interceptors>
- <bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"/>
- <mvc:interceptors>
- <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" p:paramName="lang"/>
- </mvc:interceptors>
- private RedisSet<String> following(String uid) {
- return new DefaultRedisSet<String>(KeyUtils.following(uid), template);
- }
- public List<String> commonFollowers(String uid, String targetUid) {
- RedisSet<String> tempSet = following(uid).intersectAndStore(following(targetUid),
- KeyUtils.commonFollowers(uid, targetUid));
- tempSet.expire(5, TimeUnit.SECONDS);
- return covertUidsToNames(tempSet.getKey());
- }
- public String addUser(String name, String password) {
- String uid = String.valueOf(userIdCounter.incrementAndGet());
- // save user as hash
- // uid -> user
- BoundHashOperations<String, String, String> userOps = template.boundHashOps(KeyUtils.uid(uid));
- userOps.put("name", name);
- userOps.put("pass", password);
- valueOps.set(KeyUtils.user(name), uid);
- users.addFirst(name);
- return addAuth(name);
- }
http://xpenxpen.iteye.com/blog/2082966