博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Dubbo
阅读量:2154 次
发布时间:2019-05-01

本文共 5669 字,大约阅读时间需要 18 分钟。

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 协议

默认就是用 dubbo 协议,单一长连接,进行的是 NIO 异步通信,基于 hessian 作为序列化协议。使用的场景是:传输数据量小(每次请求在 100kb 以内),但是并发量很高。

为了要支持高并发场景,一般是服务提供者就一台机器,但是服务消费者有上百台,可能每天调用量达到上亿次!此时用长连接是最合适的,就是跟每个服务消费者维持一个长连接就可以,可能总共就 100 个连接。然后后面直接基于长连接 NIO 异步通信,可以支撑高并发请求。

长连接,通俗点说,就是建立连接过后可以持续发送请求,无须再建立连接。而短连接,每次要发送请求之前,需要先重新建立一次连接。

rmi 协议

走Java二进制序列化,多个短连接,适合消费者和提供者数量差不多的情况,适用于文件的传输,一般较少用。

hessian 协议

走hessian 序列化协议,多个短连接,适用于提供者数量比消费者数量还多的情况,适用于文件的传输,一般较少用。

http 协议

走json 序列化。

webservice

走 SOAP 文本序列化。

dubbo支持的序列化协议

dubbo支持 hession、Java 二进制序列化、json、SOAP文本序列化多种序列化协议。但是 hessian 是其默认的序列化协议。

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

可能有一些同学比较习惯于 JSON or XML 数据存储格式,对于 Protocol Buffer 还比较陌生。 Protocol Buffer 其实是 Google 出品的一种轻量并且高效的结构化数据存储格式,性能比JSON 、 XML 要高很多。 其实 PB 之所以性能如此好,主要得益于两个:第一,它使用proto 编译器Varint,自动进行序列化和反序列化,速度非常快,应该比XML 和 JSON 快上了 20~100 倍;第二,它的数据压缩效果好,就是说它序列化后的数据量体积小。因为体积小,传输起来带宽和速度上会有优化。

dubbo 负载均衡策略

random loadbalance

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

roundrobin loadbalance

这个的话默认就是均匀地将流量打到各个机器上去,但是如果各个机器的性能不一样,容易导致性能差的机器负载过高。所以此时需要调整权重,让性能差的机器承载权重小一些,流量少一些。

leastactive loadbalance

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

consistanthash loadbalance

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

dubbo 集群容错策略

failover cluster 模式

失败自动切换,自动重试其他机器,默认就是这个,常用于读操作。(失败重试其它机器)

failfast cluster 模式

一次调用失败就立即失败,常用于非幂等性的写操作,比如新增一条记录(调用失败就立即失败)

failsafe cluster 模式

出现异常时忽略掉,常用于不重要的接口调用,比如记录日志。

failback cluster 模式

失败了后台自动记录请求,然后定时重发,比较适合于写消息队列这种。

forking cluster 模式

并行调用多个 provider,只要一个成功就立即返回。常用于实时性要求比较高的读操作,但是会浪费更多的服务资源,可通过 forks=“2” 来设置最大并行数。

broadcacst cluster

逐个调用所有的 provider。任何一个 provider 出错则报错(从 2.1.0 版本开始支持)。通常用于通知所有提供者更新缓存或日志等本地资源信息。

dubbo动态代理策略

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

spi

简单来说,就是 service provider interface ,说白了是什么意思呢,比如你有个接口,现在这个接口有 3 个实现类,那么在系统运行的时候对这个接口到底选择哪个实现类呢? 这就需要 spi 了,需要根据指定的配置或者是默认的配置,去找到对应的实现类加载进来,然后用这个实现类的实例对象。

spi 机制一般用在哪里?插件扩展的场景,比如说你开发了一个给别人使用的开源框架,如果你想让别人自己写个插件,插到你的开源框架里去,从而扩展某个功能,这个时候 spi 思想就用上了。

Java spi 思想

比如说 jdbc。 Java 定义了一套 jdbc 的接口,但是 Java 并没有提供 jdbc 的实现类。 但是实际上项目跑的时候,要使用jdbc 接口的哪些实现类呢?一般来说,我们要根据自己使用的数据库,比如 mysql,你就将 mysql-jdbc-connector.jar 引入进来;oracle,你就将oracle-jdbc-connector.jar 引入进来。在系统跑的时候,碰到你使用jdbc 的接口,会在底层使用你引入的那个 jar 中提供的实现类。

