哈喽,又是一天早起的日子,今天就写写昨天实现了的浏览量逻辑设计,顺带一些其它的小知识总结,
设计思路
基本需求是当用户进入到某一篇博客的时候,该博客的浏览量 1,并且同一个用户同一天访问的博客不会再次增加1,即一天一个用户只会绑定一次某个博客。
基于这个需求,最开始设计的时候就简单的对getBlogById进行了一个扩展,就是在获取博客之前先对博客的visit进行自增,之后再返回博客给前端。此外还支持事务操作,遇到异常就进行回滚。然而事实是纯粹的这样操作,无法甄别是否是同一个用户拼命刷浏览量,无法表达出一篇文章的真实价值。
所以就想着也许可以通过用户的ip进行处理,虽然说同一个网络下的内网该ip都一样,不过基本可以满足需求。那么数据使用什么来存储呢?那当然是redis了,将所有用户维系到一个redis的一个set中即可。当然了,对于不同博客一个用户当然是可以分别增加浏览量的,所以不能只依据ip来进行反馈,所以我简单的设计了一个规则来区分:
代码语言:javascript复制ip#blog:id
首先第一段是ip地址,后面是#号用来分隔,接着跟上目标大类,以及具体的文章id。每次判断set中是否存在来决定是否为其自增,不过不管怎样都要返回博客列表就是了。对于该set集合,每天0点的时候也需要自动清空一下。
实现代码
首先是原始的博客获取service代码:
代码语言:javascript复制 @Override
public RetResult<Blog> getBlogById(Integer id) {
return RetResult.success(blogDao.getBlogById(id));
}
接着我们对该方法重载扩充功能实现,先尝试从库中尝试查找到该串,如果没找到或者未定义set,那么就添加该串并且调用增加访客量的逻辑:
代码语言:javascript复制@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
@Override
public RetResult<Blog> getBlogById(Integer id, String addr) {
String targetSer = addr "#blog:" id;
String vsStr = RedisConst.VISIT_SET;
SetOperations<String,String> ops = redisTemplate.opsForSet();
Boolean res = ops.isMember(vsStr, targetSer);
if (res == null || !res){
// 该用户今日未访问该博客, 新增访问量,并且将访问记录记入redis的visitSet中
ops.add(vsStr, targetSer);
blogDao.increaseVisit(id);
// 如果已经访问,不增加新的访问。
}
return getBlogById(id);
}
对于定时任务,只要简单的调用一下delete即可:
代码语言:javascript复制 @Scheduled(cron = "0 0 0 * * ?")
public void visitSetClear(){
Boolean delete = redisTemplate.delete(RedisConst.VISIT_SET);
if (delete != null)
log.info("redis:{} clear - {}", RedisConst.VISIT_SET, delete?"clear":"non 2 clear");
}
数据安全
现在的redis服务数据就有了一定的重要性,那么肯定也想到对redis服务进行备份,可以采用主从服务器的策略来实现。
首先另外一台服务器做好如下的配置:
代码语言:javascript复制port 6379
protected-mode yes
daemonize yes
dir /home/dai/server/redis
dbfilename dump_6379.rdb
logfile /home/dai/server/redis/redis.log
pidfile /home/dai/server/redis/redis.pid
requirepass xxxxxx
replicaof <ip> <port>
masterauth xxxxxx
这样这台redis服务器就会自动获得主服务器的数据了。
其它发现
这段时间,也发现了一些其它结论:
- springboot对于response的编码通过配置实现,如果直接设置response.setContentType好像并不能生效
server.servlet.encoding.force=true
- springboot的配置可以在jar包外的路径下新建application.yml来进行更改配置,但是如果是这样更改ssl的路径似乎并不能生效