dubbo 面试题

⼯作原理

第⼀层: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 都异步通知监控中⼼

注册中心挂了可以继续通信吗?

初始化时,需要消费者将提供者的地址等信息拉取到本地缓存所以挂了可以继续通讯

支持那些通讯协议

dubbo 协议
默认就是⾛ dubbo 协议,单⼀⻓连接,进⾏的是 NIO 异步通信,基于 hessian 作为序列化协
议。使⽤的场景是:传输数据量⼩(每次请求在 100kb 以内),但是并发量很⾼。
为了要⽀持⾼并发场景,⼀般是服务提供者就⼏台机器,但是服务消费者有上百台,可能每天
调⽤量达到上亿次!此时⽤⻓连接是最合适的,就是跟每个服务消费者维持⼀个⻓连接就可
以,可能总共就 100 个连接。然后后⾯直接基于⻓连接 NIO 异步通信,可以⽀撑⾼并发请求。
⻓连接,通俗点说,就是建⽴连接过后可以持续发送请求,⽆须再建⽴连接。
rmi 协议
⾛ Java ⼆进制序列化,多个短连接,适合消费者和提供者数量差不多的情况,适⽤于⽂件的传
输,⼀般较少⽤。
hessian 协议
⾛ hessian 序列化协议,多个短连接,适⽤于提供者数量⽐消费者数量还多的情况,适⽤于⽂
件的传输,⼀般较少⽤。
http 协议
⾛ json 序列化。
webservice
⾛ SOAP ⽂本序列化。

序列化

就是把数据结构或者是⼀些对象,转换为⼆进制串的过程,⽽反序列化是将在序列化
过程中所⽣成的⼆进制串转换成数据结构或者对象的过程。

为什么 PB 的效率是最⾼的?

其实PB之所以性能如此好,主要得益于两个原因:
1、它使用 Proto 编译器,自动进行序列化和反序列化,速度非常快,应该比 XML 和JSON 快上了 20-100倍。
2、它的数据压缩效果好,就是说它序列化后的数据量体积小。因为体积小,传输起来带宽和速度上会有优化。

Hessian的数据结构

Hessian 的对象序列化机制有 8种 基本类型:

原始二进制数据
boolean
64-bit date (64位毫秒值的日期)
64-bit double
32-bit int
64-bit long
null
UTF-8 编码的 string

另外还包括 2 个组合结构:
list for lists and arrays
map for maps and dictionaries
object for objects
还有一种特殊的类型:

ref:用来表示对共享对象的引用

dubbo负载均衡策略

Dubbo提供了4种均衡策略,如:
Random LoadBalance(随机均衡算法)、;
RoundRobin LoadBalance(权重轮循均衡算法)、
LeastAction LoadBalance(最少活跃调用数均衡算法)、ConsistentHash LoadBalance(一致性Hash均衡算法)。

random loadbalance

默认情况下,dubbo 是 random load balance ,即随机调⽤实现负载均衡,可以对 provider 不同
实例设置不同的权重,会按照权重来负载均衡,权重越⼤分配流量越⾼,⼀般就⽤这个默认的
就可以了。

roundrobin loadbalance

这个的话默认就是均匀地将流量打到各个机器上去,但是如果各个机器的性能不⼀样,容易导
致性能差的机器负载过⾼。所以此时需要调整权重,让性能差的机器承载权重⼩⼀些,流量少
⼀些。
举例
给两台 8 核 16G 的机器设置权重 4,
给1 台 4 核 8G 的机器设置权重 2。

leastactive loadbalance

这个就是⾃动感知⼀下,如果某个机器性能越差,那么接收的请求越少,越不活跃,此时就会
给不活跃的性能差的机器更少的请求。

consistanthash loadbalance

⼀致性 Hash 算法,相同参数的请求⼀定分发到⼀个 provider 上去,provider 挂掉的时候,会基
于虚拟节点均匀分配剩余的流量,抖动不会太⼤。如果你需要的不是随机负载均衡,是要⼀类
请求都到⼀个节点,那就⾛这个⼀致性 Hash 策略。

dubbo集群容错策略