dubbo 的 spi 思想

dubbo 也用了 spi 思想,不过没有用 jdk 的 spi 机制,是自己实现的一套 spi 机制。Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).get

Protocol 接口,在系统运行的时候,dubbo 会判断一下应该选用这个 Protocol 接口的哪个实现类来实例化对象来使用。 它会去找一个你配置的 Protocol,将你配置的 Protocol 实现类,加载到 jvm 中来,然后实例化对象,就用你的那个 Protocol 实现类就可以了。 上面那个代码就是 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

其实就是 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 中的组件

自己写个工程,要是那种可以打成 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=实现类 。

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

分布式接口幂等性

所谓幂等性,就是说一个接口,多次发起同一个请求,你这个接口得保证结果是准确的,比如不能多扣款、不能多插如多条数据、不能将统计值多加了 1。这就是幂等性。

其实保证幂等性主要是三点:

  1. 对于每个请求必须有一个唯一的标识,举个栗子:订单支付请求,肯定得包含订单 id,一个订单 id 最多支付一次,对吧。
  2. 每次处理完请求之后,必须有一个记录标识这个请求处理过了。常用的方案是在 mysql中记录个状态啥的,比如支付之前记录一条这个订单的支付流水。
  3. 每次接收请求需要进行判断,判断之前是否处理过。比如说,如果有一个订单已经支付了,就已经有了一条支付流水,那么如果重复发送这个请求,则此时先插入支付流水, orderId 已经存在了,唯一键约束生效,报错插入不进去的。然后你就不用再扣款了。

分布式接口顺序性

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

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

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

  • 上来你的服务就得去注册中心注册吧,你是不是得有个注册中心,保留各个服务的信息, 可以用zookeeper 来做,对吧。
  • 然后你的消费者需要去注册中心拿对应的服务信息吧,对吧,而且每个服务可能会存在于多台机器上。
  • 接着你就该发起一次请求了,咋发起?当然是基于动态代理了,你面向接口获取到一个动态代理,这个动态代理就是接口在本地的一个代理,然后这个代理会找到服务对应的机器地址。
  • 然后找哪个机器发送请求?那肯定得有个负载均衡算法了,比如最简单的可以随机轮询是不是。
  • 接着找到一台机器,就可以跟它发送请求了,第一个问题咋发送?你可以说用 netty了, nio方式;第二个问题发送啥格式数据?你可以说用hessian 序列化协议了,或者是别的, 对吧。然后请求过去了。
  • 服务器那边一样的,需要针对你自己的服务生成一个动态代理,监听某个网络端口了,然后代理你本地的服务代码。接收到请求的时候,就调用对应的服务代码

转载地址:http://whtwb.baihongyu.com/

你可能感兴趣的文章
【LEETCODE】119-Pascal's Triangle II
查看>>
【LEETCODE】88-Merge Sorted Array
查看>>
【LEETCODE】19-Remove Nth Node From End of List
查看>>
【LEETCODE】125-Valid Palindrome
查看>>
【LEETCODE】28-Implement strStr()
查看>>
【LEETCODE】6-ZigZag Conversion
查看>>
【LEETCODE】8-String to Integer (atoi)
查看>>
【LEETCODE】14-Longest Common Prefix
查看>>
【LEETCODE】38-Count and Say
查看>>
【LEETCODE】278-First Bad Version
查看>>
【LEETCODE】303-Range Sum Query - Immutable
查看>>
【LEETCODE】21-Merge Two Sorted Lists
查看>>
【LEETCODE】231-Power of Two
查看>>
【LEETCODE】172-Factorial Trailing Zeroes
查看>>
【LEETCODE】112-Path Sum
查看>>
【LEETCODE】9-Palindrome Number
查看>>
【极客学院】-python学习笔记-Python快速入门(面向对象-引入外部文件-Web2Py创建网站)
查看>>
【LEETCODE】190-Reverse Bits
查看>>
【LEETCODE】67-Add Binary
查看>>
【LEETCODE】7-Reverse Integer
查看>>