2023年2月23日 14:22 周四转载自https://github.com/Snailclimb/JavaGuide(添加小部分笔记)感谢作者!
Redis 共有 5 种基本数据结构:String(字符串)、List(列表)、Set(集合)、Hash(散列)、Zset(有序集合)。
这 5 种数据结构是直接提供给用户使用的,是数据的保存形式,其底层实现主要依赖这 8 种数据结构:简单动态字符串(SDS)、LinkedList(双向链表)、Hash Table(哈希表)、SkipList(跳跃表)、Intset(整数集合)、ZipList(压缩列表)、QuickList(快速列表)。
Redis 基本数据结构的底层数据结构实现如下:
| String | List | Hash | Set | Zset |
|---|
| SDS | LinkedList/ZipList/QuickList | Hash Table、ZipList | ZipList、Intset | ZipList、SkipList |
Redis 3.2 之前,List 底层实现是 LinkedList 或者 ZipList。 Redis 3.2 之后,引入了 LinkedList 和 ZipList 的结合 QuickList,List 的底层实现变为 QuickList。
你可以在 Redis 官网上找到 Redis 数据结构非常详细的介绍:
未来随着 Redis 新版本的发布,可能会有新的数据结构出现,通过查阅 Redis 官网对应的介绍,你总能获取到最靠谱的信息。

String(字符串)
#
介绍
#
String 是 Redis 中最简单同时也是最常用的一个数据结构。
String 是一种二进制安全的数据结构,可以用来存储任何类型的数据比如字符串、整数、浮点数、图片(图片的 base64 编码或者解码或者图片的路径)、序列化后的对象。
...
2023年2月23日 11:16 周四转载自https://github.com/Snailclimb/JavaGuide(添加小部分笔记)感谢作者!
Redis 事务
#
如何使用 Redis 事务?
#
Redis 可以通过 MULTI,EXEC,DISCARD 和 WATCH 等命令来实现事务(transaction)功能。
> MULTI
OK
> SET PROJECT "JavaGuide"
QUEUED
> GET PROJECT
QUEUED
> EXEC
1) OK
2) "JavaGuide"
MULTI 命令后可以输入多个命令,Redis 不会立即执行这些命令,而是将它们放到队列,当调用了
EXEC 命令后,再执行所有的命令。
这个过程是这样的:
- 开始事务(
MULTI); - 命令入队(批量操作 Redis 的命令,先进先出(FIFO)的顺序执行);
- 执行事务(
EXEC)。
你也可以通过
DISCARD 命令取消一个事务,它会清空事务队列中保存的所有命令。
> MULTI
OK
> SET PROJECT "JavaGuide"
QUEUED
> GET PROJECT
QUEUED
> DISCARD
OK
你可以通过
WATCH 命令监听指定的 Key,当调用 EXEC 命令执行事务时,如果一个被 WATCH 命令监视的 Key 被 其他客户端/Session 修改的话,整个事务都不会被执行。
...
2023年2月22日 11:31 周三转载自https://github.com/Snailclimb/JavaGuide(添加小部分笔记)感谢作者!
Redis 基础
#
什么是 Redis?
#
Redis 是一个基于 C 语言开发的开源数据库(BSD 许可),与传统数据库不同的是 Redis 的数据是存在内存中的(内存数据库),读写速度非常快,被广泛应用于缓存方向。并且,Redis 存储的是 KV 键值对数据。
为了满足不同的业务场景,Redis 内置了多种数据类型实现(比如 String、Hash、【List、Set、】Sorted Set、Bitmap)。并且,Redis 还支持事务 、持久化、Lua 脚本、多种开箱即用的集群方案(Redis Sentinel、Redis Cluster)。
Redis 没有外部依赖,Linux 和 OS X 是 Redis 开发和测试最多的两个操作系统,官方推荐生产环境使用 Linux 部署 Redis。
个人学习的话,你可以自己本机安装 Redis 或者通过 Redis 官网提供的
在线 Redis 环境来实际体验 Redis。

