Skip to content

Commit

Permalink
docs: update UUID desc to fix doocs#22, rename images
Browse files Browse the repository at this point in the history
- Update UUID desc to fix doocs#22
- Rename img to images
- Fix typo
  • Loading branch information
yanglbme committed Jan 7, 2019
1 parent 1caf071 commit a7cb243
Show file tree
Hide file tree
Showing 248 changed files with 93 additions and 94 deletions.
2 changes: 1 addition & 1 deletion _coverpage.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
![logo](img/icon.png)
![logo](images/icon.png)

# Java 进阶扫盲

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ end
5. 要是锁建立失败了,那么就依次之前建立过的锁删除;
6. 只要别人建立了一把分布式锁,你就得**不断轮询去尝试获取锁**

![redis-redlock](/img/redis-redlock.png)
![redis-redlock](/images/redis-redlock.png)

### zk 分布式锁

Expand Down
2 changes: 1 addition & 1 deletion docs/distributed-system/distributed-system-interview.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

分布式业务系统,就是把原来用 Java 开发的一个大块系统,给拆分成**多个子系统**,多个子系统之间互相调用,形成一个大系统的整体。假设原来你做了一个 OA 系统,里面包含了权限模块、员工模块、请假模块、财务模块,一个工程,里面包含了一堆模块,模块与模块之间会互相去调用,1 台机器部署。现在如果你把这个系统给拆开,权限系统、员工系统、请假系统、财务系统 4 个系统,4 个工程,分别在 4 台机器上部署。一个请求过来,完成这个请求,这个员工系统,调用权限系统,调用请假系统,调用财务系统,4 个系统分别完成了一部分的事情,最后 4 个系统都干完了以后,才认为是这个请求已经完成了。

![simple-distributed-system-oa](/img/simple-distributed-system-oa.png)
![simple-distributed-system-oa](/images/simple-distributed-system-oa.png)

> 这两年开始兴起和流行 Spring Cloud,刚流行,还没开始普及,目前普及的是 dubbo,因此这里也主要讲 dubbo。
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@

所以这都是分布式系统一些很常见的问题。


## 面试题剖析
首先,一般来说,个人建议是,你们从业务逻辑上设计的这个系统最好是不需要这种顺序性的保证,因为一旦引入顺序性保障,比如使用**分布式锁**,会**导致系统复杂度上升**,而且会带来**效率低下**,热点数据压力过大等问题。

下面我给个我们用过的方案吧,简单来说,首先你得用 dubbo 的一致性 hash 负载均衡策略,将比如某一个订单 id 对应的请求都给分发到某个机器上去,接着就是在那个机器上因为可能还是多线程并发执行的,你可能得立即将某个订单 id 对应的请求扔一个**内存队列**里去,强制排队,这样来确保他们的顺序性。

![distributed-system-request-sequence](/img/distributed-system-request-sequence.png)
![distributed-system-request-sequence](/images/distributed-system-request-sequence.png)

但是这样引发的后续问题就很多,比如说要是某个订单对应的请求特别多,造成某台机器成**热点**怎么办?解决这些问题又要开启后续一连串的复杂技术方案......曾经这类问题弄的我们头疼不已,所以,还是建议什么呢?

Expand Down
8 changes: 4 additions & 4 deletions docs/distributed-system/distributed-transaction.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

如果你要操作别人的服务的库,你必须是通过**调用别的服务的接口**来实现,绝对不允许交叉访问别人的数据库。

![distributed-transacion-XA](/img/distributed-transaction-XA.png)
![distributed-transacion-XA](/images/distributed-transaction-XA.png)

### TCC 方案
TCC 的全称是:Try、Confirm、Cancel。
Expand All @@ -43,7 +43,7 @@ TCC 的全称是:Try、Confirm、Cancel。

但是说实话,一般尽量别这么搞,自己手写回滚逻辑,或者是补偿逻辑,实在太恶心了,那个业务代码很难维护。

![distributed-transacion-TCC](/img/distributed-transaction-TCC.png)
![distributed-transacion-TCC](/images/distributed-transaction-TCC.png)

