dubbo.md 29.2 KB
Newer Older
1 2
# Dubbo知识点&面试题总结

G
guide 已提交
3
这篇文章是我根据官方文档以及自己平时的使用情况,对 Dubbo 所做的一个总结。欢迎补充!
S
Snailclimb 已提交
4

G
guide 已提交
5
## RPC基础
S
Snailclimb 已提交
6

G
guide 已提交
7
### 何为 RPC?
S
Snailclimb 已提交
8

G
guide 已提交
9
**RPC(Remote Procedure Call)** 即远程过程调用,通过名字我们就能看出 RPC 关注的是远程调用而非本地调用。
S
Snailclimb 已提交
10

G
guide 已提交
11
**为什么要 RPC  ?** 因为,两个不同的服务器上的服务提供的方法不在一个内存空间,所以,需要通过网络编程才能传递方法调用所需要的参数。并且,方法调用的结果也需要通过网络编程来接收。但是,如果我们自己手动网络编程来实现这个调用过程的话工作量是非常大的,因为,我们需要考虑底层传输方式(TCP还是UDP)、序列化方式等等方面。
S
Snailclimb 已提交
12 13


iDeputy's avatar
iDeputy 已提交
14
**RPC 能帮助我们做什么呢?** 简单来说,通过 RPC 可以帮助我们调用远程计算机上某个服务的方法,这个过程就像调用本地方法一样简单。并且!我们不需要了解底层网络编程的具体细节。
S
Snailclimb 已提交
15 16


G
guide 已提交
17
举个例子:两个不同的服务 A、B 部署在两台不同的机器上,服务 A 如果想要调用服务 B 中的某个方法的话就可以通过 RPC 来做。
S
Snailclimb 已提交
18

G
guide 已提交
19
一言蔽之:**RPC 的出现就是为了让你调用远程方法像调用本地方法一样简单。**
S
Snailclimb 已提交
20

G
guide 已提交
21
### RPC 的原理是什么?
S
Snailclimb 已提交
22

G
guide 已提交
23
为了能够帮助小伙伴们理解 RPC 原理,我们可以将整个 RPC的 核心功能看作是下面👇 6 个部分实现的:
S
Snailclimb 已提交
24 25


G
guide 已提交
26 27 28 29 30
1. **客户端(服务消费端)** :调用远程方法的一端。
1. **客户端 Stub(桩)** : 这其实就是一代理类。代理类主要做的事情很简单,就是把你调用方法、类、方法参数等信息传递到服务端。
1. **网络传输** : 网络传输就是你要把你调用的方法的信息比如说参数啊这些东西传输到服务端,然后服务端执行完之后再把返回结果通过网络传输给你传输回来。网络传输的实现方式有很多种比如最近基本的 Socket或者性能以及封装更加优秀的 Netty(推荐)。
1. **服务端 Stub(桩)** :这个桩就不是代理类了。我觉得理解为桩实际不太好,大家注意一下就好。这里的服务端 Stub 实际指的就是接收到客户端执行方法的请求后,去指定对应的方法然后返回结果给客户端的类。
1. **服务端(服务提供端)** :提供远程方法的一端。
S
Snailclimb 已提交
31

G
guide 已提交
32
具体原理图如下,后面我会串起来将整个RPC的过程给大家说一下。
S
Snailclimb 已提交
33 34


谭九鼎 已提交
35
![RPC原理图](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-6/37345851.jpg)
S
Snailclimb 已提交
36

G
guide 已提交
37 38 39 40 41 42 43
1. 服务消费端(client)以本地调用的方式调用远程服务;
1. 客户端 Stub(client stub) 接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体(序列化):`RpcRequest`
1. 客户端 Stub(client stub) 找到远程服务的地址,并将消息发送到服务提供端;
1. 服务端 Stub(桩)收到消息将消息反序列化为Java对象: `RpcRequest`
1. 服务端 Stub(桩)根据`RpcRequest`中的类、方法、方法参数等信息调用本地的方法;
1. 服务端 Stub(桩)得到方法执行结果并将组装成能够进行网络传输的消息体:`RpcResponse`(序列化)发送至消费方;
1. 客户端 Stub(client stub)接收到消息并将消息反序列化为Java对象:`RpcResponse` ,这样也就得到了最终结果。over!
S
Snailclimb 已提交
44

G
guide 已提交
45
相信小伙伴们看完上面的讲解之后,已经了解了 RPC 的原理。
S
Snailclimb 已提交
46