全世界有非常多的网站使用到了 Redis ,
techstacks.io 专门维护了一个
使用 Redis 的热门站点列表 ,感兴趣的话可以看看。
Redis 为什么这么快?
#
Redis 内部做了非常多的性能优化,比较重要的主要有下面 3 点:
- Redis 基于内存,内存的访问速度是磁盘的上千倍;
- Redis 基于 Reactor 模式设计开发了一套高效的事件处理模型,主要是单线程事件循环和 IO 多路复用(Redis 线程模式后面会详细介绍到);
- Redis 内置了多种优化过后的数据结构实现,性能非常高。
下面这张图片总结的挺不错的,分享一下,出自
Why is Redis so fast? 。
...
2023年2月21日 09:26 周二转载自https://github.com/Snailclimb/JavaGuide(添加小部分笔记)感谢作者!
原文地址:https://juejin.cn/post/7122014462181113887,JavaGuide 对本文进行了完善总结。
我有一个朋友做了一个小破站,现在要实现一个站内信 Web 消息推送的功能,对,就是下图这个小红点,一个很常用的功能。

不过他还没想好用什么方式做,这里我帮他整理了一下几种方案,并简单做了实现。
# 什么是消息推送?
#
推送的场景比较多,比如有人关注我的公众号,这时我就会收到一条推送消息,以此来吸引我点击打开应用。
消息推送通常是指网站的运营工作等人员,通过某种工具对用户当前网页或移动设备 APP 进行的主动消息推送。
消息推送一般又分为 Web 端消息推送和移动端消息推送。
移动端消息推送示例 :

Web 端消息推送示例:

在具体实现之前,咱们再来分析一下前边的需求,其实功能很简单,只要触发某个事件(主动分享了资源或者后台主动推送消息),Web 页面的通知小红点就会实时的 +1 就可以了。
通常在服务端会有若干张消息推送表,用来记录用户触发不同事件所推送不同类型的消息,前端主动查询(拉)或者被动接收(推)用户所有未读的消息数。

消息推送无非是推(push)和拉(pull)两种形式,下边我们逐个了解下。
# 消息推送常见方案
#
# 短轮询
#
轮询(polling) 应该是实现消息推送方案中最简单的一种,这里我们暂且将轮询分为短轮询和长轮询。
短轮询很好理解,指定的时间间隔,由浏览器向服务器发出 HTTP 请求,服务器实时返回未读消息数据给客户端,浏览器再做渲染显示。
一个简单的 JS 定时器就可以搞定,每秒钟请求一次未读消息数接口,返回的数据展示即可。
setInterval(() => {
// 方法请求
messageCount().then((res) => {
if (res.code === 200) {
this.messageCount = res.data
}
})
}, 1000);
效果还是可以的,短轮询实现固然简单,缺点也是显而易见,由于推送数据并不会频繁变更,无论后端此时是否有新的消息产生,客户端都会进行请求,势必会对服务端造成很大压力,浪费带宽和服务器资源。
# 长轮询
#
长轮询是对上边短轮询的一种改进版本,在尽可能减少对服务器资源浪费的同时,保证消息的相对实时性。长轮询在中间件中应用的很广泛,比如 Nacos 和 Apollo 配置中心,消息队列 Kafka、RocketMQ 中都有用到长轮询。
...
2023年2月21日 09:17 周二转载自https://github.com/Snailclimb/JavaGuide(添加小部分笔记)感谢作者!
为什么需要定时任务?
#
我们来看一下几个非常常见的业务场景:
- 某系统凌晨要进行数据备份。
- 某电商平台,用户下单半个小时未支付的情况下需要自动取消订单。
- 某媒体聚合平台,每 10 分钟动态抓取某某网站的数据为自己所用。
- 某博客平台,支持定时发送文章。
- 某基金平台,每晚定时计算用户当日收益情况并推送给用户最新的数据。
- ……
这些场景往往都要求我们在某个特定的时间去做某个事情。
单机定时任务技术选型
#
Timer
#
java.util.Timer是 JDK 1.3 开始就已经支持的一种定时任务的实现方式。
Timer 内部使用一个叫做 TaskQueue 的类存放定时任务,它是一个基于最小堆实现的优先级队列。TaskQueue 会按照任务距离下一次执行时间的大小将任务排序,保证在堆顶的任务最先执行。这样在需要执行任务时,每次只需要取出堆顶的任务运行即可!
Timer 使用起来比较简单,通过下面的方式我们就能创建一个 1s 之后执行的定时任务。
// 示例代码:
TimerTask task = new TimerTask() {
public void run() {
System.out.println("当前时间: " + new Date() + "n" +
"线程名称: " + Thread.currentThread().getName());
}
};
System.out.println("当前时间: " + new Date() + "n" +
"线程名称: " + Thread.currentThread().getName());
Timer timer = new Timer("Timer");
long delay = 1000L;
timer.schedule(task, delay);
//输出:
当前时间: Fri May 28 15:18:47 CST 2021n线程名称: main
当前时间: Fri May 28 15:18:48 CST 2021n线程名称: Timer
不过其缺陷较多,比如一个 Timer 一个线程,这就导致 Timer 的任务的执行只能串行执行,一个任务执行时间过长的话会影响其他任务(性能非常差),再比如发生异常时任务直接停止(Timer 只捕获了 InterruptedException )。
...
2023年2月21日 08:57 周二转载自https://github.com/Snailclimb/JavaGuide(添加小部分笔记)感谢作者!
系统需要对用户输入的文本进行敏感词过滤如色情、政治、暴力相关的词汇。
敏感词过滤用的使用比较多的 Trie 树算法 和 DFA 算法。
算法实现
#
Trie 树
#
Trie 树 也称为字典树、单词查找树,哈系树(这里是不是写错了,哈希树?)的一种变种,通常被用于字符串匹配,用来解决在一组字符串集合中快速查找某个字符串的问题。像浏览器搜索的关键词提示一般就是基于 Trie 树来做的。