### 本地消息表
本地消息表其实是国外的 ebay 搞出来的这么一套思想。
Expand All @@ -59,7 +59,7 @@ TCC 的全称是:Try、Confirm、Cancel。

这个方案说实话最大的问题就在于**严重依赖于数据库的消息表来管理事务**啥的,会导致如果是高并发场景咋办呢?咋扩展呢?所以一般确实很少用。

![distributed-transaction-local-message-table](/img/distributed-transaction-local-message-table.png)
![distributed-transaction-local-message-table](/images/distributed-transaction-local-message-table.png)

### 可靠消息最终一致性方案
这个的意思,就是干脆不要用本地的消息表了,直接基于 MQ 来实现事务。比如阿里的 RocketMQ 就支持消息事务。
Expand All @@ -73,7 +73,7 @@ TCC 的全称是:Try、Confirm、Cancel。
5. 这个方案里,要是系统 B 的事务失败了咋办?重试咯,自动不断重试直到成功,如果实在是不行,要么就是针对重要的资金类业务进行回滚,比如 B 系统本地回滚后,想办法通知系统 A 也回滚;或者是发送报警由人工来手工回滚和补偿。
6. 这个还是比较合适的,目前国内互联网公司大都是这么玩儿的,要不你举用 RocketMQ 支持的,要不你就自己基于类似 ActiveMQ?RabbitMQ?自己封装一套类似的逻辑出来,总之思路就是这样子的。

![distributed-transaction-reliable-message](/img/distributed-transaction-reliable-message.png)
![distributed-transaction-reliable-message](/images/distributed-transaction-reliable-message.png)

