mirror of
https://github.com/doocs/advanced-java.git
synced 2026-08-18 10:43:29 +08:00
docs: format document with prettier
全面整理项目内容,可读性更佳
This commit is contained in:
@@ -4,40 +4,40 @@
|
||||
|
||||
## 系统拆分
|
||||
|
||||
* [为什么要进行系统拆分?如何进行系统拆分?拆分后不用 Dubbo 可以吗?](/docs/distributed-system/why-dubbo.md)
|
||||
- [为什么要进行系统拆分?如何进行系统拆分?拆分后不用 Dubbo 可以吗?](/docs/distributed-system/why-dubbo.md)
|
||||
|
||||
## 分布式服务框架
|
||||
|
||||
* [说一下 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 进行服务治理、服务降级、失败重试以及超时重试?](/docs/distributed-system/dubbo-service-management.md)
|
||||
* [分布式服务接口的幂等性如何设计(比如不能重复扣款)?](/docs/distributed-system/distributed-system-idempotency.md)
|
||||
* [分布式服务接口请求的顺序性如何保证?](/docs/distributed-system/distributed-system-request-sequence.md)
|
||||
* [如何自己设计一个类似 Dubbo 的 RPC 框架?](/docs/distributed-system/dubbo-rpc-design.md)
|
||||
* [CAP定理的P是什么](/docs/distributed-system/distributed-system-cap.md)
|
||||
- [说一下 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 进行服务治理、服务降级、失败重试以及超时重试?](/docs/distributed-system/dubbo-service-management.md)
|
||||
- [分布式服务接口的幂等性如何设计(比如不能重复扣款)?](/docs/distributed-system/distributed-system-idempotency.md)
|
||||
- [分布式服务接口请求的顺序性如何保证?](/docs/distributed-system/distributed-system-request-sequence.md)
|
||||
- [如何自己设计一个类似 Dubbo 的 RPC 框架?](/docs/distributed-system/dubbo-rpc-design.md)
|
||||
- [CAP 定理的 P 是什么](/docs/distributed-system/distributed-system-cap.md)
|
||||
|
||||
## 分布式锁
|
||||
|
||||
* [Zookeeper 都有哪些应用场景?](/docs/distributed-system/zookeeper-application-scenarios.md)
|
||||
* [使用 Redis 如何设计分布式锁?使用 Zookeeper 来设计分布式锁可以吗?以上两种分布式锁的实现方式哪种效率比较高?](/docs/distributed-system/distributed-lock-redis-vs-zookeeper.md)
|
||||
- [Zookeeper 都有哪些应用场景?](/docs/distributed-system/zookeeper-application-scenarios.md)
|
||||
- [使用 Redis 如何设计分布式锁?使用 Zookeeper 来设计分布式锁可以吗?以上两种分布式锁的实现方式哪种效率比较高?](/docs/distributed-system/distributed-lock-redis-vs-zookeeper.md)
|
||||
|
||||
## 分布式事务
|
||||
|
||||
* [分布式事务了解吗?你们如何解决分布式事务问题的?TCC 如果出现网络连不通怎么办?XA 的一致性如何保证?](/docs/distributed-system/distributed-transaction.md)
|
||||
- [分布式事务了解吗?你们如何解决分布式事务问题的?TCC 如果出现网络连不通怎么办?XA 的一致性如何保证?](/docs/distributed-system/distributed-transaction.md)
|
||||
|
||||
## 分布式会话
|
||||
|
||||
* [集群部署时的分布式 Session 如何实现?](/docs/distributed-system/distributed-session.md)
|
||||
- [集群部署时的分布式 Session 如何实现?](/docs/distributed-system/distributed-session.md)
|
||||
|
||||
---
|
||||
|
||||
## 公众号
|
||||
|
||||
GitHub 技术社区 [Doocs](https://github.com/doocs) 旗下唯一公众号「**Doocs开源社区**」,欢迎扫码关注,**专注分享技术领域相关知识及行业最新资讯**。当然,也可以加我个人微信(备注:GitHub),拉你进技术交流群。
|
||||
GitHub 技术社区 [Doocs](https://github.com/doocs) 旗下唯一公众号「**Doocs 开源社区**」,欢迎扫码关注,**专注分享技术领域相关知识及行业最新资讯**。当然,也可以加我个人微信(备注:GitHub),拉你进技术交流群。
|
||||
|
||||
关注「**Doocs开源社区**」公众号,回复 **PDF**,即可获取本项目离线 PDF 文档(283 页精华),学习更加方便!
|
||||
关注「**Doocs 开源社区**」公众号,回复 **PDF**,即可获取本项目离线 PDF 文档(283 页精华),学习更加方便!
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
## 面试题
|
||||
|
||||
一般实现分布式锁都有哪些方式?使用 Redis 如何设计分布式锁?使用 zk 来设计分布式锁可以吗?这两种分布式锁的实现方式哪种效率比较高?
|
||||
|
||||
## 面试官心理分析
|
||||
@@ -13,9 +14,9 @@
|
||||
|
||||
这个分布式锁有 3 个重要的考量点:
|
||||
|
||||
* 互斥(只能有一个客户端获取锁)
|
||||
* 不能死锁
|
||||
* 容错(只要大部分 Redis 节点创建了这把锁就可以)
|
||||
- 互斥(只能有一个客户端获取锁)
|
||||
- 不能死锁
|
||||
- 容错(只要大部分 Redis 节点创建了这把锁就可以)
|
||||
|
||||
#### Redis 最普通的分布式锁
|
||||
|
||||
@@ -33,7 +34,7 @@ SET resource_name my_random_value PX 30000 NX
|
||||
|
||||
释放锁就是删除 key ,但是一般可以用 `lua` 脚本删除,判断 value 一样才删除:
|
||||
|
||||
``` lua
|
||||
```lua
|
||||
-- 删除锁的时候,找到 key 对应的 value,跟自己传过去的 value 做比较,如果是一样的才删除。
|
||||
if redis.call("get",KEYS[1]) == ARGV[1] then
|
||||
return redis.call("del",KEYS[1])
|
||||
@@ -65,7 +66,7 @@ end
|
||||
|
||||
zk 分布式锁,其实可以做的比较简单,就是某个节点尝试创建临时 znode,此时创建成功了就获取了这个锁;这个时候别的客户端来创建锁会失败,只能**注册个监听器**监听这个锁。释放锁就是删除这个 znode,一旦释放掉就会通知客户端,然后有一个等待着的客户端就可以再次重新加锁。
|
||||
|
||||
``` java
|
||||
```java
|
||||
/**
|
||||
* ZooKeeperSession
|
||||
*/
|
||||
@@ -93,7 +94,7 @@ public class ZooKeeperSession {
|
||||
|
||||
/**
|
||||
* 获取分布式锁
|
||||
*
|
||||
*
|
||||
* @param productId
|
||||
*/
|
||||
public Boolean acquireDistributedLock(Long productId) {
|
||||
@@ -126,7 +127,7 @@ public class ZooKeeperSession {
|
||||
|
||||
/**
|
||||
* 释放掉一个分布式锁
|
||||
*
|
||||
*
|
||||
* @param productId
|
||||
*/
|
||||
public void releaseDistributedLock(Long productId) {
|
||||
@@ -177,7 +178,7 @@ public class ZooKeeperSession {
|
||||
|
||||
/**
|
||||
* 获取单例
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static ZooKeeperSession getInstance() {
|
||||
@@ -198,7 +199,7 @@ public class ZooKeeperSession {
|
||||
|
||||
如果有一把锁,被多个人给竞争,此时多个人会排队,第一个拿到锁的人会执行,然后释放锁;后面的每个人都会去监听**排在自己前面**的那个人创建的 node 上,一旦某个人释放了锁,排在自己后面的人就会被 ZooKeeper 给通知,一旦被通知了之后,就 ok 了,自己就获取到了锁,就可以执行代码了。
|
||||
|
||||
``` java
|
||||
```java
|
||||
public class ZooKeeperDistributedLock implements Watcher {
|
||||
|
||||
private ZooKeeper zk;
|
||||
@@ -257,17 +258,17 @@ public class ZooKeeperDistributedLock implements Watcher {
|
||||
// locksRoot = locks
|
||||
// /locks/10000000000,/locks/10000000001,/locks/10000000002
|
||||
lockNode = zk.create(locksRoot + "/" + productId, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
|
||||
|
||||
|
||||
// 看看刚创建的节点是不是最小的节点
|
||||
// locks:10000000000,10000000001,10000000002
|
||||
List<String> locks = zk.getChildren(locksRoot, false);
|
||||
Collections.sort(locks);
|
||||
|
||||
|
||||
if(lockNode.equals(locksRoot+"/"+ locks.get(0))){
|
||||
//如果是最小的节点,则表示取得锁
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//如果不是最小的节点,找到比自己小1的节点
|
||||
int previousLockIndex = -1;
|
||||
for(int i = 0; i < locks.size(); i++) {
|
||||
@@ -276,7 +277,7 @@ public class ZooKeeperDistributedLock implements Watcher {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.waitNode = locks.get(previousLockIndex);
|
||||
} catch (KeeperException e) {
|
||||
throw new LockException(e);
|
||||
@@ -327,8 +328,8 @@ public class ZooKeeperDistributedLock implements Watcher {
|
||||
|
||||
### redis 分布式锁和 zk 分布式锁的对比
|
||||
|
||||
* redis 分布式锁,其实**需要自己不断去尝试获取锁**,比较消耗性能。
|
||||
* zk 分布式锁,获取不到锁,注册个监听器即可,不需要不断主动尝试获取锁,性能开销较小。
|
||||
- redis 分布式锁,其实**需要自己不断去尝试获取锁**,比较消耗性能。
|
||||
- zk 分布式锁,获取不到锁,注册个监听器即可,不需要不断主动尝试获取锁,性能开销较小。
|
||||
|
||||
另外一点就是,如果是 Redis 获取锁的那个客户端 出现 bug 挂了,那么只能等待超时时间之后才能释放锁;而 zk 的话,因为创建的是临时 znode,只要客户端挂了,znode 就没了,此时就自动释放锁。
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
## 面试题
|
||||
|
||||
集群部署时的分布式 Session 如何实现?
|
||||
|
||||
## 面试官心理分析
|
||||
@@ -25,11 +26,11 @@ Session 是啥?浏览器有个 Cookie,在一段时间内这个 Cookie 都存
|
||||
|
||||
### Tomcat + Redis
|
||||
|
||||
这个其实还挺方便的,就是使用 Session 的代码,跟以前一样,还是基于 Tomcat 原生的 Session 支持即可,然后就是用一个叫做 `Tomcat RedisSessionManager` 的东西,让所有我们部署的 Tomcat 都将 Session 数据存储到 Redis 即可。
|
||||
这个其实还挺方便的,就是使用 Session 的代码,跟以前一样,还是基于 Tomcat 原生的 Session 支持即可,然后就是用一个叫做 `Tomcat RedisSessionManager` 的东西,让所有我们部署的 Tomcat 都将 Session 数据存储到 Redis 即可。
|
||||
|
||||
在 Tomcat 的配置文件中配置:
|
||||
|
||||
``` xml
|
||||
```xml
|
||||
<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
|
||||
|
||||
<Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
|
||||
@@ -37,7 +38,7 @@ Session 是啥?浏览器有个 Cookie,在一段时间内这个 Cookie 都存
|
||||
port="{redis.port}"
|
||||
database="{redis.dbnum}"
|
||||
maxInactiveInterval="60"/>
|
||||
```
|
||||
```
|
||||
|
||||
然后指定 Redis 的 host 和 port 就 ok 了。
|
||||
|
||||
@@ -61,7 +62,7 @@ Session 是啥?浏览器有个 Cookie,在一段时间内这个 Cookie 都存
|
||||
|
||||
在 pom.xml 中配置:
|
||||
|
||||
``` xml
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.springframework.session</groupId>
|
||||
<artifactId>spring-session-data-redis</artifactId>
|
||||
@@ -76,7 +77,7 @@ Session 是啥?浏览器有个 Cookie,在一段时间内这个 Cookie 都存
|
||||
|
||||
在 Spring 配置文件中配置:
|
||||
|
||||
``` xml
|
||||
```xml
|
||||
<bean id="redisHttpSessionConfiguration"
|
||||
class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
|
||||
<property name="maxInactiveIntervalInSeconds" value="600"/>
|
||||
@@ -100,7 +101,7 @@ Session 是啥?浏览器有个 Cookie,在一段时间内这个 Cookie 都存
|
||||
|
||||
在 web.xml 中配置:
|
||||
|
||||
``` xml
|
||||
```xml
|
||||
<filter>
|
||||
<filter-name>springSessionRepositoryFilter</filter-name>
|
||||
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
|
||||
@@ -113,7 +114,7 @@ Session 是啥?浏览器有个 Cookie,在一段时间内这个 Cookie 都存
|
||||
|
||||
示例代码:
|
||||
|
||||
``` java
|
||||
```java
|
||||
@RestController
|
||||
@RequestMapping("/test")
|
||||
public class TestController {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
## 面试题
|
||||
|
||||
分布式服务接口的幂等性如何设计(比如不能重复扣款)?
|
||||
|
||||
## 面试官心理分析
|
||||
@@ -21,9 +22,9 @@
|
||||
|
||||
其实保证幂等性主要是三点:
|
||||
|
||||
* 对于每个请求必须有一个唯一的标识,举个栗子:订单支付请求,肯定得包含订单 id,一个订单 id 最多支付一次,对吧。
|
||||
* 每次处理完请求之后,必须有一个记录标识这个请求处理过了。常见的方案是在 mysql 中记录个状态啥的,比如支付之前记录一条这个订单的支付流水。
|
||||
* 每次接收请求需要进行判断,判断之前是否处理过。比如说,如果有一个订单已经支付了,就已经有了一条支付流水,那么如果重复发送这个请求,则此时先插入支付流水,orderId 已经存在了,唯一键约束生效,报错插入不进去的。然后你就不用再扣款了。
|
||||
- 对于每个请求必须有一个唯一的标识,举个栗子:订单支付请求,肯定得包含订单 id,一个订单 id 最多支付一次,对吧。
|
||||
- 每次处理完请求之后,必须有一个记录标识这个请求处理过了。常见的方案是在 mysql 中记录个状态啥的,比如支付之前记录一条这个订单的支付流水。
|
||||
- 每次接收请求需要进行判断,判断之前是否处理过。比如说,如果有一个订单已经支付了,就已经有了一条支付流水,那么如果重复发送这个请求,则此时先插入支付流水,orderId 已经存在了,唯一键约束生效,报错插入不进去的。然后你就不用再扣款了。
|
||||
|
||||
实际运作过程中,你要结合自己的业务来,比如说利用 Redis,用 orderId 作为唯一键。只有成功插入这个支付流水,才可以执行实际的支付扣款。
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
## 分布式系统面试连环炮
|
||||
|
||||
有一些同学,之前呢主要是做传统行业,或者外包项目,一直是在那种小的公司,技术一直都搞的比较简单。他们有共同的一个问题,就是都没怎么搞过分布式系统,现在互联网公司,一般都是做分布式的系统,大家都不是做底层的分布式系统、分布式存储系统 Hadoop HDFS、分布式计算系统 Hadoop MapReduce / Spark、分布式流式计算系统 Storm。
|
||||
|
||||
分布式业务系统,就是把原来用 Java 开发的一个大块系统,给拆分成**多个子系统**,多个子系统之间互相调用,形成一个大系统的整体。假设原来你做了一个 OA 系统,里面包含了权限模块、员工模块、请假模块、财务模块,一个工程,里面包含了一堆模块,模块与模块之间会互相去调用,1 台机器部署。现在如果你把这个系统给拆开,权限系统、员工系统、请假系统、财务系统 4 个系统,4 个工程,分别在 4 台机器上部署。一个请求过来,完成这个请求,这个员工系统,调用权限系统,调用请假系统,调用财务系统,4 个系统分别完成了一部分的事情,最后 4 个系统都干完了以后,才认为是这个请求已经完成了。
|
||||
@@ -11,27 +12,27 @@
|
||||
|
||||
### 为什么要进行系统拆分?
|
||||
|
||||
* 为什么要进行系统拆分?如何进行系统拆分?拆分后不用 Dubbo 可以吗?Dubbo 和 thrift 有什么区别呢?
|
||||
- 为什么要进行系统拆分?如何进行系统拆分?拆分后不用 Dubbo 可以吗?Dubbo 和 thrift 有什么区别呢?
|
||||
|
||||
### 分布式服务框架
|
||||
|
||||
* 说一下的 Dubbo 的工作原理?注册中心挂了可以继续通信吗?
|
||||
* Dubbo 支持哪些序列化协议?说一下 Hessian 的数据结构?PB 知道吗?为什么 PB 的效率是最高的?
|
||||
* Dubbo 负载均衡策略和高可用策略都有哪些?动态代理策略呢?
|
||||
* Dubbo 的 SPI 思想是什么?
|
||||
* 如何基于 Dubbo 进行服务治理、服务降级、失败重试以及超时重试?
|
||||
* 分布式服务接口的幂等性如何设计(比如不能重复扣款)?
|
||||
* 分布式服务接口请求的顺序性如何保证?
|
||||
* 如何自己设计一个类似 Dubbo 的 RPC 框架?
|
||||
- 说一下的 Dubbo 的工作原理?注册中心挂了可以继续通信吗?
|
||||
- Dubbo 支持哪些序列化协议?说一下 Hessian 的数据结构?PB 知道吗?为什么 PB 的效率是最高的?
|
||||
- Dubbo 负载均衡策略和高可用策略都有哪些?动态代理策略呢?
|
||||
- Dubbo 的 SPI 思想是什么?
|
||||
- 如何基于 Dubbo 进行服务治理、服务降级、失败重试以及超时重试?
|
||||
- 分布式服务接口的幂等性如何设计(比如不能重复扣款)?
|
||||
- 分布式服务接口请求的顺序性如何保证?
|
||||
- 如何自己设计一个类似 Dubbo 的 RPC 框架?
|
||||
|
||||
### 分布式锁
|
||||
|
||||
* 使用 Redis 如何设计分布式锁?使用 zk 来设计分布式锁可以吗?这两种分布式锁的实现方式哪种效率比较高?
|
||||
- 使用 Redis 如何设计分布式锁?使用 zk 来设计分布式锁可以吗?这两种分布式锁的实现方式哪种效率比较高?
|
||||
|
||||
### 分布式事务
|
||||
|
||||
* 分布式事务了解吗?你们如何解决分布式事务问题的?TCC 如果出现网络连不通怎么办?XA 的一致性如何保证?
|
||||
- 分布式事务了解吗?你们如何解决分布式事务问题的?TCC 如果出现网络连不通怎么办?XA 的一致性如何保证?
|
||||
|
||||
### 分布式会话
|
||||
|
||||
* 集群部署时的分布式 Session 如何实现?
|
||||
- 集群部署时的分布式 Session 如何实现?
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
## 面试题
|
||||
|
||||
分布式服务接口请求的顺序性如何保证?
|
||||
|
||||
## 面试官心理分析
|
||||
|
||||
@@ -37,9 +37,9 @@
|
||||
|
||||
TCC 的全称是: `Try` 、 `Confirm` 、 `Cancel` 。
|
||||
|
||||
* Try 阶段:这个阶段说的是对各个服务的资源做检测以及对资源进行**锁定或者预留**。
|
||||
* Confirm 阶段:这个阶段说的是在各个服务中**执行实际的操作**。
|
||||
* Cancel 阶段:如果任何一个服务的业务方法执行出错,那么这里就需要**进行补偿**,就是执行已经执行成功的业务逻辑的回滚操作。(把那些执行成功的回滚)
|
||||
- Try 阶段:这个阶段说的是对各个服务的资源做检测以及对资源进行**锁定或者预留**。
|
||||
- Confirm 阶段:这个阶段说的是在各个服务中**执行实际的操作**。
|
||||
- Cancel 阶段:如果任何一个服务的业务方法执行出错,那么这里就需要**进行补偿**,就是执行已经执行成功的业务逻辑的回滚操作。(把那些执行成功的回滚)
|
||||
|
||||
这种方案说实话几乎很少人使用,我们用的也比较少,但是也有使用的场景。因为这个**事务回滚**实际上是**严重依赖于你自己写代码来回滚和补偿**了,会造成补偿代码巨大,非常之恶心。
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
## 面试题
|
||||
|
||||
说一下的 dubbo 的工作原理?注册中心挂了可以继续通信吗?说说一次 rpc 请求的流程?
|
||||
|
||||
## 面试官心理分析
|
||||
@@ -9,29 +10,29 @@ MQ、ES、Redis、Dubbo,上来先问你一些**思考性的问题**、**原理
|
||||
|
||||
当然去年开始 spring cloud 非常火,现在大量的公司开始转向 spring cloud 了,spring cloud 人家毕竟是微服务架构的全家桶式的这么一个东西。但是因为很多公司还在用 dubbo,所以 dubbo 肯定会是目前面试的重点,何况人家 dubbo 现在重启开源社区维护了,捐献给了 apache,未来应该也还是有一定市场和地位的。
|
||||
|
||||
既然聊 dubbo,那肯定是先从 dubbo 原理开始聊了,你先说说 dubbo 支撑 rpc 分布式调用的架构啥的,然后说说一次 rpc 请求 dubbo 是怎么给你完成的,对吧。
|
||||
既然聊 dubbo,那肯定是先从 dubbo 原理开始聊了,你先说说 dubbo 支撑 rpc 分布式调用的架构啥的,然后说说一次 rpc 请求 dubbo 是怎么给你完成的,对吧。
|
||||
|
||||
## 面试题剖析
|
||||
|
||||
### dubbo 工作原理
|
||||
|
||||
* 第一层:service 层,接口层,给服务提供者和消费者来实现的
|
||||
* 第二层:config 层,配置层,主要是对 dubbo 进行各种配置的
|
||||
* 第三层:proxy 层,服务代理层,无论是 consumer 还是 provider,dubbo 都会给你生成代理,代理之间进行网络通信
|
||||
* 第四层:registry 层,服务注册层,负责服务的注册与发现
|
||||
* 第五层:cluster 层,集群层,封装多个服务提供者的路由以及负载均衡,将多个实例组合成一个服务
|
||||
* 第六层:monitor 层,监控层,对 rpc 接口的调用次数和调用时间进行监控
|
||||
* 第七层:protocal 层,远程调用层,封装 rpc 调用
|
||||
* 第八层:exchange 层,信息交换层,封装请求响应模式,同步转异步
|
||||
* 第九层:transport 层,网络传输层,抽象 mina 和 netty 为统一接口
|
||||
* 第十层:serialize 层,数据序列化层
|
||||
- 第一层:service 层,接口层,给服务提供者和消费者来实现的
|
||||
- 第二层:config 层,配置层,主要是对 dubbo 进行各种配置的
|
||||
- 第三层:proxy 层,服务代理层,无论是 consumer 还是 provider,dubbo 都会给你生成代理,代理之间进行网络通信
|
||||
- 第四层:registry 层,服务注册层,负责服务的注册与发现
|
||||
- 第五层:cluster 层,集群层,封装多个服务提供者的路由以及负载均衡,将多个实例组合成一个服务
|
||||
- 第六层:monitor 层,监控层,对 rpc 接口的调用次数和调用时间进行监控
|
||||
- 第七层:protocal 层,远程调用层,封装 rpc 调用
|
||||
- 第八层:exchange 层,信息交换层,封装请求响应模式,同步转异步
|
||||
- 第九层:transport 层,网络传输层,抽象 mina 和 netty 为统一接口
|
||||
- 第十层:serialize 层,数据序列化层
|
||||
|
||||
### 工作流程
|
||||
|
||||
* 第一步:provider 向注册中心去注册
|
||||
* 第二步:consumer 从注册中心订阅服务,注册中心会通知 consumer 注册好的服务
|
||||
* 第三步:consumer 调用 provider
|
||||
* 第四步:consumer 和 provider 都异步通知监控中心
|
||||
- 第一步:provider 向注册中心去注册
|
||||
- 第二步:consumer 从注册中心订阅服务,注册中心会通知 consumer 注册好的服务
|
||||
- 第三步:consumer 调用 provider
|
||||
- 第四步:consumer 和 provider 都异步通知监控中心
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
## 面试题
|
||||
|
||||
如何自己设计一个类似 Dubbo 的 RPC 框架?
|
||||
|
||||
## 面试官心理分析
|
||||
|
||||
说实话,就这问题,其实就跟问你如何自己设计一个 MQ 一样的道理,就考两个:
|
||||
|
||||
* 你有没有对某个 rpc 框架原理有非常深入的理解。
|
||||
* 你能不能从整体上来思考一下,如何设计一个 rpc 框架,考考你的系统设计能力。
|
||||
- 你有没有对某个 rpc 框架原理有非常深入的理解。
|
||||
- 你能不能从整体上来思考一下,如何设计一个 rpc 框架,考考你的系统设计能力。
|
||||
|
||||
## 面试题剖析
|
||||
|
||||
@@ -16,11 +17,11 @@
|
||||
|
||||
举个栗子,我给大家说个最简单的回答思路:
|
||||
|
||||
* 上来你的服务就得去注册中心注册吧,你是不是得有个注册中心,保留各个服务的信息,可以用 zookeeper 来做,对吧。
|
||||
* 然后你的消费者需要去注册中心拿对应的服务信息吧,对吧,而且每个服务可能会存在于多台机器上。
|
||||
* 接着你就该发起一次请求了,咋发起?当然是基于动态代理了,你面向接口获取到一个动态代理,这个动态代理就是接口在本地的一个代理,然后这个代理会找到服务对应的机器地址。
|
||||
* 然后找哪个机器发送请求?那肯定得有个负载均衡算法了,比如最简单的可以随机轮询是不是。
|
||||
* 接着找到一台机器,就可以跟它发送请求了,第一个问题咋发送?你可以说用 netty 了,nio 方式;第二个问题发送啥格式数据?你可以说用 hessian 序列化协议了,或者是别的,对吧。然后请求过去了。
|
||||
* 服务器那边一样的,需要针对你自己的服务生成一个动态代理,监听某个网络端口了,然后代理你本地的服务代码。接收到请求的时候,就调用对应的服务代码,对吧。
|
||||
- 上来你的服务就得去注册中心注册吧,你是不是得有个注册中心,保留各个服务的信息,可以用 zookeeper 来做,对吧。
|
||||
- 然后你的消费者需要去注册中心拿对应的服务信息吧,对吧,而且每个服务可能会存在于多台机器上。
|
||||
- 接着你就该发起一次请求了,咋发起?当然是基于动态代理了,你面向接口获取到一个动态代理,这个动态代理就是接口在本地的一个代理,然后这个代理会找到服务对应的机器地址。
|
||||
- 然后找哪个机器发送请求?那肯定得有个负载均衡算法了,比如最简单的可以随机轮询是不是。
|
||||
- 接着找到一台机器,就可以跟它发送请求了,第一个问题咋发送?你可以说用 netty 了,nio 方式;第二个问题发送啥格式数据?你可以说用 hessian 序列化协议了,或者是别的,对吧。然后请求过去了。
|
||||
- 服务器那边一样的,需要针对你自己的服务生成一个动态代理,监听某个网络端口了,然后代理你本地的服务代码。接收到请求的时候,就调用对应的服务代码,对吧。
|
||||
|
||||
这就是一个最最基本的 rpc 框架的思路,先不说你有多牛逼的技术功底,哪怕这个最简单的思路你先给出来行不行?
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
## 面试题
|
||||
|
||||
dubbo 支持哪些通信协议?支持哪些序列化协议?说一下 Hessian 的数据结构?PB 知道吗?为什么 PB 的效率是最高的?
|
||||
|
||||
## 面试官心理分析
|
||||
@@ -15,7 +16,7 @@ dubbo 支持哪些通信协议?支持哪些序列化协议?说一下 Hessian
|
||||
|
||||
### dubbo 支持不同的通信协议
|
||||
|
||||
* dubbo 协议
|
||||
- dubbo 协议
|
||||
|
||||
**默认**就是走 dubbo 协议,单一长连接,进行的是 NIO 异步通信,基于 hessian 作为序列化协议。使用的场景是:传输数据量小(每次请求在 100kb 以内),但是并发量很高。
|
||||
|
||||
@@ -29,19 +30,19 @@ dubbo 支持哪些通信协议?支持哪些序列化协议?说一下 Hessian
|
||||
|
||||

|
||||
|
||||
* rmi 协议
|
||||
- rmi 协议
|
||||
|
||||
走 Java 二进制序列化,多个短连接,适合消费者和提供者数量差不多的情况,适用于文件的传输,一般较少用。
|
||||
|
||||
* hessian 协议
|
||||
- hessian 协议
|
||||
|
||||
走 hessian 序列化协议,多个短连接,适用于提供者数量比消费者数量还多的情况,适用于文件的传输,一般较少用。
|
||||
|
||||
* http 协议
|
||||
- http 协议
|
||||
|
||||
走表单序列化。
|
||||
|
||||
* webservice
|
||||
- webservice
|
||||
|
||||
走 SOAP 文本序列化。
|
||||
|
||||
@@ -53,24 +54,24 @@ dubbo 支持 hession、Java 二进制序列化、json、SOAP 文本序列化多
|
||||
|
||||
Hessian 的对象序列化机制有 8 种原始类型:
|
||||
|
||||
* 原始二进制数据
|
||||
* boolean
|
||||
* 64-bit date(64 位毫秒值的日期)
|
||||
* 64-bit double
|
||||
* 32-bit int
|
||||
* 64-bit long
|
||||
* null
|
||||
* UTF-8 编码的 string
|
||||
- 原始二进制数据
|
||||
- boolean
|
||||
- 64-bit date(64 位毫秒值的日期)
|
||||
- 64-bit double
|
||||
- 32-bit int
|
||||
- 64-bit long
|
||||
- null
|
||||
- UTF-8 编码的 string
|
||||
|
||||
另外还包括 3 种递归类型:
|
||||
|
||||
* list for lists and arrays
|
||||
* map for maps and dictionaries
|
||||
* object for objects
|
||||
- list for lists and arrays
|
||||
- map for maps and dictionaries
|
||||
- object for objects
|
||||
|
||||
还有一种特殊的类型:
|
||||
|
||||
* ref:用来表示对共享对象的引用。
|
||||
- ref:用来表示对共享对象的引用。
|
||||
|
||||
### 为什么 PB 的效率是最高的?
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
## 面试题
|
||||
|
||||
如何基于 dubbo 进行服务治理、服务降级、失败重试以及超时重试?
|
||||
|
||||
## 面试官心理分析
|
||||
@@ -27,17 +28,17 @@
|
||||
|
||||
需要自动统计**各个接口和服务之间的调用次数以及访问延时**,而且要分成两个级别。
|
||||
|
||||
* 一个级别是接口粒度,就是每个服务的每个接口每天被调用多少次,TP50/TP90/TP99,三个档次的请求延时分别是多少;
|
||||
* 第二个级别是从源头入口开始,一个完整的请求链路经过几十个服务之后,完成一次请求,每天全链路走多少次,全链路请求延时的 TP50/TP90/TP99,分别是多少。
|
||||
- 一个级别是接口粒度,就是每个服务的每个接口每天被调用多少次,TP50/TP90/TP99,三个档次的请求延时分别是多少;
|
||||
- 第二个级别是从源头入口开始,一个完整的请求链路经过几十个服务之后,完成一次请求,每天全链路走多少次,全链路请求延时的 TP50/TP90/TP99,分别是多少。
|
||||
|
||||
这些东西都搞定了之后,后面才可以来看当前系统的压力主要在哪里,如何来扩容和优化啊。
|
||||
|
||||
#### 3. 其它
|
||||
|
||||
* 服务分层(避免循环依赖)
|
||||
* 调用链路失败监控和报警
|
||||
* 服务鉴权
|
||||
* 每个服务的可用性的监控(接口调用成功率?几个 9?99.99%,99.9%,99%)
|
||||
- 服务分层(避免循环依赖)
|
||||
- 调用链路失败监控和报警
|
||||
- 服务鉴权
|
||||
- 每个服务的可用性的监控(接口调用成功率?几个 9?99.99%,99.9%,99%)
|
||||
|
||||
### 服务降级
|
||||
|
||||
@@ -45,7 +46,7 @@
|
||||
|
||||
举个栗子,我们有接口 `HelloService` 。 `HelloServiceImpl` 有该接口的具体实现。
|
||||
|
||||
``` java
|
||||
```java
|
||||
public interface HelloService {
|
||||
void sayHello();
|
||||
}
|
||||
@@ -57,7 +58,7 @@ public class HelloServiceImpl implements HelloService {
|
||||
}
|
||||
```
|
||||
|
||||
``` xml
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
|
||||
@@ -92,7 +93,7 @@ public class HelloServiceImpl implements HelloService {
|
||||
|
||||
mock 的值也可以修改为 true,然后再跟接口同一个路径下实现一个 Mock 类,命名规则是 “接口名称+ `Mock` ” 后缀。然后在 Mock 类里实现自己的降级逻辑。
|
||||
|
||||
``` java
|
||||
```java
|
||||
public class HelloServiceMock implements HelloService {
|
||||
public void sayHello() {
|
||||
// 降级逻辑
|
||||
@@ -104,7 +105,7 @@ public class HelloServiceMock implements HelloService {
|
||||
|
||||
所谓失败重试,就是 consumer 调用 provider 要是失败了,比如抛异常了,此时应该是可以重试的,或者调用超时了也可以重试。配置如下:
|
||||
|
||||
``` xml
|
||||
```xml
|
||||
<dubbo:reference id="xxxx" interface="xx" check="true" async="false" retries="3" timeout="2000"/>
|
||||
```
|
||||
|
||||
@@ -114,5 +115,5 @@ public class HelloServiceMock implements HelloService {
|
||||
|
||||
可以结合你们公司具体的场景来说说你是怎么设置这些参数的:
|
||||
|
||||
* `timeout` :一般设置为 `200ms` ,我们认为不能超过 `200ms` 还没返回。
|
||||
* `retries` :设置 retries,一般是在读请求的时候,比如你要查询个数据,你可以设置个 retries,如果第一次没读到,报错,重试指定的次数,尝试再次读取。
|
||||
- `timeout` :一般设置为 `200ms` ,我们认为不能超过 `200ms` 还没返回。
|
||||
- `retries` :设置 retries,一般是在读请求的时候,比如你要查询个数据,你可以设置个 retries,如果第一次没读到,报错,重试指定的次数,尝试再次读取。
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
## 面试题
|
||||
|
||||
dubbo 的 spi 思想是什么?
|
||||
|
||||
## 面试官心理分析
|
||||
@@ -10,11 +11,12 @@ dubbo 的 spi 思想是什么?
|
||||
## 面试题剖析
|
||||
|
||||
### spi 是啥?
|
||||
|
||||
spi,简单来说,就是 `service provider interface` ,说白了是什么意思呢,比如你有个接口,现在这个接口有 3 个实现类,那么在系统运行的时候对这个接口到底选择哪个实现类呢?这就需要 spi 了,需要**根据指定的配置**或者是**默认的配置**,去**找到对应的实现类**加载进来,然后用这个实现类的实例对象。
|
||||
|
||||
举个栗子。
|
||||
|
||||
你有一个接口 A。A1/A2/A3 分别是接口A的不同实现。你通过配置 `接口 A = 实现 A2` ,那么在系统实际运行的时候,会加载你的配置,用实现 A2 实例化一个对象来提供服务。
|
||||
你有一个接口 A。A1/A2/A3 分别是接口 A 的不同实现。你通过配置 `接口 A = 实现 A2` ,那么在系统实际运行的时候,会加载你的配置,用实现 A2 实例化一个对象来提供服务。
|
||||
|
||||
spi 机制一般用在哪儿?**插件扩展的场景**,比如说你开发了一个给别人使用的开源框架,如果你想让别人自己写个插件,插到你的开源框架里面,从而扩展某个功能,这个时候 spi 思想就用上了。
|
||||
|
||||
@@ -32,7 +34,7 @@ Java 定义了一套 jdbc 的接口,但是 Java 并没有提供 jdbc 的实现
|
||||
|
||||
dubbo 也用了 spi 思想,不过没有用 jdk 的 spi 机制,是自己实现的一套 spi 机制。
|
||||
|
||||
``` java
|
||||
```java
|
||||
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
|
||||
```
|
||||
|
||||
@@ -42,26 +44,26 @@ Protocol 接口,在系统运行的时候,,dubbo 会判断一下应该选
|
||||
|
||||
上面那行代码就是 dubbo 里大量使用的,就是对很多组件,都是保留一个接口和多个实现,然后在系统运行的时候动态根据配置去找到对应的实现类。如果你没配置,那就走默认的实现好了,没问题。
|
||||
|
||||
``` java
|
||||
@SPI("dubbo")
|
||||
public interface Protocol {
|
||||
|
||||
int getDefaultPort();
|
||||
|
||||
@Adaptive
|
||||
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
|
||||
|
||||
@Adaptive
|
||||
<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
|
||||
```java
|
||||
@SPI("dubbo")
|
||||
public interface Protocol {
|
||||
|
||||
void destroy();
|
||||
|
||||
}
|
||||
int getDefaultPort();
|
||||
|
||||
@Adaptive
|
||||
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
|
||||
|
||||
@Adaptive
|
||||
<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
|
||||
|
||||
void destroy();
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
在 dubbo 自己的 jar 里,在 `/META_INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol` 文件中:
|
||||
|
||||
``` xml
|
||||
```xml
|
||||
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
|
||||
http=com.alibaba.dubbo.rpc.protocol.http.HttpProtocol
|
||||
hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol
|
||||
@@ -83,7 +85,7 @@ hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol
|
||||
|
||||
然后自己搞一个 `dubbo provider` 工程,在这个工程里面依赖你自己搞的那个 jar,然后在 spring 配置文件里给个配置:
|
||||
|
||||
``` xml
|
||||
```xml
|
||||
<dubbo:protocol name=”my” port=”20000” />
|
||||
```
|
||||
|
||||
@@ -93,4 +95,4 @@ provider 启动的时候,就会加载到我们 jar 包里的 `my=com.bingo.MyP
|
||||
|
||||
dubbo 里面提供了大量的类似上面的扩展点,就是说,你如果要扩展一个东西,只要自己写个 jar,让你的 consumer 或者是 provider 工程,依赖你的那个 jar,在你的 jar 里指定目录下配置好接口名称对应的文件,里面通过 `key=实现类` 。
|
||||
|
||||
然后对于对应的组件,类似 `<dubbo:protocol>` 用你的那个 key 对应的实现类来实现某个接口,你可以自己去扩展 dubbo 的各种功能,提供你自己的实现。
|
||||
然后对于对应的组件,类似 `<dubbo:protocol>` 用你的那个 key 对应的实现类来实现某个接口,你可以自己去扩展 dubbo 的各种功能,提供你自己的实现。
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
## 面试题
|
||||
|
||||
为什么要进行系统拆分?如何进行系统拆分?拆分后不用 dubbo 可以吗?
|
||||
|
||||
## 面试官心理分析
|
||||
@@ -20,6 +21,7 @@
|
||||
## 面试题剖析
|
||||
|
||||
### 为什么要将系统进行拆分?
|
||||
|
||||
网上查查,答案极度零散和复杂,很琐碎,原因一大坨。但是我这里给大家直观的感受:
|
||||
|
||||
要是**不拆分**,一个大系统几十万行代码,20 个人维护一份代码,简直是悲剧啊。代码经常改着改着就冲突了,各种代码冲突和合并要处理,非常耗费时间;经常我改动了我的代码,你调用了我的,导致你的代码也得重新测试,麻烦的要死;然后每次发布都是几十万行代码的系统一起发布,大家得一起提心吊胆准备上线,几十万行代码的上线,可能每次上线都要做很多的检查,很多异常问题的处理,简直是又麻烦又痛苦;而且如果我现在打算把技术升级到最新的 spring 版本,还不行,因为这可能导致你的代码报错,我不敢随意乱改技术。
|
||||
@@ -42,7 +44,7 @@ A 就检查了自己负责的 1 万行代码对应的功能,确保 ok 就闪
|
||||
|
||||
系统拆分为分布式系统,拆成多个服务,拆成微服务的架构,是需要拆很多轮的。并不是说上来一个架构师一次就给拆好了,而以后都不用拆。
|
||||
|
||||
第一轮;团队继续扩大,拆好的某个服务,刚开始是 1 个人维护 1 万行代码,后来业务系统越来越复杂,这个服务是 10 万行代码,5 个人;第二轮,1个服务 -> 5个服务,每个服务 2 万行代码,每人负责一个服务。
|
||||
第一轮;团队继续扩大,拆好的某个服务,刚开始是 1 个人维护 1 万行代码,后来业务系统越来越复杂,这个服务是 10 万行代码,5 个人;第二轮,1 个服务 -> 5 个服务,每个服务 2 万行代码,每人负责一个服务。
|
||||
|
||||
如果是多人维护一个服务,最理想的情况下,几十个人,1 个人负责 1 个或 2~3 个服务;某个服务工作量变大了,代码量越来越多,某个同学,负责一个服务,代码量变成了 10 万行了,他自己不堪重负,他现在一个人拆开,5 个服务,1 个人顶着,负责 5 个人,接着招人,2 个人,给那个同学带着,3 个人负责 5 个服务,其中 2 个人每个人负责 2 个服务,1 个人负责 1 个服务。
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
## 面试题
|
||||
|
||||
zookeeper 都有哪些使用场景?
|
||||
|
||||
## 面试官心理分析
|
||||
|
||||
现在聊的 topic 是分布式系统,面试官跟你聊完了 dubbo 相关的一些问题之后,已经确认你对分布式服务框架/RPC框架基本都有一些认知了。那么他可能开始要跟你聊分布式相关的其它问题了。
|
||||
现在聊的 topic 是分布式系统,面试官跟你聊完了 dubbo 相关的一些问题之后,已经确认你对分布式服务框架/RPC 框架基本都有一些认知了。那么他可能开始要跟你聊分布式相关的其它问题了。
|
||||
|
||||
分布式锁这个东西,很常用的,你做 Java 系统开发,分布式系统,可能会有一些场景会用到。最常用的分布式锁就是基于 zookeeper 来实现的。
|
||||
|
||||
@@ -13,10 +14,10 @@ zookeeper 都有哪些使用场景?
|
||||
|
||||
大致来说,zookeeper 的使用场景如下,我就举几个简单的,大家能说几个就好了:
|
||||
|
||||
* 分布式协调
|
||||
* 分布式锁
|
||||
* 元数据/配置信息管理
|
||||
* HA高可用性
|
||||
- 分布式协调
|
||||
- 分布式锁
|
||||
- 元数据/配置信息管理
|
||||
- HA 高可用性
|
||||
|
||||
### 分布式协调
|
||||
|
||||
@@ -36,7 +37,7 @@ zookeeper 可以用作很多系统的配置信息的管理,比如 kafka、stor
|
||||
|
||||

|
||||
|
||||
### HA高可用性
|
||||
### HA 高可用性
|
||||
|
||||
这个应该是很常见的,比如 hadoop、hdfs、yarn 等很多大数据系统,都选择基于 zookeeper 来开发 HA 高可用机制,就是一个**重要进程一般会做主备**两个,主进程挂了立马通过 zookeeper 感知到切换到备用进程。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user