# blog **Repository Path**: S_Yang/blog ## Basic Information - **Project Name**: blog - **Description**: No description available - **Primary Language**: Java - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-08-04 - **Last Updated**: 2021-10-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ### 热榜思路: 因为排行榜是最近七天的评论数量最多的排行榜,所以项目启动的时候就在数据库查一下,前时间和文章被发表的时间差在七天以内(这里用的sql的大于)文章缓存到Redis的hash结构。并且为每一个文章设置据他发表到七天以后的时间。 当文章有评论更改的时候,首先在zset里面Redis自增,有删除评论就进行自减。因为每天凌晨同步Redis数据到数据库,然后删除Redis,所以Redis文章的评论数量的key是包含了是哪一天的哪一个文章,如果有新的评论的时候,就尝试从Redis里面去get,如果没有这个key,说明数据库刚完成Redis的同步更新,就去数据库查一下然后自加。 每一次删除增加评论的时候,都要对最近七天的评论Redis的zset做一次并集,这样就获得了最近七天的 热评数量。 此外,对于恶意刷榜,给予认证,例如每一个用户在增加一条评论以后向Redis写入信息,然后每次写Redis的value就自增,如果大于一个阈值就给予前端一个认证的措施。 ### 分布式锁思路 当进行对访问量增长的时候,虽然可以使用自增的方式,但是为了复现分布式的情况,采用了get之后再set的情况,这样再分布式系统和多线程情况下就会产生线程安全的问题, 可能在get之后自加再set的时候,有其他线程再get然后自加set,这样最终的结果就会 产生很大的问题。 所以采用了Redis锁方式,在进行增长访问量的时候,会开启一个线程,尝试去拿到锁,如果拿到了,就在锁的value写uuid,然后并且这是一个过期时间,这个是原子操作,然后 进行逻辑操作,如果逻辑操作成功,判断这把锁的value是不是自己的uuid,如果是的话就 删除这个锁。 如果没有拿到锁,就会采用 **阻塞-唤醒方式** 或者 **自旋锁** 的方式,下文介绍。 ### 阻塞-唤醒方式 如果没有成功拿到锁,就先在Redis的list里面写入自己服务器的ip和自己线程id,因为在分布式系统中,多台服务同时工作,可能出现线程id重复,所以前面加上服务器ip来加以区分,写到list以后,线程阻塞自己。等待其他线程唤醒。 项目启动以后会开启一个线程,他的作用是监视Redis队列有没有任务,如果有,查看当前锁有没有人正在用,如果没有人,对比最后要出队的是不是自己的线程,如果是,就出队,唤醒执行增长的线程,同时阻塞自己,减少不必要的轮询,同时,增长线程完成了自己的逻辑处理,在释放锁之后,就会唤醒这个线程,这个线程又可以去判断队列有没有元素去执行判断。 通过线程id找到线程办法:解析出他的线程id,然后通过线程组的方式遍历所有的线程,找到这个线程唤醒,执行下面的逻辑。 ### 自旋锁的方式 如果没有拿到锁,线程会入队,这个和上面的思路一样,然后这个线程会每隔一段时间看看即将要出队的线程是不是自己,如果是自己就出队,不断尝试拿到锁,如果拿到了就执行逻辑,如果不是自己则睡眠一段时间再去看。 ### Redis全局唯一ID实现思路 在Redis中利用keyValue结构,key是当前时间的毫秒值,value默认是1,然后有一个线程访问就自增一次,最后拼接字符串,可以保证每一毫秒接收可以容long的最大值个线程。 在实现的时候,首先去获取当前的时间戳,然后利用Redis的setnx命令,设置当前的毫秒值的value是1,如果当前已经有线程设置好了,那么这条命令就不会成功,自增就可以了,最后拼接自增结果和时间戳。 ### cookie加密实现思路 当用户登录之后,会对登录的信息进行AES的加密,这种加密方式是对称加密,即加密和解密都是采用相同的公钥,所以这个公钥的保密尤为重要,不能够写死。 在进行获取秘钥的时候,首先可以利用Redis全局id作为Redis的key,秘钥作为value,最后把这个唯一的id写入cookie,在用的时候先通过cookie找到唯一id找到公钥,然后进行解密,再重新登录。 考虑到这个会造成Redis空间的大量浪费,可以更改秘钥的key,比如每7天换一次秘钥。登录的时候获取时间,在Redis当中查看这个时间点的的秘钥是什么,在进行解密。 ### 多端登录其他端被踢下线思路 在登录的时候,会向session和Redis写入信息,key是用户名,value是Redis全局唯一ID,在每一次请求的时候,会对比session和Redis的信息是否一致,如果发生了不一致,说明已经有人修改了当前用户Redis上的新的全局id,但是因为浏览器还在打开,他不会修改session当中当前用户的信息。造成了这个用户的信息不一致,说明当前用户被踢下线,执行注销操作。可以在这个时候去给予前端提示。 Redis上的信息代表了这个用户最新的登录信息,session上的信息代表了这个用户在这太设备上的登录信息,如果一致,说明没有人抢占,如果不一致,说明有人修改Redis信息,不一致的一端被踢下线。