### 最大努力通知方案
这个方案的大致意思就是:
Expand Down
2 changes: 1 addition & 1 deletion docs/distributed-system/dubbo-operating-principle.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ MQ、ES、Redis、Dubbo,上来先问你一些思考的问题,原理(kafka
- 第三步:consumer 调用 provider
- 第四步:consumer 和 provider 都异步通知监控中心

![dubbo-operating-principle](/img/dubbo-operating-principle.png)
![dubbo-operating-principle](/images/dubbo-operating-principle.png)

### 注册中心挂了可以继续通信吗?
可以,因为刚开始初始化的时候,消费者会将提供者的地址等信息**拉取到本地缓存**,所以注册中心挂了可以继续通信。
2 changes: 1 addition & 1 deletion docs/distributed-system/dubbo-service-management.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

那就需要基于 dubbo 做的分布式系统中,对各个服务之间的调用自动记录下来,然后自动将**各个服务之间的依赖关系和调用链路生成出来**,做成一张图,显示出来,大家才可以看到对吧。

![dubbo-service-invoke-road](/img/dubbo-service-invoke-road.png)
![dubbo-service-invoke-road](/images/dubbo-service-invoke-road.png)

#### 2. 服务访问压力以及时长统计
需要自动统计**各个接口和服务之间的调用次数以及访问延时**,而且要分成两个级别。
Expand Down
2 changes: 1 addition & 1 deletion docs/distributed-system/dubbo-spi.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol

provider 启动的时候,就会加载到我们 jar 包里的`my=com.bingo.MyProtocol` 这行配置里,接着会根据你的配置使用你定义好的 MyProtocol 了,这个就是简单说明一下,你通过上述方式,可以替换掉大量的 dubbo 内部的组件,就是扔个你自己的 jar 包,然后配置一下即可。

![dubbo-spi](/img/dubbo-spi.png)
![dubbo-spi](/images/dubbo-spi.png)

dubbo 里面提供了大量的类似上面的扩展点,就是说,你如果要扩展一个东西,只要自己写个 jar,让你的 consumer 或者是 provider 工程,依赖你的那个 jar,在你的 jar 里指定目录下配置好接口名称对应的文件,里面通过 `key=实现类`

Expand Down
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
22 changes: 11 additions & 11 deletions docs/distributed-system/zookeeper-application-scenarios.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,34 @@ zookeeper 都有哪些使用场景?
## 面试官心理分析
现在聊的 topic 是分布式系统,面试官跟你聊完了 dubbo 相关的一些问题之后,已经确认你对分布式服务框架/RPC框架基本都有一些认知了。那么他可能开始要跟你聊分布式相关的其它问题了。

分布式锁这个东西,很常用的,你做 Java系统开发,分布式系统,可能会有一些场景会用到。最常用的分布式锁就是基于 zookeeper 来实现的。
分布式锁这个东西,很常用的,你做 Java 系统开发,分布式系统,可能会有一些场景会用到。最常用的分布式锁就是基于 zookeeper 来实现的。

其实说实话,问这个问题,一般就是看看你是否了解 zookeeper,因为 zk 是分布式系统中很常见的一个基础系统。而且问的话常问的就是说 zk 的使用场景是什么?看你知道不知道一些基本的使用场景。但是其实 zk 挖深了自然是可以问的很深很深的。
其实说实话,问这个问题,一般就是看看你是否了解 zookeeper,因为 zookeeper 是分布式系统中很常见的一个基础系统。而且问的话常问的就是说 zookeeper 的使用场景是什么?看你知道不知道一些基本的使用场景。但是其实 zookeeper 挖深了自然是可以问的很深很深的。

## 面试题剖析
大致来说,zk 的使用场景如下,我就举几个简单的,大家能说几个就好了:
大致来说,zookeeper 的使用场景如下,我就举几个简单的,大家能说几个就好了:

- 分布式协调
- 分布式锁
- 元数据/配置信息管理
- HA高可用性

### 分布式协调
这个其实是 zk 很经典的一个用法,简单来说,就好比,你 A 系统发送个请求到 mq,然后 B 系统消息消费之后处理了。那 A 系统如何知道 B 系统的处理结果?用 zk 就可以实现分布式系统之间的协调工作。A 系统发送请求之后可以在 zk**对某个节点的值注册个监听器**,一旦 B 系统处理完了就修改 zk 那个节点的值,A 立马就可以收到通知,完美解决。
这个其实是 zookeeper 很经典的一个用法,简单来说,就好比,你 A 系统发送个请求到 mq,然后 B 系统消息消费之后处理了。那 A 系统如何知道 B 系统的处理结果?用 zookeeper 就可以实现分布式系统之间的协调工作。A 系统发送请求之后可以在 zookeeper**对某个节点的值注册个监听器**,一旦 B 系统处理完了就修改 zookeeper 那个节点的值,A 立马就可以收到通知,完美解决。

![zookeeper-distributed-coordination](/img/zookeeper-distributed-coordination.png)
![zookeeper-distributed-coordination](/images/zookeeper-distributed-coordination.png)

### 分布式锁
举个栗子。对某一个数据连续发出两个修改操作,两台机器同时收到了请求,但是只能一台机器先执行完另外一个机器再执行。那么此时就可以使用 zk 分布式锁,一个机器接收到了请求之后先获取 zk 上的一把分布式锁,就是可以去创建一个 znode,接着执行操作;然后另外一个机器也**尝试去创建**那个 znode,结果发现自己创建不了,因为被别人创建了,那只能等着,等第一个机器执行完了自己再执行。
举个栗子。对某一个数据连续发出两个修改操作,两台机器同时收到了请求,但是只能一台机器先执行完另外一个机器再执行。那么此时就可以使用 zookeeper 分布式锁,一个机器接收到了请求之后先获取 zookeeper 上的一把分布式锁,就是可以去创建一个 znode,接着执行操作;然后另外一个机器也**尝试去创建**那个 znode,结果发现自己创建不了,因为被别人创建了,那只能等着,等第一个机器执行完了自己再执行。

![zookeeper-distributed-lock-demo](/img/zookeeper-distributed-lock-demo.png)
![zookeeper-distributed-lock-demo](/images/zookeeper-distributed-lock-demo.png)

### 元数据/配置信息管理
zk 可以用作很多系统的配置信息的管理,比如 kafka、storm 等等很多分布式系统都会选用 zk 来做一些元数据、配置信息的管理,包括 dubbo 注册中心不也支持 zk 么?
zookeeper 可以用作很多系统的配置信息的管理,比如 kafka、storm 等等很多分布式系统都会选用 zookeeper 来做一些元数据、配置信息的管理,包括 dubbo 注册中心不也支持 zookeeper 么?

![zookeeper-meta-data-manage](/img/zookeeper-meta-data-manage.png)
![zookeeper-meta-data-manage](/images/zookeeper-meta-data-manage.png)

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

![zookeeper-active-standby](/img/zookeeper-active-standby.png)
![zookeeper-active-standby](/images/zookeeper-active-standby.png)
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
### 小型电商网站的商品详情页系统架构
小型电商网站的页面展示采用页面全量静态化的思想。数据库中存放了所有的商品信息,页面静态化系统,将数据填充进静态模板中,形成静态化页面,推入 Nginx 服务器。用户浏览网站页面时,取用一个已经静态化好的 html 页面,直接返回回去,不涉及任何的业务逻辑处理。

![e-commerce-website-detail-page-architecture-1](/img/e-commerce-website-detail-page-architecture-1.png)
![e-commerce-website-detail-page-architecture-1](/images/e-commerce-website-detail-page-architecture-1.png)

- 好处:用户每次浏览一个页面,不需要进行任何的跟数据库的交互逻辑,也不需要执行任何的代码,直接返回一个 html 页面就可以了,速度和性能非常高。
- 坏处:仅仅适用于一些小型的网站,比如页面的规模在几十到几万不等。对于一些大型的电商网站,亿级数量的页面,你说你每次页面模板修改了,都需要将这么多页面全量静态化,靠谱吗?
Expand All @@ -13,7 +13,7 @@

用户浏览网页时,动态将 Nginx 本地数据渲染到本地 html 模板并返回给用户。

![e-commerce-website-detail-page-architecture-2](/img/e-commerce-website-detail-page-architecture-2.png)
![e-commerce-website-detail-page-architecture-2](/images/e-commerce-website-detail-page-architecture-2.png)


虽然没有直接返回 html 页面那么快,但是因为数据在本地缓存,所以也很快,其实耗费的也就是动态渲染一个 html 页面的性能。如果 html 模板发生了变更,不需要将所有的页面重新静态化,直接将数据渲染进最新的 html 页面模板后响应即可。
Expand Down
2 changes: 1 addition & 1 deletion docs/high-availability/hystrix-execution-isolation.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ HystrixThreadPoolProperties.Setter().withCoreSize(int value);
### queueSizeRejectionThreshold
如果说线程池中的 10 个线程都在工作中,没有空闲的线程来做其它的事情,此时再有请求过来,会先进入队列积压。如果说队列积压满了,再有请求过来,就直接 reject,拒绝请求,执行 fallback 降级的逻辑,快速返回。

![hystrix-thread-pool-queue](/img/hystrix-thread-pool-queue.png)
![hystrix-thread-pool-queue](/images/hystrix-thread-pool-queue.png)

控制 queue 满了之后 reject 的 threshold,因为 maxQueueSize 不允许热修改,因此提供这个参数可以热修改,控制队列的最大大小。

Expand Down
2 changes: 1 addition & 1 deletion docs/high-availability/hystrix-introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Hystrix 是高可用性保障的一个框架。Netflix(可以认为是国外

调用服务 C,只需要 20ms,现在因为服务 C 故障了,比如延迟,或者挂了,此时线程会 hang 住 2s 左右。40 个线程全部被卡住,由于请求不断涌入,其它的线程也用来调用服务 C,同样也会被卡住。这样导致服务 B 的线程资源被耗尽,无法接收新的请求,甚至可能因为大量线程不断的运转,导致自己宕机。服务 A 也挂。

![service-invoke-road](/img/service-invoke-road.png)
![service-invoke-road](/images/service-invoke-road.png)

Hystrix 可以对其进行资源隔离,比如限制服务 B 只有 40 个线程调用服务 C。当此 40 个线程被 hang 住时,其它 60 个线程依然能正常调用工作。从而确保整个系统不会被拖垮。

Expand Down
6 changes: 3 additions & 3 deletions docs/high-availability/hystrix-process.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

这里是整个 8 大步骤的流程图,我会对每个步骤进行细致的讲解。学习的过程中,对照着这个流程图,相信思路会比较清晰。

![hystrix-process](/img/hystrix-process.png)
![hystrix-process](/images/hystrix-process.png)

### 步骤一:创建 command
一个 HystrixCommand 或 HystrixObservableCommand 对象,代表了对某个依赖服务发起的一次请求或者调用。创建的时候,可以在构造函数中传入任何需要的参数。
Expand Down Expand Up @@ -64,7 +64,7 @@ final Future<R> delegate = toObservable().toBlocking().toFuture();

也就是说,先通过 toObservable() 获得 Future 对象,然后调用 Future 的 get() 方法。那么,其实无论是哪种方式执行 command,最终都是依赖于 toObservable() 去执行的。

![hystrix-process](/img/hystrix-process.png)
![hystrix-process](/images/hystrix-process.png)

### 步骤三:检查是否开启缓存
从这一步开始,就进入到 Hystrix 底层运行原理啦,看一下 Hystrix 一些更高级的功能和特性。
Expand Down Expand Up @@ -121,7 +121,7 @@ observable.subscribe(new Observer<ProductInfo>() {

如果没有 timeout,也正常执行的话,那么调用线程就会拿到一些调用依赖服务获取到的结果,然后 Hystrix 也会做一些 logging 记录和 metric 度量统计。

![hystrix-process](/img/hystrix-process.png)
![hystrix-process](/images/hystrix-process.png)

### 步骤七:断路健康检查
Hystrix 会把每一个依赖服务的调用成功、失败、Reject、Timeout 等事件发送给 circuit breaker 断路器。断路器就会对这些事件的次数进行统计,根据异常事件发生的比例来决定是否要进行断路(熔断)。如果打开了断路器,那么在接下来一段时间内,会直接断路,返回降级结果。
Expand Down
2 changes: 1 addition & 1 deletion docs/high-availability/hystrix-request-cache.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Hystrix command 执行时 8 大步骤第三步,就是检查 Request cache 是

举个栗子。比如说我们在一次请求上下文中,请求获取 productId 为 1 的数据,第一次缓存中没有,那么会从商品服务中获取数据,返回最新数据结果,同时将数据缓存在内存中。后续同一次请求上下文中,如果还有获取 productId 为 1 的数据的请求,直接从缓存中取就好了。

![hystrix-request-cache](/img/hystrix-request-cache.png)
![hystrix-request-cache](/images/hystrix-request-cache.png)

HystrixCommand 和 HystrixObservableCommand 都可以指定一个缓存 key,然后 Hystrix 会自动进行缓存,接着在同一个 request context 内,再次访问的话,就会直接取用缓存。

Expand Down
4 changes: 2 additions & 2 deletions docs/high-availability/hystrix-semphore-isolation.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ Hystrix 实现资源隔离,主要有两种技术:
### 信号量机制
信号量的资源隔离只是起到一个开关的作用,比如,服务 A 的信号量大小为 10,那么就是说它同时只允许有 10 个 tomcat 线程来访问服务 A,其它的请求都会被拒绝,从而达到资源隔离和限流保护的作用。

![hystrix-semphore](/img/hystrix-semphore.png)
![hystrix-semphore](/images/hystrix-semphore.png)

### 线程池与信号量区别
线程池隔离技术,并不是说去控制类似 tomcat 这种 web 容器的线程。更加严格的意义上来说,Hystrix 的线程池隔离技术,控制的是 tomcat 线程的执行。Hystrix 线程池满后,会确保说,tomcat 的线程不会因为依赖服务的接口调用延迟或故障而被 hang 住,tomcat 其它的线程不会卡死,可以快速返回,然后支撑其它的事情。

线程池隔离技术,是用 Hystrix 自己的线程去执行调用;而信号量隔离技术,是直接让 tomcat 线程去调用依赖服务。信号量隔离,只是一道关卡,信号量有多少,就允许多少个 tomcat 线程通过它,然后去执行。

![hystrix-semphore-thread-pool](/img/hystrix-semphore-thread-pool.png)
![hystrix-semphore-thread-pool](/images/hystrix-semphore-thread-pool.png)

**适用场景**
- **线程池技术**,适合绝大多数场景,比如说我们对依赖服务的网络请求的调用和访问、需要对调用的 timeout 进行控制(捕捉 timeout 超时异常)。
Expand Down
Loading

0 comments on commit a7cb243

Please sign in to comment.