failover cluster
dubbo默认。失败自动切换,自动重试其他机器。常用于读操作。
几种配置方式

<dubbo:service retries="2" />
<dubbo:reference retries="2" />
<dubbo:reference>
    <dubbo:method name="findByName" retries="2" />
</dubbo:reference>

failfast cluster
一次调用失败就立即失败,常见于非幂等性操作,比如新增操作。
failsafe cluster
出现异常时忽略,常用于日志记录等不是很重要的接口调用。
failback cluster
失败了在后台自动记录请求,然后定时重发。比较适合写消息队列等操作。
forking cluster
并行调用多个服务提供者,只要有一个成功立即返回。适用于实时性要求比较高的读操作,但是对资源比较浪费。
broadcast cluster
逐个调用所有的provider。任何一个provider出错则报错。常用于通知所有提供者更新缓存或日志等本地资源信息。

动态代理策略

默认使用 javassist 动态字节码生成,创建代理类。但是可以通过 spi 扩展机制配置自己的动态代理策略。

dubbo 的 spi 思想是什么?

spi 是啥?

spi,简单来说,就是 service provider interface ,说⽩了是什么意思呢,⽐如你有个接
⼝,现在这个接⼝有 3 个实现类,那么在系统运⾏的时候对这个接⼝到底选择哪个实现类呢?
这就需要 spi 了,需要根据指定的配置或者是默认的配置,去找到对应的实现类加载进来,然
后⽤这个实现类的实例对象。
举个栗⼦。
你有⼀个接⼝ A。A1/A2/A3 分别是接⼝A的不同实现。你通过配置 接⼝ A = 实现 A2 ,那么在
系统实际运⾏的时候,会加载你的配置,⽤实现 A2 实例化⼀个对象来提供服务。
spi 机制⼀般⽤在哪⼉?插件扩展的场景,⽐如说你开发了⼀个给别⼈使⽤的开源框架,如果你
想让别⼈⾃⼰写个插件,插到你的开源框架⾥⾯,从⽽扩展某个功能,这个时候 spi 思想就⽤
上了

Java spi 思想的体现

spi 经典的思想体现,大家平时都在用,比如说 jdbc。

Java 定义了一套 jdbc 的接口,但是 Java 并没有提供 jdbc 的实现类。

但是实际上项目跑的时候,要使用 jdbc 接口的哪些实现类呢?一般来说,我们要根据自己使用的数据库,比如 mysql,你就将 mysql-jdbc-connector.jar 引入进来;oracle,你就将 oracle-jdbc-connector.jar 引入进来。

dubbo 的 spi 思想

在系统跑的时候,碰到你使用 jdbc 的接口,他会在底层使用你引入的那个 jar 中提供的实现类。
Protocol 接口,在系统运行的时候,,dubbo 会判断一下应该选用这个 Protocol 接口的哪个实现类来实例化对象来使用。

它会去找一个你配置的 Protocol,将你配置的 Protocol 实现类,加载到 jvm 中来,然后实例化对象,就用你的那个 Protocol 实现类就可以了。

@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;  

    void destroy();  
  
}  

上面那行代码就是 dubbo 里大量使用的,就是对很多组件,都是保留一个接口和多个实现,然后在系统运行的时候动态根据配置去找到对应的实现类。如果你没配置,那就走默认的实现好了,没问题。
在 dubbo 自己的 jar 里,在/META_INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol文件中:

dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
http=com.alibaba.dubbo.rpc.protocol.http.HttpProtocol
hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol

所以说,这就看到了 dubbo 的 spi 机制默认是怎么玩儿的了,其实就是 Protocol 接口,@SPI(“dubbo”) 说的是,通过 SPI 机制来提供实现类,实现类是通过 dubbo 作为默认 key 去配置文件里找到的,配置文件名称与接口全限定名一样的,通过 dubbo 作为 key 可以找到默认的实现类就是 com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol。

如果想要动态替换掉默认的实现类,需要使用 @Adaptive 接口,Protocol 接口中,有两个方法加了 @Adaptive 注解,就是说那俩接口会被代理实现。