假如我们的敏感词库中有以下敏感词:
我们构造出来的敏感词 Trie 树就是下面这样的:

当我们要查找对应的字符串“东京热”的话,我们会把这个字符串切割成单个的字符“东”、“京”、“热”,然后我们从 Trie 树的根节点开始匹配。
可以看出, Trie 树的核心原理其实很简单,就是通过公共前缀来提高字符串匹配效率。
Apache Commons Collecions 这个库中就有 Trie 树实现:

Trie<String, String> trie = new PatriciaTrie<>();
trie.put("Abigail", "student");
trie.put("Abi", "doctor");
trie.put("Annabel", "teacher");
trie.put("Christina", "student");
trie.put("Chris", "doctor");
Assertions.assertTrue(trie.containsKey("Abigail"));
assertEquals("{Abi=doctor, Abigail=student}", trie.prefixMap("Abi").toString());
assertEquals("{Chris=doctor, Christina=student}", trie.prefixMap("Chr").toString());
Aho-Corasick(AC)自动机是一种建立在 Trie 树上的一种改进算法,是一种多模式匹配算法,由贝尔实验室的研究人员 Alfred V. Aho 和 Margaret J.Corasick 发明。
...
2023年2月20日 15:54 周一转载自https://github.com/Snailclimb/JavaGuide(添加小部分笔记)感谢作者!
作者:转转技术团队
原文:https://mp.weixin.qq.com/s/ONMuELjdHYa0yQceTj01Iw
ly:比较繁琐,大概看了前面的部分
老权限系统的问题与现状
#
转转公司在过去并没有一个统一的权限管理系统,权限管理由各业务自行研发或是使用其他业务的权限系统,权限管理的不统一带来了不少问题:
- 各业务重复造轮子,维护成本高
- 各系统只解决部分场景问题,方案不够通用,新项目选型时没有可靠的权限管理方案
- 缺乏统一的日志管理与审批流程,在授权信息追溯上十分困难
基于上述问题,去年底公司启动建设转转统一权限系统,目标是开发一套灵活、易用、安全的权限管理系统,供各业务使用。
业界权限系统的设计方式
#
目前业界主流的权限模型有两种,下面分别介绍下:
- 基于角色的访问控制(RBAC)
- 基于属性的访问控制(ABAC)
RBAC 模型
#
基于角色的访问控制(Role-Based Access Control,简称 RBAC) 指的是通过用户的角色(Role)授权其相关权限,实现了灵活的访问控制,相比直接授予用户权限,要更加简单、高效、可扩展。
一个用户可以拥有若干角色,每一个角色又可以被分配若干权限这样,就构造成“用户-角色-权限” 的授权模型。在这种模型中,用户与角色、角色与权限之间构成了多对多的关系。
用一个图来描述如下:

