问题1: spi的原理是什么 dubbo哪些地方用到了
RPC 通信是大型服务框架的核心
微服务的核心是远程通信和服务治理。远程通信提供了服务之间通信的桥梁,服务治理则提供了服务的后勤保障。所以,我们在做技术选型时,更多要考虑的是这两个核心的需求。目前,很多微服务框架中的服务通信是基于 RPC 通信实现的,在没有进行组件扩展的前提下,SpringCloud 是基于 Feign 组件实现的 RPC 通信(基于 Http+Json 序列化实现),Dubbo 是基于 SPI 扩展了很多 RPC 通信框架,包括 RMI、Dubbo、Hessian 等 RPC 通信框架(默认是 Dubbo+Hessian 序列化)。不同的业务场景下,RPC 通信的选择和优化标准也不同。例如,开头我提到的我们部门在选择微服务框架时,选择了 Dubbo。当时的选择标准就是RPC 通信可以支持抢购类的高并发,在这个业务场景中,请求的特点是瞬时高峰、请求量大和传入、传出参数数据包较小。而 Dubbo 中的 Dubbo 协议就很好地支持了这个请求。以下是基于 Dubbo:2.6.4 版本进行的简单的性能测试。分别测试 Dubbo+Protobuf 序列化以及 Http+Json 序列化的通信性能(这里主要模拟单一 TCP 长连接 +Protobuf 序列化和短连接的 Http+Json 序列化的性能对比)。为了验证在数据量不同的情况下二者的性能表现,我分别准备了小对象和大对象的性能压测,通过这样的方式我们也可以间接地了解下二者在 RPC 通信方面的水平。RT(RESPONSE TIME) 代表响应时长
通过以上测试结果可以发现:无论从响应时间还是吞吐量上来看,单一 TCP 长连接
+Protobuf 序列化实现的 RPC 通信框架都有着非常明显的优势。
Dubbo 与 Feign 都依赖注册中心、负载均衡。
二、区别
1、协议
Dubbo:支持多传输协议(Dubbo、Rmi、http、redis等等),可以根据业务场景选择最佳的方式。非常灵活。
默认的Dubbo协议:利用Netty,TCP传输,单一、异步、长连接,适合数据量小、高并发和服务提供者远远少于消费者的场景。
Feign:基于Http传输协议,短连接,不适合高并发的访问。
2、负载均衡
Dubbo:
支持4种算法(随机、轮询、活跃度、Hash一致性),而且算法里面引入权重的概念。
配置的形式不仅支持代码配置,还支持Dubbo控制台灵活动态配置。
负载均衡的算法可以精准到某个服务接口的某个方法。
Feign:
只支持N种策略:轮询、随机、ResponseTime加权。
负载均衡算法是Client级别的。
3、容错策略
Dubbo:支持多种容错策略:failover、failfast、brodecast、forking等,也引入了retry次数、timeout等配置参数。
Feign:利用熔断机制来实现容错的,处理的方式不一样。
拓展: RMI-JDK 自带的 RPC 通信框架
RMI(Remote Method Invocation)是 JDK 中最先实现了 RPC 通信的框架之一,RMI
的实现对建立分布式 Java 应用程序至关重要,是 Java 体系非常重要的底层技术,很多开
源的 RPC 通信框架也是基于 RMI 实现原理设计出来的,包括 Dubbo 框架中也接入了
RMI 框架。接下来我们就一起了解下 RMI 的实现原理,看看它存在哪些性能瓶颈有待优
化。
RMI 在高并发场景下的性能瓶颈
1> Java 默认序列化
RMI 的序列化采用的是 Java 默认的序列化方式,我在 09 讲中详细地介绍过 Java 序列
化,我们深知它的性能并不是很好,而且其它语言框架也暂时不支持 Java 序列化。
2> TCP 短连接
由于 RMI 是基于 TCP 短连接实现,在高并发情况下,大量请求会带来大量连接的创建和销
毁,这对于系统来说无疑是非常消耗性能的。
3> 阻塞式网络 I/O
在 08 讲中,我提到了网络通信存在 I/O 瓶颈,如果在 Socket 编程中使用传统的 I/O 模
型,在高并发场景下基于短连接实现的网络通信就很容易产生 I/O 阻塞,性能将会大打折
扣。
Dubbo底层是使用 Netty NIO 框架,基于 TCP 协议传输配合以 Hessian 序列化的 RPC 分布式服务框架
Dubbo 和 Spring Cloud 有什么区别?
1)通信方式不同
Dubbo 使用的是 RPC 通信,而 Spring Cloud 使用的是 HTTP RESTFul 方式。
Dubbo,RPC的性能比HTTP的性能更好,并发能力更强,经过深度优化的RPC服务框架,性能和并发能力是更好一些。很多中小型公司而言,其实稍微好一点的性能,Dubbo一次请求10ms,Spring Cloud耗费20ms,对很多中小型公司而言,性能、并发,并不是最主要的因素,Spring Cloud这套架构原理,走HTTP接口和HTTP请求,就足够满足性能和并发的需要了,没必要使用高度优化的RPC服务框架。
2)组成部分不同
4、dubbo都支持什么通讯协议,推荐用哪种?
HTTP连接
HTTP协议即超文本传送协议(Hypertext Transfer Protocol ),是Web联网的基础,也是手机联网常用的协议之一,HTTP协议是建立在TCP协议之上的一种应用。
HTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”。
1)在HTTP 1.0中,客户端的每次请求都要求建立一次单独的连接,在处理完本次请求后,就自动释放连接。
2)在HTTP 1.1中则可以在一次连接中处理多个请求,并且多个请求可以重叠进行,不需要等待一个请求结束后再发送下一个请求。
由于HTTP在每次请求结束后都会主动释放连接,因此HTTP连接是一种“短连接”,要保持客户端程序的在线状态,需要不断地向服务器发起连接请求
协议名称 | 实现描述 | 连接 | 使用场景 |
dubbo | 传输:mina、netty、grizzy 序列化:dubbo、hessian2 二进制序列化(默认)、java、json | dubbo缺省采用单一长连接和NIO异步通讯
| 1.传入传出参数数据包较小 |
rmi (少用) | 传输:java rmi 序列化:java 标准二进制序列化 | 连接个数:多连接 连接方式:短连接 传输协议:TCP 传输方式:BIO | 1.常规RPC调用 2.与原RMI客户端互操作 3.可传文件 4.不支持防火墙穿透 |
hessian | 传输:Serverlet容器 序列化:hessian二进制序列化 | 连接个数:多连接 连接方式:短连接 传输协议:HTTP 传输方式:同步传输 | 1.提供者比消费者多 约束:
Hessian 是 Caucho 开源的一个 RPC 框架,其通讯效率高于 WebService 和 Java 自带的序列化 |
http 基于 HTTP 表单的远程调用协议,采用 Spring 的 HttpInvoker 实现 | 传输:servlet容器 序列化:表单序列化 | 连接个数:多连接 连接方式:短连接 传输协议:HTTP 传输方式:同步传输 | 1.提供者多余消费者 分析请求的发送与接收、编解码、线程派发以及响应的发送与接收等过程,至于服务降级、过滤器链和序列化3.可用表单或URL传入参数,暂不支持传文件。 |
webservice | 传输:HTTP 序列化:SOAP文件序列化 | 连接个数:多连接 连接方式:短连接 传输协议:HTTP 传输方式:同步传输 | 1.系统集成 2.跨语言调用 |
thrift | 与thrift RPC实现集成,并在基础上修改了报文头 | 长连接、NIO异步传输 |
1> 默认就是走dubbo协议的,单一长连接,NIO异步通信,基于hessian作为序列化协议
适用的场景就是:传输数据量很小(每次请求在100kb以内),但是并发量很高
为什么采用异步单一长连接?
因为服务的现状大都是服务提供者少,通常只有几台机器,而服务的消费者多,可能整个网站都在访问该服务,通过单一连接,保证单一消费者不会压死提供者,长连接,减少连接握手验证等 然后基于长连接NIO异步通信,可以支撑高并发请求 否则如果上亿次请求每次都是短连接的话,服务提供者会扛不住。
为什么不能传大包?
因 dubbo 协议采用单一长连接,网络将成为瓶颈
5. Dubbo里面有哪几种节点角色以及服务注册与发现的流程图?
0. 服务容器负责启动,加载,运行服务提供者。
· 1. 服务提供者在启动时,向注册中心注册自己提供的服务。
· 2. 服务消费者在启动时,向注册中心订阅自己所需的服务。
· 3. 注册中心返回服务提供者地址列表(注册的生产者的地址接口)给消费者,消费者缓存到本地,调用服务时不会每次都去zk中查询而是在本地cache文件中查询. 如果有变更(watch 机制),注册中心将基于长连接推
送变更数据给消费者,告知哪台服务器挂了。
· 4. 服务消费者从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,
如果调用失败,再选另一台调用(dubbo 在调用服务不成功时,默认是会重试两次)。
· 5. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计
数据到监控中心。
注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,服务消费者向注册中心获取服务提供者地址列表,并根据负载算法直接调用提供者,注册中心,服务提供者,服务消费者三者之间均为长连接,监控中心除外,注册中心通过长连接感知服务提供者的存在,服务提供者宕机,注册中心将立即推送事件通知消费者
注册中心和监控中心全部宕机,不影响已运行的提供者和消费者,消费者在本地缓存了提供者列表
注册中心和监控中心都是可选的,服务消费者可以直连服务提供者
Dubbo 服务调用过程
服务消费者通过代理工厂ProxyFactory生成的代理对象Proxy (服务接口),经过过滤器去集群拿服务列表,在经过复杂均衡策略选取服务, 经过通讯协议dubbo hessian发起远程调用, 接着通过网络客户端将编码后的请求发送到服务提供方server,server收到请求后, 现将数据进行解码,然后将解码后的请求发送到分发器Dispatcher, 在通过分发器将请求发送发到指定的线程池上,最后有线程池调用具体的服务
6.Dubbo默认使用什么注册中心,还有别的选择吗?
推荐使用 Zookeeper 作为注册中心
- Multicast 注册中心:Multicast 注册中心不需要任何中心节点,只要广播地址,就能进行服务注册和发现,基于网络中组播传输实现。
- Zookeeper 注册中心:基于分布式协调系统 Zookeeper 实现,采用 Zookeeper 的 watch 机制实现数据变更。
- Redis 注册中心:基于 Redis 实现,采用 key/map 存储, key 存储服务名和类型,map 中 key 存储服务 url,value 服务过期时间。基于 Redis 的发布/订阅模式通知数据变更。
- Simple 注册中心。
7.Dubbo 核心的配置有哪些?
我曾经面试就遇到过面试官让你写这些配置,我也是蒙逼。。
8. 在 Provider 上可以配置的 Consumer 端的属性有哪些?
1)timeout:方法调用超时
2)retries:失败重试次数,默认重试 2 次
3)loadbalance:负载均衡算法,默认随机
4)actives 消费者端,最大并发调用限制
consumer的优先级高于provider
服务端:
客户端
8.1 dubbo 数据传输大小配置
<dubbo:protocol name="${dubbo.protocol.name}" port="${dubbo.protocol.port}" payload ="${dubbo.protocol.dubbo.payload}" />
下面大概说一下dubbo:protocol里面的属性
threadpool:线程池类型,可选:fixed/cached ,默认fixed 。
threads :服务线程池大小(固定大小) ,默认为100
payload:请求及响应数据包大小限制,单位:字节,默认为88388608(=8M) 1M=1048576不是最大值
如:
<dubbo:protocol name="dubbo" port="4021" server="netty" client="netty" serialization="hessian2" charset="UTF-8" threadpool="cached" threads="20000" queues="0" iothreads="25" buffer="8192" accepts="20000" payload="8388608" />
ThreadPool
fixed 固定大小线程池,启动时建立线程,不关闭,一直持有。(缺省)
cached 缓存线程池,空闲一分钟自动删除,需要时重建。
limited可伸缩线程池,但池中的线程数只会增长不会收缩。(为避免收缩时突然来了大流量引起的性能问题)。
buffer 网络读写缓冲区大小
iothreads io线程池大小(固定大小)
threads 服务线程池大小(固定大小)
accepts 服务提供方最大可接受连接数
9. Dubbo推荐使用什么序列化框架,你知道的还有哪些?
推荐使用Hessian序列化,还有Duddo、FastJson、Java自带序列化。
10. Dubbo有哪几种负载均衡策略,默认是哪种?
Random LoadBalance
随机,按权重设置随机概率。可以对provider不同实例设置不同的权重,会按照权重来负载均衡,权重越大分配流量越高,一般就用这个默认的就可以了。(权重可以在dubbo管控台配置)
RoundRobin LoadBalance
轮循,按公约后的权重设置轮循比率。存在慢的提供者累积请求问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。
LeastActive LoadBalance
最少活跃调用数,这个就是自动感知一下,如果某个机器性能越差,那么接收的请求越少,越不活跃,此时就会给不活跃的性能差的机器更少的请求 ( 相同活跃数的随机,活跃数指调用前后计数差。使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。 )
ConsistentHash LoadBalance
一致性Hash,相同参数的请求总是发到同一provider提供者。当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。缺省只对第一个参数Hash,如果要修改,请配置
11. dubbo集群容错策略
1)failover cluster模式
失败自动切换,自动重试其他机器,默认就是这个,常见于读操作
2) failfast cluster模式
一次调用失败就立即失败,常见于写操作
3)failsafe cluster模式
出现异常时忽略掉,常用于不重要的接口调用,比如记录日志
4)failback cluster模式
失败了后台自动记录请求,然后定时重发,比较适合于写消息队列这种
5)forking cluster
并行调用多个provider,只要一个成功就立即返回
6)broadcacst cluster
逐个调用所有的provider
12 dubbo动态代理策略
默认使用javassist动态字节码生成,创建代理类
但是可以通过spi扩展机制配置自己的动态代理策略
13. Dubbo中zookeeper做注册中心,如果注册中心集群都挂掉,发布者和订阅者之间还能通信么?
可以的,启动dubbo时,消费者会从zk拉取注册的生产者的地址接口等数据,缓存在本地。每次调用时,按照本地存储的地址进行调用 注册中心全部宕掉,服务提供者和消费者仍可以通过本地缓存通讯
使用dubbo遇到过哪些问题?
增加提供服务版本号和消费服务版本号
这个具体来说不算是一个问题,而是一种问题的解决方案,通过版本号找到对应的服务.
<dubbo:serviceinterface="com.xxx.XxxService" ref="xxxService" version="1.0"/>
<dubbo:referenceid="xxxService" interface="com.xxx.XxxService" version="1.0"/>