啥意思呢?

比如这个 Protocol 接口搞了俩 @Adaptive 注解标注了方法,在运行的时候会针对 Protocol 生成代理类,这个代理类的那俩方法里面会有代理代码,代理代码会在运行的时候动态根据 url 中的 protocol 来获取那个 key,默认是 dubbo,你也可以自己指定,你如果指定了别的 key,那么就会获取别的实现类的实例了。

如何自己扩展 dubbo 中的组件

下面来说说怎么来自己扩展 dubbo 中的组件。

自己写个工程,要是那种可以打成 jar 包的,里面的 src/main/resources 目录下,搞一个 META-INF/services,里面放个文件叫:com.alibaba.dubbo.rpc.Protocol,文件里搞一个my=com.bingo.MyProtocol。自己把 jar 弄到 nexus 私服里去。

然后自己搞一个 dubbo provider 工程,在这个工程里面依赖你自己搞的那个 jar,然后在 spring 配置文件里给个配置:

<dubbo:protocol name=”my” port=20000/>

provider 启动的时候,就会加载到我们 jar 包里的my=com.bingo.MyProtocol 这行配置里,接着会根据你的配置使用你定义好的 MyProtocol 了,这个就是简单说明一下,你通过上述方式,可以替换掉大量的 dubbo 内部的组件,就是扔个你自己的 jar 包,然后配置一下即可。
在这里插入图片描述
dubbo 里面提供了大量的类似上面的扩展点,就是说,你如果要扩展一个东西,只要自己写个 jar,让你的 consumer 或者是 provider 工程,依赖你的那个 jar,在你的 jar 里指定目录下配置好接口名称对应的文件,里面通过 key=实现类。

然后对于对应的组件,类似 用你的那个 key 对应的实现类来实现某个接口,你可以自己去扩展 dubbo 的各种功能,提供你自己的实现。

如何基于 dubbo 进行服务治理、服务降级、失败重试以及超时重试?

https://blog.csdn.net/aike8435/article/details/102350569?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-0&spm=1001.2101.3001.4242

分布式服务接口的幂等性如何设计

所谓幂等性,就是说⼀个接⼝,多次发起同⼀个请求,你这个接⼝得保证结果是准确的,⽐如不能多扣款、不能多插⼊⼀条数据、不能将统计值多加了 1。这就是幂等性。
其实保证幂等性主要是三点:
对于每个请求必须有⼀个唯⼀的标识,举个栗⼦:订单⽀付请求,肯定得包含订单 id,⼀
个订单 id 最多⽀付⼀次,对吧。
每次处理完请求之后,必须有⼀个记录标识这个请求处理过了。常⻅的⽅案是在 mysql 中
记录个状态啥的,⽐如⽀付之前记录⼀条这个订单的⽀付流⽔。
每次接收请求需要进⾏判断,判断之前是否处理过。⽐如说,如果有⼀个订单已经⽀付
了,就已经有了⼀条⽀付流⽔,那么如果重复发送这个请求,则此时先插⼊⽀付流⽔,
orderId 已经存在了,唯⼀键约束⽣效,报错插⼊不进去的。然后你就不⽤再扣款了。
实际运作过程中,你要结合⾃⼰的业务来,⽐如说利⽤ redis,⽤ orderId 作为唯⼀键。只有成
功插⼊这个⽀付流⽔,才可以执⾏实际的⽀付扣款。
要求是⽀付⼀个订单,必须插⼊⼀条⽀付流⽔,order_id 建⼀个唯⼀键 unique key 。你在⽀
付⼀个订单之前,先插⼊⼀条⽀付流⽔,order_id 就已经进去了。你就可以写⼀个标识到 redis
⾥⾯去, set order_id payed ,下⼀次重复请求过来了,先查 redis 的 order_id 对应的
value,如果是 payed 就说明已经⽀付过了,你就别重复⽀付了。

分布式服务接口请求的顺序性如何保证?

https://blog.csdn.net/weixin_39388918/article/details/99623245

如何自己设计一个类似dubbo的rpc框架?

https://www.jianshu.com/p/91785dfbb98b?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值