当使用 RBAC模型 时,通过分析用户的实际情况,基于共同的职责和需求,授予他们不同角色。这种 用户 -> 角色 -> 权限 间的关系,让我们可以不用再单独管理单个用户权限,用户从授予的角色里面获取所需的权限。
以一个简单的场景(Gitlab 的权限系统)为例,用户系统中有 Admin、Maintainer、Operator 三种角色,这三种角色分别具备不同的权限,比如只有 Admin 具备创建代码仓库、删除代码仓库的权限,其他的角色都不具备。我们授予某个用户 Admin 这个角色,他就具备了 创建代码仓库 和 删除代码仓库 这两个权限。
通过 RBAC模型 ,当存在多个用户拥有相同权限时,我们只需要创建好拥有该权限的角色,然后给不同的用户分配不同的角色,后续只需要修改角色的权限,就能自动修改角色内所有用户的权限。
ABAC 模型
#
基于属性的访问控制(Attribute-Based Access Control,简称 ABAC) 是一种比 RBAC模型 更加灵活的授权模型,它的原理是通过各种属性来动态判断一个操作是否可以被允许。这个模型在云系统中使用的比较多,比如 AWS,阿里云等。
考虑下面这些场景的权限控制:
- 授权某个人具体某本书的编辑权限
- 当一个文档的所属部门跟用户的部门相同时,用户可以访问这个文档
- 当用户是一个文档的拥有者并且文档的状态是草稿,用户可以编辑这个文档
- 早上九点前禁止 A 部门的人访问 B 系统
- 在除了上海以外的地方禁止以管理员身份访问 A 系统
- 用户对 2022-06-07 之前创建的订单有操作权限
可以发现上述的场景通过 RBAC模型 很难去实现,因为 RBAC模型 仅仅描述了用户可以做什么操作,但是操作的条件,以及操作的数据,RBAC模型 本身是没有这些限制的。但这恰恰是 ABAC模型 的长处,ABAC模型 的思想是基于用户、访问的数据的属性、以及各种环境因素去动态计算用户是否有权限进行操作。
...
2023年2月20日 15:20 周一转载自https://github.com/Snailclimb/JavaGuide(添加小部分笔记)感谢作者!
本文授权转载自 :
https://ken.io/note/sso-design-implement 作者:ken.io
SSO 介绍
#
什么是 SSO?
#
SSO 英文全称 Single Sign On,单点登录。SSO 是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。
例如你登录网易账号中心(https://reg.163.com/ )之后访问以下站点都是登录状态。
SSO 有什么好处?
#
- 用户角度 :用户能够做到一次登录多次使用,无需记录多套用户名和密码,省心。
- 系统管理员角度 : 管理员只需维护好一个统一的账号中心就可以了,方便。
- 新系统开发角度: 新系统开发时只需直接对接统一的账号中心即可,简化开发流程,省时。
SSO 设计与实现
#
本篇文章也主要是为了探讨如何设计&实现一个 SSO 系统
以下为需要实现的核心功能:
核心应用与依赖
#

| 应用/模块/对象 | 说明 |
|---|
| 前台站点 | 需要登录的站点 |
| SSO 站点-登录 | 提供登录的页面 |
| SSO 站点-登出 | 提供注销登录的入口 |
| SSO 服务-登录 | 提供登录服务 |
| SSO 服务-登录状态 | 提供登录状态校验/登录信息查询的服务 |
| SSO 服务-登出 | 提供用户注销登录的服务 |
| 数据库 | 存储用户账户信息 |
| 缓存 | 存储用户的登录信息,通常使用 Redis |
用户登录状态的存储与校验
#
常见的 Web 框架对于 Session 的实现都是生成一个 SessionId 存储在浏览器 Cookie 中。然后将 Session 内容存储在服务器端内存中,这个
ken.io 在之前
Session 工作原理中也提到过。整体也是借鉴这个思路。
...
2023年2月20日 14:40 周一转载自https://github.com/Snailclimb/JavaGuide(添加小部分笔记)感谢作者!
在
JWT 基本概念详解这篇文章中,我介绍了:
- 什么是 JWT?
- JWT 由哪些部分组成?
- 如何基于 JWT 进行身份验证?
- JWT 如何防止 Token 被篡改?
- 如何加强 JWT 的安全性?
这篇文章,我们一起探讨一下 JWT 身份认证的优缺点以及常见问题的解决办法。
JWT 的优势
#
相比于 Session 认证的方式来说,使用 JWT 进行身份认证主要有下面 4 个优势。
无状态
#
JWT 自身包含了身份验证所需要的所有信息,因此,我们的服务器不需要存储 Session 信息。这显然增加了系统的可用性和伸缩性,大大减轻了服务端的压力。
不过,也正是由于 JWT 的无状态,也导致了它最大的缺点:不可控!
就比如说,我们想要在 JWT 有效期内废弃一个 JWT 或者更改它的权限的话,并不会立即生效,通常需要等到有效期过后才可以。再比如说,当用户 Logout 的话,JWT 也还有效。除非,我们在后端增加额外的处理逻辑比如将失效的 JWT 存储起来,后端先验证 JWT 是否有效再进行处理。具体的解决办法,我们会在后面的内容中详细介绍到,这里只是简单提一下。
有效避免了 CSRF 攻击
#
[ˈfɔːdʒəri] forgery 伪造
CSRF(Cross Site Request Forgery) 一般被翻译为 跨站请求伪造,属于网络攻击领域范围。相比于 SQL 脚本注入、XSS 等安全攻击方式,CSRF 的知名度并没有它们高。但是,它的确是我们开发系统时必须要考虑的安全隐患。就连业内技术标杆 Google 的产品 Gmail 也曾在 2007 年的时候爆出过 CSRF 漏洞,这给 Gmail 的用户造成了很大的损失。
...
2023年2月20日 14:28 周一转载自https://github.com/Snailclimb/JavaGuide(添加小部分笔记)感谢作者!

什么是 JWT?
#
JWT (JSON Web Token) 是目前最流行的跨域认证解决方案,是一种基于 Token 的认证授权机制。 从 JWT 的全称可以看出,JWT 本身也是 Token,一种规范化之后的 JSON 结构的 Token。
跨域认证的问题
互联网服务离不开用户认证。一般流程是下面这样。
这种模式的问题在于,扩展性(scaling)不好。单机当然没有问题,如果是服务器集群,或者是跨域的服务导向架构,就要求 session 数据共享,每台服务器都能够读取 session。
举例来说,A 网站和 B 网站是同一家公司的关联服务。现在要求,用户只要在其中一个网站登录,再访问另一个网站就会自动登录,请问怎么实现?
一种解决方案是 session 数据持久化,写入数据库或别的持久层。各种服务收到请求后,都向持久层请求数据。这种方案的优点是架构清晰,缺点是工程量比较大。另外,持久层万一挂了,就会单点失败。
另一种方案是服务器索性不保存 session 数据了,所有数据都保存在客户端,每次请求都发回服务器。JWT 就是这种方案的一个代表。
JWT 自身包含了身份验证所需要的所有信息,因此,我们的服务器不需要存储 Session 信息。这显然增加了系统的可用性和伸缩性,大大减轻了服务端的压力。
ly:我觉得这里的重点就是,服务器不存储Session以维护"用户"和cookie(session id)的关系了
可以看出,JWT 更符合设计 RESTful API 时的「Stateless(无状态)」原则 。
并且, 使用 JWT 认证可以有效避免 CSRF 攻击,因为 JWT 一般是存在在 localStorage 中,使用 JWT 进行身份验证的过程中是不会涉及到 Cookie 的。
我在
JWT 优缺点分析这篇文章中有详细介绍到使用 JWT 做身份认证的优势和劣势。
下面是
RFC 7519 对 JWT 做的较为正式的定义。
...