mirror of
https://github.com/doocs/advanced-java.git
synced 2026-08-18 10:43:29 +08:00
fix: update documents about Redis and ElasticSearch
更新 Redis 和 ElasticSearch 文档说明
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
* [说一下 Dubbo 的工作原理?注册中心挂了可以继续通信吗?](/docs/distributed-system/dubbo-operating-principle.md)
|
||||
* [Dubbo 支持哪些序列化协议?说一下 Hessian 的数据结构?PB 知道吗?为什么 PB 的效率是最高的?](/docs/distributed-system/dubbo-serialization-protocol.md)
|
||||
* [Dubbo 负载均衡策略和集群容错策略都有哪些?动态代理策略呢?](/docs/distributed-system/dubbo-load-balancing.md)
|
||||
* [Dubbo 的 spi 思想是什么?](/docs/distributed-system/dubbo-spi.md)
|
||||
* [Dubbo 的 SPI 思想是什么?](/docs/distributed-system/dubbo-spi.md)
|
||||
* [如何基于 Dubbo 进行服务治理、服务降级、失败重试以及超时重试?](/docs/distributed-system/dubbo-service-management.md)
|
||||
* [分布式服务接口的幂等性如何设计(比如不能重复扣款)?](/docs/distributed-system/distributed-system-idempotency.md)
|
||||
* [分布式服务接口请求的顺序性如何保证?](/docs/distributed-system/distributed-system-request-sequence.md)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
## 面试题
|
||||
一般实现分布式锁都有哪些方式?使用 redis 如何设计分布式锁?使用 zk 来设计分布式锁可以吗?这两种分布式锁的实现方式哪种效率比较高?
|
||||
一般实现分布式锁都有哪些方式?使用 Redis 如何设计分布式锁?使用 zk 来设计分布式锁可以吗?这两种分布式锁的实现方式哪种效率比较高?
|
||||
|
||||
## 面试官心理分析
|
||||
|
||||
@@ -7,19 +7,19 @@
|
||||
|
||||
## 面试题剖析
|
||||
|
||||
### redis 分布式锁
|
||||
### Redis 分布式锁
|
||||
|
||||
官方叫做 `RedLock` 算法,是 redis 官方支持的分布式锁算法。
|
||||
官方叫做 `RedLock` 算法,是 Redis 官方支持的分布式锁算法。
|
||||
|
||||
这个分布式锁有 3 个重要的考量点:
|
||||
|
||||
* 互斥(只能有一个客户端获取锁)
|
||||
* 不能死锁
|
||||
* 容错(只要大部分 redis 节点创建了这把锁就可以)
|
||||
* 容错(只要大部分 Redis 节点创建了这把锁就可以)
|
||||
|
||||
#### redis 最普通的分布式锁
|
||||
#### Redis 最普通的分布式锁
|
||||
|
||||
第一个最普通的实现方式,就是在 redis 里使用 `SET key value [EX seconds] [PX milliseconds] NX` 创建一个 key,这样就算加锁。其中:
|
||||
第一个最普通的实现方式,就是在 Redis 里使用 `SET key value [EX seconds] [PX milliseconds] NX` 创建一个 key,这样就算加锁。其中:
|
||||
|
||||
- `NX`:表示只有 `key` 不存在的时候才会设置成功,如果此时 redis 中存在这个 `key`,那么设置失败,返回 `nil`。
|
||||
- `EX seconds`:设置 `key` 的过期时间,精确到秒级。意思是 `seconds` 秒后锁自动释放,别人创建的时候如果发现已经有了就不能加锁了。
|
||||
@@ -44,11 +44,11 @@ end
|
||||
|
||||
为啥要用 `random_value` 随机值呢?因为如果某个客户端获取到了锁,但是阻塞了很长时间才执行完,比如说超过了 30s,此时可能已经自动释放锁了,此时可能别的客户端已经获取到了这个锁,要是你这个时候直接删除 key 的话会有问题,所以得用随机值加上面的 `lua` 脚本来释放锁。
|
||||
|
||||
但是这样是肯定不行的。因为如果是普通的 redis 单实例,那就是单点故障。或者是 redis 普通主从,那 redis 主从异步复制,如果主节点挂了(key 就没有了),key 还没同步到从节点,此时从节点切换为主节点,别人就可以 set key,从而拿到锁。
|
||||
但是这样是肯定不行的。因为如果是普通的 Redis 单实例,那就是单点故障。或者是 Redis 普通主从,那 Redis 主从异步复制,如果主节点挂了(key 就没有了),key 还没同步到从节点,此时从节点切换为主节点,别人就可以 set key,从而拿到锁。
|
||||
|
||||
#### RedLock 算法
|
||||
|
||||
这个场景是假设有一个 redis cluster,有 5 个 redis master 实例。然后执行如下步骤获取一把锁:
|
||||
这个场景是假设有一个 Redis cluster,有 5 个 Redis master 实例。然后执行如下步骤获取一把锁:
|
||||
|
||||
1. 获取当前时间戳,单位是毫秒;
|
||||
2. 跟上面类似,轮流尝试在每个 master 节点上创建锁,过期时间较短,一般就几十毫秒;
|
||||
@@ -196,7 +196,7 @@ public class ZooKeeperSession {
|
||||
|
||||
也可以采用另一种方式,创建临时顺序节点:
|
||||
|
||||
如果有一把锁,被多个人给竞争,此时多个人会排队,第一个拿到锁的人会执行,然后释放锁;后面的每个人都会去监听**排在自己前面**的那个人创建的 node 上,一旦某个人释放了锁,排在自己后面的人就会被 zookeeper 给通知,一旦被通知了之后,就 ok 了,自己就获取到了锁,就可以执行代码了。
|
||||
如果有一把锁,被多个人给竞争,此时多个人会排队,第一个拿到锁的人会执行,然后释放锁;后面的每个人都会去监听**排在自己前面**的那个人创建的 node 上,一旦某个人释放了锁,排在自己后面的人就会被 ZooKeeper 给通知,一旦被通知了之后,就 ok 了,自己就获取到了锁,就可以执行代码了。
|
||||
|
||||
``` java
|
||||
public class ZooKeeperDistributedLock implements Watcher {
|
||||
@@ -330,8 +330,8 @@ public class ZooKeeperDistributedLock implements Watcher {
|
||||
* redis 分布式锁,其实**需要自己不断去尝试获取锁**,比较消耗性能。
|
||||
* zk 分布式锁,获取不到锁,注册个监听器即可,不需要不断主动尝试获取锁,性能开销较小。
|
||||
|
||||
另外一点就是,如果是 redis 获取锁的那个客户端 出现 bug 挂了,那么只能等待超时时间之后才能释放锁;而 zk 的话,因为创建的是临时 znode,只要客户端挂了,znode 就没了,此时就自动释放锁。
|
||||
另外一点就是,如果是 Redis 获取锁的那个客户端 出现 bug 挂了,那么只能等待超时时间之后才能释放锁;而 zk 的话,因为创建的是临时 znode,只要客户端挂了,znode 就没了,此时就自动释放锁。
|
||||
|
||||
redis 分布式锁大家没发现好麻烦吗?遍历上锁,计算时间等等......zk 的分布式锁语义清晰实现简单。
|
||||
Redis 分布式锁大家没发现好麻烦吗?遍历上锁,计算时间等等......zk 的分布式锁语义清晰实现简单。
|
||||
|
||||
所以先不分析太多的东西,就说这两点,我个人实践认为 zk 的分布式锁比 redis 的分布式锁牢靠、而且模型简单易用。
|
||||
所以先不分析太多的东西,就说这两点,我个人实践认为 zk 的分布式锁比 Redis 的分布式锁牢靠、而且模型简单易用。
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
## 面试题
|
||||
集群部署时的分布式 session 如何实现?
|
||||
集群部署时的分布式 Session 如何实现?
|
||||
|
||||
## 面试官心理分析
|
||||
|
||||
面试官问了你一堆 dubbo 是怎么玩儿的,你会玩儿 dubbo 就可以把单块系统弄成分布式系统,然后分布式之后接踵而来的就是一堆问题,最大的问题就是**分布式事务**、**接口幂等性**、**分布式锁**,还有最后一个就是**分布式 session**。
|
||||
面试官问了你一堆 Dubbo 是怎么玩儿的,你会玩儿 Dubbo 就可以把单块系统弄成分布式系统,然后分布式之后接踵而来的就是一堆问题,最大的问题就是**分布式事务**、**接口幂等性**、**分布式锁**,还有最后一个就是**分布式 Session**。
|
||||
|
||||
当然了,分布式系统中的问题何止这么一点,非常之多,复杂度很高,这里只是说一下常见的几个问题,也是面试的时候常问的几个。
|
||||
|
||||
## 面试题剖析
|
||||
|
||||
session 是啥?浏览器有个 cookie,在一段时间内这个 cookie 都存在,然后每次发请求过来都带上一个特殊的 `jsessionid cookie` ,就根据这个东西,在服务端可以维护一个对应的 session 域,里面可以放点数据。
|
||||
Session 是啥?浏览器有个 Cookie,在一段时间内这个 Cookie 都存在,然后每次发请求过来都带上一个特殊的 `jsessionid cookie` ,就根据这个东西,在服务端可以维护一个对应的 Session 域,里面可以放点数据。
|
||||
|
||||
一般的话只要你没关掉浏览器,cookie 还在,那么对应的那个 session 就在,但是如果 cookie 没了,session 也就没了。常见于什么购物车之类的东西,还有登录状态保存之类的。
|
||||
一般的话只要你没关掉浏览器,Cookie 还在,那么对应的那个 Session 就在,但是如果 Cookie 没了,Session 也就没了。常见于什么购物车之类的东西,还有登录状态保存之类的。
|
||||
|
||||
这个不多说了,懂 Java 的都该知道这个。
|
||||
|
||||
单块系统的时候这么玩儿 session 没问题,但是你要是分布式系统呢,那么多的服务,session 状态在哪儿维护啊?
|
||||
单块系统的时候这么玩儿 Session 没问题,但是你要是分布式系统呢,那么多的服务,Session 状态在哪儿维护啊?
|
||||
|
||||
其实方法很多,但是常见常用的是以下几种:
|
||||
|
||||
### 完全不用 session
|
||||
### 完全不用 Session
|
||||
|
||||
使用 JWT Token 储存用户身份,然后再从数据库或者 cache 中获取其他的信息。这样无论请求分配到哪个服务器都无所谓。
|
||||
|
||||
### tomcat + redis
|
||||
### Tomcat + Redis
|
||||
|
||||
这个其实还挺方便的,就是使用 session 的代码,跟以前一样,还是基于 tomcat 原生的 session 支持即可,然后就是用一个叫做 `Tomcat RedisSessionManager` 的东西,让所有我们部署的 tomcat 都将 session 数据存储到 redis 即可。
|
||||
这个其实还挺方便的,就是使用 Session 的代码,跟以前一样,还是基于 Tomcat 原生的 Session 支持即可,然后就是用一个叫做 `Tomcat RedisSessionManager` 的东西,让所有我们部署的 Tomcat 都将 Session 数据存储到 Redis 即可。
|
||||
|
||||
在 tomcat 的配置文件中配置:
|
||||
在 Tomcat 的配置文件中配置:
|
||||
|
||||
``` xml
|
||||
<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
|
||||
@@ -39,7 +39,7 @@ session 是啥?浏览器有个 cookie,在一段时间内这个 cookie 都存
|
||||
maxInactiveInterval="60"/>
|
||||
```
|
||||
|
||||
然后指定 redis 的 host 和 port 就 ok 了。
|
||||
然后指定 Redis 的 host 和 port 就 ok 了。
|
||||
|
||||
```xml
|
||||
<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
|
||||
@@ -49,15 +49,15 @@ session 是啥?浏览器有个 cookie,在一段时间内这个 cookie 都存
|
||||
maxInactiveInterval="60"/>
|
||||
```
|
||||
|
||||
还可以用上面这种方式基于 redis 哨兵支持的 redis 高可用集群来保存 session 数据,都是 ok 的。
|
||||
还可以用上面这种方式基于 Redis 哨兵支持的 Redis 高可用集群来保存 Session 数据,都是 ok 的。
|
||||
|
||||
### spring session + redis
|
||||
### Spring Session + Redis
|
||||
|
||||
上面所说的第二种方式会与 tomcat 容器重耦合,如果我要将 web 容器迁移成 jetty,难道还要重新把 jetty 都配置一遍?
|
||||
上面所说的第二种方式会与 Tomcat 容器重耦合,如果我要将 Web 容器迁移成 Jetty,难道还要重新把 Jetty 都配置一遍?
|
||||
|
||||
因为上面那种 tomcat + redis 的方式好用,但是会**严重依赖于web容器**,不好将代码移植到其他 web 容器上去,尤其是你要是换了技术栈咋整?比如换成了 spring cloud 或者是 spring boot 之类的呢?
|
||||
因为上面那种 Tomcat + Redis 的方式好用,但是会**严重依赖于 Web 容器**,不好将代码移植到其他 Web 容器上去,尤其是你要是换了技术栈咋整?比如换成了 Spring Cloud 或者是 Spring Boot 之类的呢?
|
||||
|
||||
所以现在比较好的还是基于 Java 一站式解决方案,也就是 spring。人家 spring 基本上承包了大部分我们需要使用的框架,spirng cloud 做微服务,spring boot 做脚手架,所以用 sping session 是一个很好的选择。
|
||||
所以现在比较好的还是基于 Java 一站式解决方案,也就是 Spring。人家 Spring 基本上承包了大部分我们需要使用的框架,Spirng Cloud 做微服务,Spring Boot 做脚手架,所以用 Spring Session 是一个很好的选择。
|
||||
|
||||
在 pom.xml 中配置:
|
||||
|
||||
@@ -74,7 +74,7 @@ session 是啥?浏览器有个 cookie,在一段时间内这个 cookie 都存
|
||||
</dependency>
|
||||
```
|
||||
|
||||
在 spring 配置文件中配置:
|
||||
在 Spring 配置文件中配置:
|
||||
|
||||
``` xml
|
||||
<bean id="redisHttpSessionConfiguration"
|
||||
@@ -132,6 +132,6 @@ public class TestController {
|
||||
}
|
||||
```
|
||||
|
||||
上面的代码就是 ok 的,给 sping session 配置基于 redis 来存储 session 数据,然后配置了一个 spring session 的过滤器,这样的话,session 相关操作都会交给 spring session 来管了。接着在代码中,就用原生的 session 操作,就是直接基于 spring sesion 从 redis 中获取数据了。
|
||||
上面的代码就是 ok 的,给 Spring Session 配置基于 Redis 来存储 Session 数据,然后配置了一个 Spring Session 的过滤器,这样的话,Session 相关操作都会交给 Spring Session 来管了。接着在代码中,就用原生的 Session 操作,就是直接基于 Spring Session 从 Redis 中获取数据了。
|
||||
|
||||
实现分布式的会话有很多种方式,我说的只不过是比较常见的几种方式,tomcat + redis 早期比较常用,但是会重耦合到 tomcat 中;近些年,通过 spring session 来实现。
|
||||
实现分布式的会话有很多种方式,我说的只不过是比较常见的几种方式,Tomcat + Redis 早期比较常用,但是会重耦合到 Tomcat 中;近些年,通过 Spring Session 来实现。
|
||||
|
||||
@@ -25,6 +25,6 @@
|
||||
* 每次处理完请求之后,必须有一个记录标识这个请求处理过了。常见的方案是在 mysql 中记录个状态啥的,比如支付之前记录一条这个订单的支付流水。
|
||||
* 每次接收请求需要进行判断,判断之前是否处理过。比如说,如果有一个订单已经支付了,就已经有了一条支付流水,那么如果重复发送这个请求,则此时先插入支付流水,orderId 已经存在了,唯一键约束生效,报错插入不进去的。然后你就不用再扣款了。
|
||||
|
||||
实际运作过程中,你要结合自己的业务来,比如说利用 redis,用 orderId 作为唯一键。只有成功插入这个支付流水,才可以执行实际的支付扣款。
|
||||
实际运作过程中,你要结合自己的业务来,比如说利用 Redis,用 orderId 作为唯一键。只有成功插入这个支付流水,才可以执行实际的支付扣款。
|
||||
|
||||
要求是支付一个订单,必须插入一条支付流水,order_id 建一个唯一键 `unique key` 。你在支付一个订单之前,先插入一条支付流水,order_id 就已经进去了。你就可以写一个标识到 redis 里面去, `set order_id payed` ,下一次重复请求过来了,先查 redis 的 order_id 对应的 value,如果是 `payed` 就说明已经支付过了,你就别重复支付了。
|
||||
要求是支付一个订单,必须插入一条支付流水,order_id 建一个唯一键 `unique key` 。你在支付一个订单之前,先插入一条支付流水,order_id 就已经进去了。你就可以写一个标识到 Redis 里面去, `set order_id payed` ,下一次重复请求过来了,先查 Redis 的 order_id 对应的 value,如果是 `payed` 就说明已经支付过了,你就别重复支付了。
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
## 分布式系统面试连环炮
|
||||
有一些同学,之前呢主要是做传统行业,或者外包项目,一直是在那种小的公司,技术一直都搞的比较简单。他们有共同的一个问题,就是都没怎么搞过分布式系统,现在互联网公司,一般都是做分布式的系统,大家都不是做底层的分布式系统、分布式存储系统 hadoop hdfs、分布式计算系统 hadoop mapreduce / spark、分布式流式计算系统 storm。
|
||||
有一些同学,之前呢主要是做传统行业,或者外包项目,一直是在那种小的公司,技术一直都搞的比较简单。他们有共同的一个问题,就是都没怎么搞过分布式系统,现在互联网公司,一般都是做分布式的系统,大家都不是做底层的分布式系统、分布式存储系统 Hadoop HDFS、分布式计算系统 Hadoop MapReduce / Spark、分布式流式计算系统 Storm。
|
||||
|
||||
分布式业务系统,就是把原来用 Java 开发的一个大块系统,给拆分成**多个子系统**,多个子系统之间互相调用,形成一个大系统的整体。假设原来你做了一个 OA 系统,里面包含了权限模块、员工模块、请假模块、财务模块,一个工程,里面包含了一堆模块,模块与模块之间会互相去调用,1 台机器部署。现在如果你把这个系统给拆开,权限系统、员工系统、请假系统、财务系统 4 个系统,4 个工程,分别在 4 台机器上部署。一个请求过来,完成这个请求,这个员工系统,调用权限系统,调用请假系统,调用财务系统,4 个系统分别完成了一部分的事情,最后 4 个系统都干完了以后,才认为是这个请求已经完成了。
|
||||
|
||||

|
||||
|
||||
> 近几年开始兴起和流行 Spring Cloud,刚流行,还没开始普及,目前普及的是 dubbo,因此这里也主要讲 dubbo。
|
||||
> 近几年开始兴起和流行 Spring Cloud,刚流行,还没开始普及,目前普及的是 Dubbo,因此这里也主要讲 Dubbo。
|
||||
|
||||
面试官可能会问你以下问题。
|
||||
|
||||
### 为什么要进行系统拆分?
|
||||
|
||||
* 为什么要进行系统拆分?如何进行系统拆分?拆分后不用dubbo可以吗?dubbo和thrift有什么区别呢?
|
||||
* 为什么要进行系统拆分?如何进行系统拆分?拆分后不用 Dubbo 可以吗?Dubbo 和 thrift 有什么区别呢?
|
||||
|
||||
### 分布式服务框架
|
||||
|
||||
* 说一下的 dubbo 的工作原理?注册中心挂了可以继续通信吗?
|
||||
* dubbo 支持哪些序列化协议?说一下 hessian 的数据结构?PB 知道吗?为什么 PB 的效率是最高的?
|
||||
* dubbo 负载均衡策略和高可用策略都有哪些?动态代理策略呢?
|
||||
* dubbo 的 spi 思想是什么?
|
||||
* 如何基于 dubbo 进行服务治理、服务降级、失败重试以及超时重试?
|
||||
* 说一下的 Dubbo 的工作原理?注册中心挂了可以继续通信吗?
|
||||
* Dubbo 支持哪些序列化协议?说一下 Hessian 的数据结构?PB 知道吗?为什么 PB 的效率是最高的?
|
||||
* Dubbo 负载均衡策略和高可用策略都有哪些?动态代理策略呢?
|
||||
* Dubbo 的 SPI 思想是什么?
|
||||
* 如何基于 Dubbo 进行服务治理、服务降级、失败重试以及超时重试?
|
||||
* 分布式服务接口的幂等性如何设计(比如不能重复扣款)?
|
||||
* 分布式服务接口请求的顺序性如何保证?
|
||||
* 如何自己设计一个类似 dubbo 的 rpc 框架?
|
||||
* 如何自己设计一个类似 Dubbo 的 RPC 框架?
|
||||
|
||||
### 分布式锁
|
||||
|
||||
* 使用 redis 如何设计分布式锁?使用 zk 来设计分布式锁可以吗?这两种分布式锁的实现方式哪种效率比较高?
|
||||
* 使用 Redis 如何设计分布式锁?使用 zk 来设计分布式锁可以吗?这两种分布式锁的实现方式哪种效率比较高?
|
||||
|
||||
### 分布式事务
|
||||
|
||||
@@ -34,4 +34,4 @@
|
||||
|
||||
### 分布式会话
|
||||
|
||||
* 集群部署时的分布式 session 如何实现?
|
||||
* 集群部署时的分布式 Session 如何实现?
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
首先,一般来说,个人建议是,你们从业务逻辑上设计的这个系统最好是不需要这种顺序性的保证,因为一旦引入顺序性保障,比如使用**分布式锁**,会**导致系统复杂度上升**,而且会带来**效率低下**,热点数据压力过大等问题。
|
||||
|
||||
下面我给个我们用过的方案吧,简单来说,首先你得用 dubbo 的一致性 hash 负载均衡策略,将比如某一个订单 id 对应的请求都给分发到某个机器上去,接着就是在那个机器上,因为可能还是多线程并发执行的,你可能得立即将某个订单 id 对应的请求扔一个**内存队列**里去,强制排队,这样来确保他们的顺序性。
|
||||
下面我给个我们用过的方案吧,简单来说,首先你得用 Dubbo 的一致性 hash 负载均衡策略,将比如某一个订单 id 对应的请求都给分发到某个机器上去,接着就是在那个机器上,因为可能还是多线程并发执行的,你可能得立即将某个订单 id 对应的请求扔一个**内存队列**里去,强制排队,这样来确保他们的顺序性。
|
||||
|
||||

|
||||
|
||||
|
||||
Reference in New Issue
Block a user