G
guide 已提交
47 48 49 50 51
虽然篇幅不多,但是基本把 RPC 框架的核心原理讲清楚了!另外,对于上面的技术细节,我会在后面的章节介绍到。

**最后,对于 RPC 的原理,希望小伙伴不单单要理解,还要能够自己画出来并且能够给别人讲出来。因为,在面试中这个问题在面试官问到 RPC 相关内容的时候基本都会碰到。**

## Dubbo基础
S
Snailclimb 已提交
52

G
guide 已提交
53
### 什么是 Dubbo?
S
Snailclimb 已提交
54

G
guide 已提交
55
![](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2020-8/427f2168-1930-4c14-8760-415fac8db1d0-20200802184737978.png)
S
Snailclimb 已提交
56

iDeputy's avatar
iDeputy 已提交
57
[Apache Dubbo](https://github.com/apache/dubbo) |ˈdʌbəʊ|  是一款高性能、轻量级的开源 Java RPC 框架。
S
Snailclimb 已提交
58

G
guide 已提交
59
根据 [Dubbo 官方文档](https://dubbo.apache.org/zh/)的介绍,Dubbo 提供了六大核心能力
S
Snailclimb 已提交
60

G
guide 已提交
61 62 63 64 65 66
1. 面向接口代理的高性能RPC调用。
2. 智能容错和负载均衡。
3. 服务自动注册和发现。
4. 高度可扩展能力。
5. 运行期流量调度。
6. 可视化的服务治理与运维。
S
Snailclimb 已提交
67

G
guide 已提交
68
![Dubbo提供的六大核心能力](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/%E6%BA%90%E7%A0%81/dubbo/dubbo%E6%8F%90%E4%BE%9B%E7%9A%84%E5%85%AD%E5%A4%A7%E6%A0%B8%E5%BF%83%E8%83%BD%E5%8A%9B.png)
S
Snailclimb 已提交
69

G
guide 已提交
70
简单来说就是: **Dubbo 不光可以帮助我们调用远程服务,还提供了一些其他开箱即用的功能比如智能负载均衡。**
S
Snailclimb 已提交
71

G
guide 已提交
72 73 74 75 76 77 78
Dubbo 目前已经有接近 34.4 k 的 Star  。 

**2020 年度 OSC 中国开源项目** 评选活动中,Dubbo 位列开发框架和基础组件类项目的第7名。想比几年前来说,热度和排名有所下降。

![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/%E6%BA%90%E7%A0%81/dubbo/image-20210107153159545.png)

Dubbo 是由阿里开源,后来加入了 Apache 。正式由于 Dubbo 的出现,才使得越来越多的公司开始使用以及接受分布式架构。
S
Snailclimb 已提交
79

G
guide 已提交
80
### 为什么要用 Dubbo?
S
Snailclimb 已提交
81

G
guide 已提交
82
随着互联网的发展,网站的规模越来越大,用户数量越来越多。单一应用架构 、垂直应用架构无法满足我们的需求,这个时候分布式服务架构就诞生了。
S
Snailclimb 已提交
83

G
guide 已提交
84 85 86 87 88 89
分布式服务架构下,系统被拆分成不同的服务比如短信服务、安全服务,每个服务独立提供系统的某个核心服务。

我们可以使用 Java RMI(Java Remote Method Invocation)、Hessian这种支持远程调用的框架来简单地暴露和引用远程服务。但是!当服务越来越多之后,服务调用关系越来越复杂。当应用访问压力越来越大后,负载均衡以及服务监控的需求也迫在眉睫。我们可以用 F5 这类硬件来做负载均衡,但这样增加了成本,并且存在单点故障的风险。

不过,Dubbo 的出现让上述问题得到了解决。**Dubbo 帮助我们解决了什么问题呢?**

iDeputy's avatar
iDeputy 已提交
90
1. **负载均衡** : 同一个服务部署在不同的机器时该调用哪一台机器上的服务。
G
guide 已提交
91 92 93 94
2. **服务调用链路生成**  : 随着系统的发展,服务越来越多,服务间依赖关系变得错踪复杂,甚至分不清哪个应用要在哪个应用之前启动,架构师都不能完整的描述应用的架构关系。Dubbo 可以为我们解决服务之间互相是如何调用的。
3. **服务访问压力以及时长统计、资源调度和治理** :基于访问压力实时管理集群容量,提高集群利用率。
4. ......

谭九鼎 已提交
95
![](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-26/43050183.jpg)
S
Snailclimb 已提交
96 97 98 99 100

另外,Dubbo 除了能够应用在分布式系统中,也可以应用在现在比较火的微服务系统中。不过,由于 Spring Cloud 在微服务中应用更加广泛,所以,我觉得一般我们提 Dubbo 的话,大部分是分布式系统的情况。

**我们刚刚提到了分布式这个概念,下面再给大家介绍一下什么是分布式?为什么要分布式?**

G
guide 已提交
101 102 103
## 分布式基础

### 什么是分布式?
S
Snailclimb 已提交
104 105 106

分布式或者说 SOA 分布式重要的就是面向服务,说简单的分布式就是我们把整个系统拆分成不同的服务然后将这些服务放在不同的服务器上减轻单体服务的压力提高并发量和性能。比如电商系统可以简单地拆分成订单系统、商品系统、登录系统等等,拆分之后的每个服务可以部署在不同的机器上,如果某一个服务的访问量比较大的话也可以将这个服务同时部署在多台机器上。

G
guide 已提交
107 108 109
![分布式事务示意图](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/java-guide-blog/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E7%A4%BA%E6%84%8F%E5%9B%BE.png)

### 为什么要分布式?
S
Snailclimb 已提交
110 111 112

从开发角度来讲单体应用的代码都集中在一起,而分布式系统的代码根据业务被拆分。所以,每个团队可以负责一个服务的开发,这样提升了开发效率。另外,代码根据业务拆分之后更加便于维护和扩展。

D
dongzl 已提交
113
另外,我觉得将系统拆分成分布式之后不光便于系统扩展和维护,更能提高整个系统的性能。你想一想嘛?把整个系统拆分成不同的服务/系统,然后每个服务/系统 单独部署在一台服务器上,是不是很大程度上提高了系统性能呢?
S
Snailclimb 已提交
114

G
guide 已提交
115
## Dubbo 架构
S
Snailclimb 已提交
116

G
guide 已提交
117
### Dubbo 架构中的核心角色有哪些?
S
Snailclimb 已提交
118

G
guide 已提交
119
[官方文档中的框架设计章节](https://dubbo.apache.org/zh/docs/v2.7/dev/design/) 已经介绍的非常详细了,我这里把一些比较重要的点再提一下。
S
Snailclimb 已提交
120

G
guide 已提交
121
![dubbo-relation](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/%E6%BA%90%E7%A0%81/dubbo/dubbo-relation.jpg)
S
Snailclimb 已提交
122

G
guide 已提交
123
上述节点简单介绍以及他们之间的关系:
S
Snailclimb 已提交
124

G
guide 已提交
125 126 127 128 129
- **Container:** 服务运行容器,负责加载、运行服务提供者。必须。
- **Provider:** 暴露服务的服务提供方,会向注册中心注册自己提供的服务。必须。
- **Consumer:** 调用远程服务的服务消费方,会向注册中心订阅自己所需的服务。必须。
- **Registry:** 服务注册与发现的注册中心。注册中心会返回服务提供者地址列表给消费者。非必须。
- **Monitor:** 统计服务的调用次数和调用时间的监控中心。服务消费者和提供者会定时发送统计数据到监控中心。 非必须。
S
Snailclimb 已提交
130

G
guide 已提交
131
### Dubbo 中的 Invoker 概念了解么?
S
Snailclimb 已提交
132

G
guide 已提交
133
`Invoker` 是 Dubbo 领域模型中非常重要的一个概念,你如果阅读过 Dubbo 源码的话,你会无数次看到这玩意。就比如下面我要说的负载均衡这块的源码中就有大量 `Invoker` 的身影。
S
Snailclimb 已提交
134

G
guide 已提交
135
简单来说,`Invoker` 就是 Dubbo 对远程调用的抽象。
S
Snailclimb 已提交
136

G
guide 已提交
137
![dubbo_rpc_invoke.jpg](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/java-guide-blog/dubbo_rpc_invoke.jpg)
S
Snailclimb 已提交
138

G
guide 已提交
139
按照 Dubbo 官方的话来说,`Invoker`  分为
S
Snailclimb 已提交
140

G
guide 已提交
141 142
- 服务提供 `Invoker` 
- 服务消费 `Invoker`
S
Snailclimb 已提交
143

G
guide 已提交
144
假如我们需要调用一个远程方法,我们需要动态代理来屏蔽远程调用的细节吧!我们屏蔽掉的这些细节就依赖对应的 `Invoker`  实现, `Invoker` 实现了真正的远程服务调用。
S
Snailclimb 已提交
145

G
guide 已提交
146
### Dubbo 的工作原理了解么?
S
Snailclimb 已提交
147

G
guide 已提交
148
下图是 Dubbo 的整体设计,从下至上分为十层,各层均为单向依赖。
S
Snailclimb 已提交
149

G
guide 已提交
150
> 左边淡蓝背景的为服务消费方使用的接口,右边淡绿色背景的为服务提供方使用的接口,位于中轴线上的为双方都用到的接口。
S
Snailclimb 已提交
151

G
guide 已提交
152
![dubbo-framework](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/source-code/dubbo/dubbo-framework.jpg)
S
Snailclimb 已提交
153

G
guide 已提交
154 155 156 157 158 159 160 161 162
- **config 配置层**:Dubbo相关的配置。支持代码配置,同时也支持基于 Spring  来做配置,以 `ServiceConfig`, `ReferenceConfig` 为中心
- **proxy 服务代理层**:调用远程方法像调用本地的方法一样简单的一个关键,真实调用过程依赖代理类,以 `ServiceProxy` 为中心。
- **registry 注册中心层**:封装服务地址的注册与发现。
- **cluster 路由层**:封装多个提供者的路由及负载均衡,并桥接注册中心,以 `Invoker` 为中心。
- **monitor 监控层**:RPC 调用次数和调用时间监控,以 `Statistics` 为中心。
- **protocol 远程调用层**:封装 RPC 调用,以 `Invocation`, `Result` 为中心。
- **exchange 信息交换层**:封装请求响应模式,同步转异步,以 `Request`, `Response` 为中心。
- **transport 网络传输层**:抽象 mina 和 netty 为统一接口,以 `Message` 为中心。
- **serialize 数据序列化层** :对需要在网络传输的数据进行序列化。
S
Snailclimb 已提交
163

G
guide 已提交
164
### Dubbo 的 SPI 机制了解么? 如何扩展 Dubbo 中的默认实现?
S
Snailclimb 已提交
165

G
guide 已提交
166
SPI(Service Provider Interface) 机制被大量用在开源项目中,它可以帮助我们动态寻找服务/功能(比如负载均衡策略)的实现。
S
Snailclimb 已提交
167

G
guide 已提交
168
SPI 的具体原理是这样的:我们将接口的实现类放在配置文件中,我们在程序运行过程中读取配置文件,通过反射加载实现类。这样,我们可以在运行的时候,动态替换接口的实现类。和 IoC 的解耦思想是类似的。
S
Snailclimb 已提交
169

G
guide 已提交
170
Java 本身就提供了 SPI 机制的实现。不过,Dubbo 没有直接用,而是对 Java原生的 SPI机制进行了增强,以便更好满足自己的需求。
S
Snailclimb 已提交
171

G
guide 已提交
172
**那我们如何扩展 Dubbo 中的默认实现呢?**
S
Snailclimb 已提交
173

G
guide 已提交
174
比如说我们想要实现自己的负载均衡策略,我们创建对应的实现类 `XxxLoadBalance` 实现 `LoadBalance` 接口或者 `AbstractLoadBalance` 类。
S
Snailclimb 已提交
175

G
guide 已提交
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
```java
package com.xxx;
 
import org.apache.dubbo.rpc.cluster.LoadBalance;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.RpcException; 
 
public class XxxLoadBalance implements LoadBalance {
    public <T> Invoker<T> select(List<Invoker<T>> invokers, Invocation invocation) throws RpcException {
        // ...
    }
}
```

iDeputy's avatar
iDeputy 已提交
191
我们将这个实现类的路径写入到`resources` 目录下的 `META-INF/dubbo/org.apache.dubbo.rpc.cluster.LoadBalance`文件中即可。
G
guide 已提交
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211

```java
src
 |-main
    |-java
        |-com
            |-xxx
                |-XxxLoadBalance.java (实现LoadBalance接口)
    |-resources
        |-META-INF
            |-dubbo
                |-org.apache.dubbo.rpc.cluster.LoadBalance (纯文本文件内容为xxx=com.xxx.XxxLoadBalance)
```

`org.apache.dubbo.rpc.cluster.LoadBalance` 

```
xxx=com.xxx.XxxLoadBalance
```

G
guide 已提交
212 213 214 215
其他还有很多可供扩展的选择,你可以在[官方文档@SPI扩展实现](https://dubbo.apache.org/zh/docs/v2.7/dev/impls/)这里找到。

![](https://img-blog.csdnimg.cn/20210328091015555.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM0MzM3Mjcy,size_16,color_FFFFFF,t_70)

G
guide 已提交
216 217 218 219 220
### Dubbo 的微内核架构了解吗?

Dubbo 采用 微内核(Microkernel) + 插件(Plugin) 模式,简单来说就是微内核架构。微内核只负责组装插件。

**何为微内核架构呢?** 《软件架构模式》 这本书是这样介绍的:
S
Snailclimb 已提交
221

G
guide 已提交
222
> 微内核架构模式(有时被称为插件架构模式)是实现基于产品应用程序的一种自然模式。基于产品的应用程序是已经打包好并且拥有不同版本,可作为第三方插件下载的。然后,很多公司也在开发、发布自己内部商业应用像有版本号、说明及可加载插件式的应用软件(这也是这种模式的特征)。微内核系统可让用户添加额外的应用如插件,到核心应用,继而提供了可扩展性和功能分离的用法。
S
Snailclimb 已提交
223

G
guide 已提交
224
微内核架构包含两类组件:**核心系统(core system)****插件模块(plug-in modules)**
S
Snailclimb 已提交
225

G
guide 已提交
226
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/source-code/dubbo/%E5%BE%AE%E5%86%85%E6%A0%B8%E6%9E%B6%E6%9E%84%E7%A4%BA%E6%84%8F%E5%9B%BE.png)
S
Snailclimb 已提交
227

G
guide 已提交
228
核心系统提供系统所需核心能力,插件模块可以扩展系统的功能。因此, 基于微内核架构的系统,非常易于扩展功能。
S
Snailclimb 已提交
229

G
guide 已提交
230
我们常见的一些IDE,都可以看作是基于微内核架构设计的。绝大多数 IDE比如IDEA、VSCode都提供了插件来丰富自己的功能。
S
Snailclimb 已提交
231

G
guide 已提交
232
正是因为Dubbo基于微内核架构,才使得我们可以随心所欲替换Dubbo的功能点。比如你觉得Dubbo 的序列化模块实现的不满足自己要求,没关系啊!你自己实现一个序列化模块就好了啊!
S
shuang.kou 已提交
233

iDeputy's avatar
iDeputy 已提交
234
通常情况下,微核心都会采用 Factory、IoC、OSGi 等方式管理插件生命周期。Dubbo 不想依赖 Spring 等 IoC 容器,也不想自己造一个小的 IoC 容器(过度设计),因此采用了一种最简单的 Factory 方式管理插件 :**JDK 标准的 SPI 扩展机制**`java.util.ServiceLoader`)。
S
Snailclimb 已提交
235

G
guide 已提交
236
### 关于Dubbo架构的一些自测小问题
S
Snailclimb 已提交
237

G
guide 已提交
238
#### 注册中心的作用了解么?
S
Snailclimb 已提交
239

G
guide 已提交
240
注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互。
S
Snailclimb 已提交
241

G
guide 已提交
242
#### 服务提供者宕机后,注册中心会做什么?
S
Snailclimb 已提交
243

G
guide 已提交
244
注册中心会立即推送事件通知消费者。
S
Snailclimb 已提交
245

G
guide 已提交
246
#### 监控中心的作用呢?
S
Snailclimb 已提交
247

G
guide 已提交
248
监控中心负责统计各服务调用次数,调用时间等。
S
Snailclimb 已提交
249

G
guide 已提交
250
#### 注册中心和监控中心都宕机的话,服务都会挂掉吗?
S
Snailclimb 已提交
251

G
guide 已提交
252
不会。两者都宕机也不影响已运行的提供者和消费者,消费者在本地缓存了提供者列表。注册中心和监控中心都是可选的,服务消费者可以直连服务提供者。
S
Snailclimb 已提交
253 254


G
guide 已提交
255
## Dubbo 的负载均衡策略
S
Snailclimb 已提交
256

G
guide 已提交
257 258 259 260 261 262 263
### 什么是负载均衡?

先来看一下稍微官方点的解释。下面这段话摘自维基百科对负载均衡的定义:

> 负载均衡改善了跨多个计算资源(例如计算机,计算机集群,网络链接,中央处理单元或磁盘驱动)的工作负载分布。负载平衡旨在优化资源使用,最大化吞吐量,最小化响应时间,并避免任何单个资源的过载。使用具有负载平衡而不是单个组件的多个组件可以通过冗余提高可靠性和可用性。负载平衡通常涉及专用软件或硬件。

**上面讲的大家可能不太好理解,再用通俗的话给大家说一下。**
S
Snailclimb 已提交
264

G
guide 已提交
265 266 267 268 269 270 271
我们的系统中的某个服务的访问量特别大,我们将这个服务部署在了多台服务器上,当客户端发起请求的时候,多台服务器都可以处理这个请求。那么,如何正确选择处理该请求的服务器就很关键。假如,你就要一台服务器来处理该服务的请求,那该服务部署在多台服务器的意义就不复存在了。负载均衡就是为了避免单个服务器响应同一请求,容易造成服务器宕机、崩溃等问题,我们从负载均衡的这四个字就能明显感受到它的意义。

### Dubbo 提供的负载均衡策略有哪些?

在集群负载均衡时,Dubbo 提供了多种均衡策略,默认为 `random` 随机调用。我们还可以自行扩展负载均衡策略(参考Dubbo SPI机制)。

在 Dubbo 中,所有负载均衡实现类均继承自 `AbstractLoadBalance`,该类实现了 `LoadBalance` 接口,并封装了一些公共的逻辑。
S
Snailclimb 已提交
272 273

```java
G
guide 已提交
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
public abstract class AbstractLoadBalance implements LoadBalance {

    static int calculateWarmupWeight(int uptime, int warmup, int weight) {
    }

    @Override
    public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) {
    }

    protected abstract <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation);


    int getWeight(Invoker<?> invoker, Invocation invocation) {

    }
}
S
Snailclimb 已提交
290
```
G
guide 已提交
291 292 293

`AbstractLoadBalance` 的实现类有下面这些:

G
guide 已提交
294
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/java-guide-blog/image-20210326105257812.png)
G
guide 已提交
295 296 297 298 299 300 301 302 303

官方文档对负载均衡这部分的介绍非常详细,推荐小伙伴们看看,地址:[https://dubbo.apache.org/zh/docs/v2.7/dev/source/loadbalance/#m-zhdocsv27devsourceloadbalance](https://dubbo.apache.org/zh/docs/v2.7/dev/source/loadbalance/#m-zhdocsv27devsourceloadbalance )

####  RandomLoadBalance

根据权重随机选择(对加权随机算法的实现)。这是Dubbo默认采用的一种负载均衡策略。

` RandomLoadBalance` 具体的实现原理非常简单,假如有两个提供相同服务的服务器 S1,S2,S1的权重为7,S2的权重为3。

iDeputy's avatar
iDeputy 已提交
304
我们把这些权重值分布在坐标区间会得到:S1->[0, 7) ,S2->[7, 10)。我们生成[0, 10) 之间的随机数,随机数落到对应的区间,我们就选择对应的服务器来处理请求。
G
guide 已提交
305 306 307 308 309 310

![RandomLoadBalance](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/java-guide-blog/%20RandomLoadBalance.png)

`RandomLoadBalance` 的源码非常简单,简单花几分钟时间看一下。

> 以下源码来自 Dubbo master 分支上的最新的版本 2.7.9。
S
Snailclimb 已提交
311 312

```java
G
guide 已提交
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
public class RandomLoadBalance extends AbstractLoadBalance {

    public static final String NAME = "random";

    @Override
    protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {

        int length = invokers.size();
        boolean sameWeight = true;
        int[] weights = new int[length]; 
        int totalWeight = 0;
        // 下面这个for循环的主要作用就是计算所有该服务的提供者的权重之和 totalWeight(),
        // 除此之外,还会检测每个服务提供者的权重是否相同
        for (int i = 0; i < length; i++) {
            int weight = getWeight(invokers.get(i), invocation);
            totalWeight += weight;
            weights[i] = totalWeight;
            if (sameWeight && totalWeight != weight * (i + 1)) {
                sameWeight = false;
            }
        }
        if (totalWeight > 0 && !sameWeight) {
            // 随机生成一个 [0, totalWeight) 区间内的数字
            int offset = ThreadLocalRandom.current().nextInt(totalWeight);
            // 判断会落在哪个服务提供者的区间
            for (int i = 0; i < length; i++) {
                if (offset < weights[i]) {
                    return invokers.get(i);
                }
            }
  
        return invokers.get(ThreadLocalRandom.current().nextInt(length));
    }

}

S
Snailclimb 已提交
349 350
```

G
guide 已提交
351 352 353 354 355 356 357 358 359 360 361 362 363
####  LeastActiveLoadBalance

`LeastActiveLoadBalance` 直译过来就是**最小活跃数负载均衡**

这个名字起得有点不直观,不仔细看官方对活跃数的定义,你压根不知道这玩意是干嘛的。

我这么说吧!初始状态下所有服务提供者的活跃数均为 0(每个服务提供者的中特定方法都对应一个活跃数,我在后面的源码中会提到),每收到一个请求后,对应的服务提供者的活跃数 +1,当这个请求处理完之后,活跃数 -1。

因此,**Dubbo 就认为谁的活跃数越少,谁的处理速度就越快,性能也越好,这样的话,我就优先把请求给活跃数少的服务提供者处理。**

**如果有多个服务提供者的活跃数相等怎么办?**

很简单,那就再走一遍  `RandomLoadBalance`
S
Snailclimb 已提交
364 365

```java
G
guide 已提交
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
public class LeastActiveLoadBalance extends AbstractLoadBalance {

    public static final String NAME = "leastactive";

    @Override
    protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
        int length = invokers.size();
        int leastActive = -1;
        int leastCount = 0;
        int[] leastIndexes = new int[length];
        int[] weights = new int[length];
        int totalWeight = 0;
        int firstWeight = 0;
        boolean sameWeight = true;
        // 这个 for 循环的主要作用是遍历 invokers 列表,找出活跃数最小的 Invoker
        // 如果有多个 Invoker 具有相同的最小活跃数,还会记录下这些 Invoker 在 invokers 集合中的下标,并累加它们的权重,比较它们的权重值是否相等
        for (int i = 0; i < length; i++) {
            Invoker<T> invoker = invokers.get(i);
            // 获取 invoker 对应的活跃(active)数
            int active = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()).getActive();
            int afterWarmup = getWeight(invoker, invocation);
            weights[i] = afterWarmup;
            if (leastActive == -1 || active < leastActive) {
                leastActive = active;
                leastCount = 1;
                leastIndexes[0] = i;
                totalWeight = afterWarmup;
                firstWeight = afterWarmup;
                sameWeight = true;
            } else if (active == leastActive) {
                leastIndexes[leastCount++] = i;
                totalWeight += afterWarmup;
                if (sameWeight && afterWarmup != firstWeight) {
                    sameWeight = false;
                }
            }
        }
       // 如果只有一个 Invoker 具有最小的活跃数,此时直接返回该 Invoker 即可
        if (leastCount == 1) {
            return invokers.get(leastIndexes[0]);
        }
        // 如果有多个 Invoker 具有相同的最小活跃数,但它们之间的权重不同
        // 这里的处理方式就和  RandomLoadBalance 一致了
        if (!sameWeight && totalWeight > 0) {
            int offsetWeight = ThreadLocalRandom.current().nextInt(totalWeight);
            for (int i = 0; i < leastCount; i++) {
                int leastIndex = leastIndexes[i];
                offsetWeight -= weights[leastIndex];
                if (offsetWeight < 0) {
                    return invokers.get(leastIndex);
                }
            }
        }
        return invokers.get(leastIndexes[ThreadLocalRandom.current().nextInt(leastCount)]);
    }
}

S
Snailclimb 已提交
423 424
```

G
guide 已提交
425
活跃数是通过 `RpcStatus` 中的一个 `ConcurrentMap` 保存的,根据 URL 以及服务提供者被调用的方法的名称,我们便可以获取到对应的活跃数。也就是说服务提供者中的每一个方法的活跃数都是互相独立的。
S
Snailclimb 已提交
426 427

```java
G
guide 已提交
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442
public class RpcStatus {
    
    private static final ConcurrentMap<String, ConcurrentMap<String, RpcStatus>> METHOD_STATISTICS =
            new ConcurrentHashMap<String, ConcurrentMap<String, RpcStatus>>();

   public static RpcStatus getStatus(URL url, String methodName) {
        String uri = url.toIdentityString();
        ConcurrentMap<String, RpcStatus> map = METHOD_STATISTICS.computeIfAbsent(uri, k -> new ConcurrentHashMap<>());
        return map.computeIfAbsent(methodName, k -> new RpcStatus());
    }
    public int getActive() {
        return active.get();
    }

}
S
Snailclimb 已提交
443 444
```

G
guide 已提交
445
####  ConsistentHashLoadBalance
S
Snailclimb 已提交
446

G
guide 已提交
447
`ConsistentHashLoadBalance`  小伙伴们应该也不会陌生,在分库分表、各种集群中就经常使用这个负载均衡策略。
S
Snailclimb 已提交
448

G
guide 已提交
449
`ConsistentHashLoadBalance`**一致性Hash负载均衡策略**`ConsistentHashLoadBalance` 中没有权重的概念,具体是哪个服务提供者处理请求是由你的请求的参数决定的,也就是说相同参数的请求总是发到同一个服务提供者。
S
Snailclimb 已提交
450

G
guide 已提交
451
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/java-guide-blog/consistent-hash-data-incline.jpg)
S
Snailclimb 已提交
452

G
guide 已提交
453
另外,Dubbo 为了避免数据倾斜问题(节点不够分散,大量请求落到同一节点),还引入了虚拟节点的概念。通过虚拟节点可以让节点更加分散,有效均衡各个节点的请求量。
S
Snailclimb 已提交
454 455 456



G
guide 已提交
457
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/java-guide-blog/consistent-hash-invoker.jpg)
S
Snailclimb 已提交
458

G
guide 已提交
459
官方有详细的源码分析:[https://dubbo.apache.org/zh/docs/v2.7/dev/source/loadbalance/#23-consistenthashloadbalance](https://dubbo.apache.org/zh/docs/v2.7/dev/source/loadbalance/#23-consistenthashloadbalance) 。这里还有一个相关的 [PR#5440](https://github.com/apache/dubbo/pull/5440) 来修复老版本中 ConsistentHashLoadBalance 存在的一些Bug。感兴趣的小伙伴,可以多花点时间研究一下。我这里不多分析了,这个作业留给你们!
S
Snailclimb 已提交
460

G
guide 已提交
461
####  RoundRobinLoadBalance
S
Snailclimb 已提交
462

G
guide 已提交
463
加权轮询负载均衡。
S
Snailclimb 已提交
464

G
guide 已提交
465
轮询就是把请求依次分配给每个服务提供者。加权轮询就是在轮询的基础上,让更多的请求落到权重更大的服务提供者上。比如假如有两个提供相同服务的服务器 S1,S2,S1的权重为7,S2的权重为3。
S
Snailclimb 已提交
466

G
guide 已提交
467
如果我们有 10 次请求,那么  7 次会被 S1处理,3次被 S2处理。
S
Snailclimb 已提交
468

G
guide 已提交
469
但是,如果是 `RandomLoadBalance` 的话,很可能存在10次请求有9次都被 S1 处理的情况(概率性问题)。
S
Snailclimb 已提交
470

G
guide 已提交
471
Dubbo 中的 `RoundRobinLoadBalance` 的代码实现被修改重建了好几次,Dubbo-2.6.5 版本的 `RoundRobinLoadBalance` 为平滑加权轮询算法。
S
Snailclimb 已提交
472

G
guide 已提交
473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
## Dubbo序列化协议

### Dubbo 支持哪些序列化方式呢?

![](https://img-blog.csdnimg.cn/20210328092219640.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM0MzM3Mjcy,size_16,color_FFFFFF,t_70)

Dubbo 支持多种序列化方式:JDK自带的序列化、hessian2、JSON、Kryo、FST、Protostuff,ProtoBuf等等。

Dubbo 默认使用的序列化方式是 hession2。

### 谈谈你对这些序列化协议了解?

一般我们不会直接使用 JDK 自带的序列化方式。主要原因有两个:

1. **不支持跨语言调用** : 如果调用的是其他语言开发的服务的时候就不支持了。
2. **性能差** :相比于其他序列化框架性能更低,主要原因是序列化之后的字节数组体积较大,导致传输成本加大。

JSON 序列化由于性能问题,我们一般也不会考虑使用。

像 Protostuff,ProtoBuf、hessian2这些都是跨语言的序列化方式,如果有跨语言需求的话可以考虑使用。

Kryo和FST这两种序列化方式是 Dubbo 后来才引入的,性能非常好。不过,这两者都是专门针对 Java 语言的。Dubbo 官网的一篇文章中提到说推荐使用 Kryo 作为生产环境的序列化方式。(文章地址:[https://dubbo.apache.org/zh/docs/v2.7/user/references/protocol/rest/](https://dubbo.apache.org/zh/docs/v2.7/user/references/protocol/rest/))

![](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2020-8/569e541a-22b2-4846-aa07-0ad479f07440.png)

Dubbo 官方文档中还有一个关于这些[序列化协议的性能对比图](https://dubbo.apache.org/zh/docs/v2.7/user/serialization/#m-zhdocsv27userserialization)可供参考。

![](https://img-blog.csdnimg.cn/20210328093219609.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM0MzM3Mjcy,size_16,color_FFFFFF,t_70)