Dubbo
RPC Dubbo
为什么需要Dubbo?
前期是单体应用、垂直应用框架、SOA架构
RPC
- RPC(Remote Procedure Call)远程过程调⽤,简单的理解是⼀个节点请求另⼀个节点提供的服务
- 本地过程调⽤:如需将本地student对象age+1,可以实现⼀个addAge()⽅法,将student对象传⼊,对年龄进⾏更新之后返回即可。
- 远程过程调⽤:上述操作的过程中,如果addAge()这个⽅法在服务端,执⾏函数的函数体在远程机器上。
// Client端 Student student = Call(ServerAddr, addAge, student)
将这个调⽤映射为Call ID。
将Call ID,student(params)序列化,以⼆进制形式打包
把2中得到的数据包发送给ServerAddr,这需要使⽤⽹络传输层(server端第一步开始)
等待服务器返回结果
如果服务器调⽤成功,那么就将结果反序列化,并赋给student,年龄更新
// Server端
- 在本地维护⼀个Call ID到函数指针的映射call_id_map,可以⽤Map<String, Method>
callIdMap
等待客户端请求
得到⼀个请求后,将其数据包反序列化,得到Call ID
通过在callIdMap中查找,得到相应的函数指针
将student(params)反序列化后,在本地调⽤addAge()函数,得到结果
将student结果序列化后通过⽹络返回给Client
RPC调用过程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XP7KFBpg-1629195447417)(/Users/apple/Library/Application Support/typora-user-images/image-20210717202405705.png)]
- 客户端(Client):服务调⽤⽅。
- 客户端存根(Client Stub):存放服务端地址信息,将客户端的请求参数数据信息打包成⽹络消息,再通过⽹络传输发送给服务端。
- 服务端存根(Server Stub):接收客户端发送过来的请求消息并进⾏解包,然后再调⽤本地服务进⾏处理。
- 服务端(Server):服务的真正提供者。
- Network Service:底层传输,可以是 TCP 或 HTTP。
一次性服务过程
1.服务消费者(Client 客户端)通过本地调⽤的⽅式调⽤服务。
2.客户端存根(Client Stub)接收到调⽤请求后负责将⽅法、⼊参等信息序列化(组装)成能够进⾏⽹络传
输的消息体。
3.客户端存根(Client Stub)找到远程的服务地址,并且将消息通过⽹络发送给服务端。
4.服务端存根(Server Stub)收到消息后进⾏解码(反序列化操作)。
5.服务端存根(Server Stub)根据解码结果调⽤本地的服务进⾏相关处理
6.服务端(Server)本地服务业务处理。
7.处理结果返回给服务端存根(Server Stub)。
8.服务端存根(Server Stub)序列化结果。
9.服务端存根(Server Stub)将结果通过⽹络发送⾄消费⽅。
10.客户端存根(Client Stub)接收到消息,并进⾏解码(反序列化)。
11.服务消费⽅得到最终结果。
完整的RPC进程图
- 在⼀个典型 RPC 的使⽤场景中,包含了服务发现、负载、容错、⽹络传输、序列化等组件,其中“RPC 协议”就指明了程序如何进⾏⽹络传输和序列化。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-42XyTS0F-1629195447419)(/Users/apple/Library/Application Support/typora-user-images/image-20210717203159469.png)]
RPC的核心功能
-
服务寻址
1、服务寻址可以使⽤ Call ID 映射。在本地调⽤中,函数体是直接通过函数指针来指定的,但是在远程调⽤中,函数指针是不⾏的,因为两个进程的地址空间是完全不⼀样的。
2、所以在 RPC 中,所有的函数都必须有⾃⼰的⼀个 ID。这个 ID 在所有进程中都是唯⼀确定的。
3、客户端在做远程过程调⽤时,必须附上这个 ID。然后我们还需要在客户端和服务端分别维护⼀个函数和Call ID的对应表。
4、当客户端需要进⾏远程调⽤时,它就查⼀下这个表,找出相应的 Call ID,然后把它传给服务端,服务端也通过查表,来确定客户端需要调⽤的函数,然后执⾏相应函数的代码。
实现方式:服务注册中心
要调⽤服务,⾸先你需要⼀个服务注册中⼼去查询对⽅服务都有哪些实例。Dubbo 的服务注册中⼼是可以配置的,官⽅推荐使⽤ Zookeeper。
-
数据流的序列化和反序列化
客户端怎么把参数值传给远程的函数呢?
在本地调⽤中,我们只需要把参数压到栈⾥,然后让函数⾃⼰去栈⾥读就⾏。
但是在远程过程调⽤时,客户端跟服务端是不同的进程,不能通过内存来传递参数。
这时候就需要客户端把参数先转成⼀个字节流,传给服务端后,再把字节流转成⾃⼰能读取的格式。
只有⼆进制数据才能在⽹络中传输,序列化和反序列化的定义是:
- 将对象转换成⼆进制流的过程叫做序列化
- 将⼆进制流转换成对象的过程叫做反序列化
常见的序列化方式:
- Hessian 是动态类型、⼆进制、紧凑的,并且可跨语⾔移植的⼀种序列化框架。Hessian 协议要⽐JDK、JSON 更加紧凑,性能上要⽐ JDK、JSON 序列化⾼效很多,⽽且⽣成的字节数也更⼩。
- Protobuf 是 Google 公司内部的混合语⾔数据标准,是⼀种轻便、⾼效的结构化数据存储格式,可以⽤于结构化数据序列化,⽀持 Java、Python、C++、Go 等语⾔。序列化后体积相⽐ JSON、Hessian ⼩很多;
- Thrift是Facebook开源提供的⼀个⾼性能,轻量级RPC服务框架,其产⽣正是为了满⾜当前⼤数据量、分布式、跨语⾔、跨平台数据通讯的需求。
-
网络传输
⽹络传输:远程调⽤往往⽤在⽹络上,客户端和服务端是通过⽹络连接的。
所有的数据都需要通过⽹络传输,因此就需要有⼀个⽹络传输层。⽹络传输层需要把 Call ID 和序列化
后的参数字节流传给服务端,然后再把序列化后的调⽤结果传回客户端。
只要能完成这两者的,都可以作为传输层使⽤。因此,它所使⽤的协议其实是不限的,能完成传输就
⾏。 ⼤部分 RPC 框架都使⽤ TCP 协议,但其实 UDP 也可以。
TCP 的连接是最常⻅的,简要分析基于 TCP 的连接:通常 TCP 连接可以是按需连接(需要调⽤的时候就
先建⽴连接,调⽤结束后就⽴⻢断掉),也可以是⻓连接(客户端和服务器建⽴起连接之后保持⻓期持
有,不管此时有⽆数据包的发送,可以配合⼼跳检测机制定期检测建⽴的连接是否存活有效),多个远程
过程调⽤共享同⼀个连接。
所以,要实现⼀个 RPC 框架,只需要把以下三点实现了就基本完成了:
- Call ID 映射:可以直接使⽤函数字符串,也可以使⽤整数 ID。映射表⼀般就是⼀个哈希表。
- 序列化反序列化:可以⾃⼰写,也可以使⽤ Protobuf 或者 FlatBuffers 之类的。
- ⽹络传输库:可以⾃⼰写 Socket,或者⽤ Asio,ZeroMQ,Netty 之类。
Dubbo有哪些能力?
- ⾯向接⼝代理的⾼性能RPC调⽤
提供⾼性能的基于代理的远程调⽤能⼒,服务以接⼝为粒度,为开发者屏蔽远程调⽤底层细节
- 智能负载均衡
内置多种负载均衡策略,智能感知下游节点健康状况,显著减少调⽤延迟,提⾼系统吞吐量
- 服务⾃动注册与发现
⽀持多种注册中⼼服务,服务实例上下线实时感知
- ⾼度可扩展能⼒
遵循微内核+插件的设计原则,所有核⼼能⼒如Protocol、Transport、Serialization被设计为扩展点,平等对待内置实现和第三⽅实现
- 运⾏期流量调度
内置条件、脚本等路由策略,通过配置不同的路由规则,轻松实现灰度发布,同机房优先等功能
- 可视化的服务治理与运维
提供丰富服务治理、运维⼯具:随时查询服务元数据、服务健康状态及调⽤统计,实时下发路由策略、调整配置参数
-
地址维护(当服务越来越多时,服务 URL 配置管理变得⾮常困难);这时候需要⼀个服务注册中⼼,动态的注册和发现服务,使服务的位置透明
-
负载均衡(当服务越来越多时,F5 硬件负载均衡器的单点压⼒也越来越⼤);通过在消费⽅获取服务提供⽅地址列表,实现软负载均衡和 Failover
-
限流/容错/降级;
-
链路监控;监控: 服务的调⽤量越来越⼤,服务的容量问题就暴露出来,这个服务需要多少机器⽀撑?什么时候该加机器?为了解决这些问题,第⼀步,要将服务现在每天的调⽤量,响应时间,都统计出来,作为容量规划的参考指标。其次,要可以动态调整权重,在线上,将某台机器的权重⼀直加⼤,并在加⼤的过程中记录响应时间的变化,直到响应时间到达阈值,记录此时的访问量,再以此访问量乘以机器数反推总容量。
Dubbo架构图
节点说明:
prodiver:暴露服务的服务提供⽅、
consumer:调⽤远程服务的服务消费⽅
registry:服务注册与发现的注册中⼼
monitor:统计服务的调⽤次数和调⽤时间的监控中⼼
container:服务运⾏容器
调用关系说明:
服务容器负责启动,加载,运⾏服务提供者。
服务提供者在启动时,向注册中⼼注册⾃⼰提供的服务。
服务消费者在启动时,向注册中⼼订阅⾃⼰所需的服务。
注册中⼼返回服务提供者地址列表给消费者,如果有变更,注册中⼼将基于⻓连接推送变更数据给消费者。
服务消费者,从提供者地址列表中,基于软负载均衡算法,选⼀台提供者进⾏调⽤,如果调⽤失败,再选另⼀台调⽤。
服务消费者和提供者,在内存中累计调⽤次数和调⽤时间,定时每分钟发送⼀次统计数据到监控中⼼。
Dubbo架构特点
-
连通性(zookeeper)
- 注册中⼼负责服务地址的注册与查找,相当于⽬录服务,服务提供者和消费者只在启动时与注册中⼼交互,注册中⼼不转发请求,压⼒较⼩
- 监控中⼼负责统计各服务调⽤次数,调⽤时间等,统计先在内存汇总后每分钟⼀次发送到监控中⼼服务器,并以报表展示
- 服务提供者向注册中⼼注册其提供的服务,并汇报调⽤时间到监控中⼼,此时间不包含⽹络开销
- 服务消费者向注册中⼼获取服务提供者地址列表,并根据负载算法直接调⽤提供者,同时汇报调⽤时间到监控中⼼,此时间包含⽹络开销
- 注册中⼼,服务提供者,服务消费者三者之间均为⻓连接,监控中⼼除外
- 注册中⼼通过⻓连接感知服务提供者的存在,服务提供者宕机,注册中⼼将⽴即推送事件通知消费者
- 注册中⼼和监控中⼼全部宕机,不影响已运⾏的提供者和消费者,消费者在本地缓存了提供者列表
- 注册中⼼和监控中⼼都是可选的,服务消费者可以直连服务提供者
-
健壮性
-
监控中⼼宕掉不影响使⽤,只是丢失部分采样数据
-
数据库宕掉后,注册中⼼仍能通过缓存提供服务列表查询,但不能注册新服务注册中⼼对等集群,任意⼀台宕掉后,将⾃动切换到另⼀台
-
注册中⼼全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯
-
服务提供者⽆状态,任意⼀台宕掉后,不影响使⽤
-
服务提供者全部宕掉后,服务消费者应⽤将⽆法使⽤,并⽆限次重连等待服务提供者恢复
-
-
伸缩性
- 注册中⼼为对等集群,可动态增加机器部署实例,所有客户端将⾃动发现新的注册中⼼
-
服务提供者⽆状态,可动态增加机器部署实例,注册中⼼将推送新的服务提供者信息给消费者
SpringBoot中使用Dubbo
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>dubbo-demo-other</artifactId>
<groupId>com.end</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.end</groupId>
<artifactId>dubbo-demo-other-provider</artifactId>
<version>1.0.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>com.end</groupId>
<artifactId>dubbo-demo-api</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.dubbo/dubbospring-boot-starter -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.3</version>
</dependency>
<!--https://mvnrepository.com/artifact/org.springframework.boot/spring-bootstarter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.3.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.dubbo/dubboregistry-zookeeper -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-zookeeper</artifactId>
<version>2.7.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.dubbo/dubbometadata-report-zookeeper -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metadata-report-zookeeper</artifactId>
<version>2.7.3</version>
</dependency>
</dependencies>
</project>
Boot和Dubbo整合的方式:XML和注解
-
xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <dubbo:application name="demo-other-provider" /> <dubbo:metadata-report address="zookeeper://127.0.0.1:2181"/> <dubbo:registry id="registry" address="zookeeper://127.0.0.1:2181? registry-type=service"/> <dubbo:protocol name="dubbo" port="20881"/> <bean id="orderService" class="com.end.dubbo.service.OrderServiceImpl"/> <dubbo:service interface="com.end.dubbo.api.service.OrderService" timeout="3000" ref="orderService" registry="registry"/> </beans>
主启动类
package com.end.dubbo; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ImportResource; @ImportResource(locations = {"classpath:dubbo-other-provider.xml"}) @SpringBootApplication public class DubboOtherProviderApplication { public static void main(String[] args) { SpringApplication.run(DubboOtherProviderApplication.class, args); } }
-
注解
RPC分布式扩展
Dubbp 集群容错
-
Failover Cluster
失败自动切换,当出现失败,重试其他服务器,。通常用与读操作,但重试会带来更长延迟。科通过retries = 2,来设置重试次数(不含第一次);
<dubbo:reference retries = “2”>
-
Failfast Cluster
快速失败,只发起⼀次调⽤,失败⽴即报错。通常⽤于⾮幂等性的写操作,⽐如新增记录。
<dubbo:service cluster=“failfast” />
-
Failsafe Cluster
失败安全,出现异常时,直接忽略。通常⽤于写⼊审计⽇志等操作。
<dubbo:service cluster=“failsafe”/>
-
Failback Cluster
失败⾃动恢复,后台记录失败请求,定时重发。通常⽤于消息通知操作。
<dubbo:service cluster=“failback”/>
-
Forking Cluster
并⾏调⽤多个服务器,只要⼀个成功即返回。通常⽤于实时性要求较⾼的读操作,但需要浪费更多服务资源。可通过 forks=“2” 来设置最⼤并⾏数。
<dubbo:service cluster=“forking” forks=“2”>
-
Broadcast Cluster
⼴播调⽤所有提供者,逐个调⽤,任意⼀台报错则报错。通常⽤于通知所有提供者更新缓存或⽇志等本地资源信息。
<dubbo:service cluster=“broadcast” forks=“2”>
Dubbo 负载均衡
-
Random LoadBalance
随机,按权重设置随机概率。
在⼀个截⾯上碰撞的概率⾼,但调⽤量越⼤分布越均匀,⽽且按概率使⽤权重后也⽐较均匀,
有利于动态调整提供者权重。
-
RoundRobin LoadBalance
轮询,按公约后的权重设置轮询⽐率。
存在慢的提供者累积请求的问题,⽐如:第⼆台机器很慢,但没挂,当请求调到第⼆台时就卡
在那,久⽽久之,所有请求都卡在调到第⼆台上。
-
LeastActive LoadBalance
最少活跃调⽤数,相同活跃数的随机,活跃数指调⽤前后计数差。
使慢的提供者收到更少请求,因为越慢的提供者的调⽤前后计数差会越⼤。
-
ConsistentHash LoadBalance
⼀致性 Hash,相同参数的请求总是发到同⼀提供者。
当某⼀台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引
起剧烈变动。
hash偏滑问题
这⾥相同颜⾊的节点均属于同⼀个服务提供者,⽐如 Invoker1-1,Invoker1-2,……, Invoker1-160。
这样做的⽬的是通过引⼊虚拟节点,让 Invoker 在圆环上分散开来,避免数据倾斜问题。所谓数据倾斜是指,由于节点不够分散,导致⼤量请求落到了同⼀个节点上,⽽其他节点只会接收到了少量请求的情况。
例如:
如上,由于 Invoker-1 和 Invoker-2 在圆环上分布不均,导致系统中75%的请求都会落到 Invoker-1上,只有 25% 的请求会落到 Invoker-2 上。解决这个问题办法是引⼊虚拟节点,通过虚拟节点均衡各个节点的请求量。
Dubbo的最佳实践
-
分包
建议将服务接⼝、服务模型、服务异常等均放在 API 包中,因为服务模型和异常也是 API 的⼀部分,这样做也符合分包原则:重⽤发布等价原则(REP),共同重⽤原则(CRP)。
如果需要,也可以考虑在 API 包中放置⼀份 Spring 的引⽤配置,这样使⽤⽅只需在 Spring 加载过程中引⽤此配置即可。配置建议放在模块的包⽬录下,以免冲突,如: com/alibaba/china/xxx/dubbo-reference.xml 。
-
粒度
服务接⼝尽可能⼤粒度,每个服务⽅法应代表⼀个功能,⽽不是某功能的⼀个步骤,否则将⾯临分布式事务问题,Dubbo 暂未提供分布式事务⽀持。
服务接⼝建议以业务场景为单位划分,并对相近业务做抽象,防⽌接⼝数量爆炸。
不建议使⽤过于抽象的通⽤接⼝,如: Map query(Map) ,这样的接⼝没有明确语义,会给后期维护带来不便。
-
版本
每个接⼝都应定义版本号,为后续不兼容升级提供可能,如:<dubbo:service interface=“com.xxx.XxxService” version=“1.0” /> 。
建议使⽤两位版本号,因为第三位版本号通常表示兼容升级,只有不兼容时才需要变更服务版本。
当不兼容时,先升级⼀半提供者为新版本,再将消费者全部升为新版本,然后将剩下的⼀半提供者升为新版本。
-
兼容性
服务接⼝增加⽅法,或服务模型增加字段,可向后兼容,删除⽅法或删除字段,将不兼容,枚举类型新增字段也不兼容,需通过变更版本号升级。
-
枚举值(不建议使用)
如果是完备集,可以⽤ Enum ,⽐如: ENABLE , DISABLE 。
如果是业务种类,以后明显会有类型增加,不建议⽤ Enum ,可以⽤ String 代替。
如果是在返回值中⽤了 Enum ,并新增了 Enum 值,建议先升级服务消费⽅,这样服务提供⽅不会返回新值。
如果是在传⼊参数中⽤了 Enum ,并新增了 Enum 值,建议先升级服务提供⽅,这样服务消费⽅不会传⼊新值。
-
序列化
服务参数及返回值建议使⽤ POJO 对象,即通过 setter , getter ⽅法表示属性的对象。
-
调用
不要只是因为是 Dubbo 调⽤,⽽把调⽤ try…catch 起来。 try…catch 应该加上合适的回滚边界上。
Provider 端需要对输⼊参数进⾏校验。如有性能上的考虑,服务实现者可以考虑在 API 包上加上服务Stub 类来完成检验。
Dubbo推荐用法
-
在provider端尽量多配置consumer端的属性
原因:
1、作服务的提供⽅,⽐服务消费⽅更清楚服务的性能参数,如调⽤的超时时间、合理的重试次数等
2、在 Provider 端配置后,Consumer 端不配置则会使⽤ Provider 端的配置,即 Provider 端的配置可以作为 Consumer 的缺省值 1。否则,Consumer 会使⽤ Consumer 端的全局设置,这对于Provider 是不可控的,并且往往是不合理的
Provider 端尽量多配置 Consumer 端的属性,让 Provider 的实现者⼀开始就思考 Provider 端的服务特点和服务质量等问题。
<dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService" timeout="300" retries="2" loadbalance="random" actives="0" />
<dubbo:service interface="com.alibaba.hello.api.WorldService" version="1.0.0" ref="helloService" timeout="300" retries="2" loadbalance="random" actives="0" >
<dubbo:method name="findAllPerson" timeout="10000" retries="9" loadbalance="leastactive" actives="5" />
<dubbo:service/>
建议在provider端配置的consumer端的属性
timeout :⽅法调⽤的超时时间
retries :失败重试次数,缺省是 2 2
loadbalance :负载均衡算法 3,缺省是随机 random 。还可以配置轮询 roundrobin 、最不活跃优先 4 leastactive 和⼀致性哈希 consistenthash 等
actives :消费者端的最⼤并发调⽤限制,即当 Consumer 对⼀个服务的并发调⽤到上限后,新调⽤会阻塞直到超时,在⽅法上配置 dubbo:method 则针对该⽅法进⾏并发限制,在接⼝上配置dubbo:service ,则针对该服务进⾏并发限制
在provider端配置合理的provider的属性
<dubbo:protocol threads="200" />
<dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService" executes="200" >
<dubbo:method name="findAllPerson" executes="50" />
</dubbo:service>
threads :服务线程池⼤⼩
executes :⼀个服务提供者并⾏执⾏请求上限,即当 Provider 对⼀个服务的并发调⽤达到上限后,新调⽤会阻塞,此时 Consumer 可能会超时。在⽅法上配置 dubbo:method 则针对该⽅法进⾏并发限制,在接⼝上配置 dubbo:service ,则针对该服务进⾏并发限制
配置管理信息
⽬前有负责⼈信息和组织信息⽤于区分站点。以便于在发现问题时找到服务对应负责⼈,建议⾄少配置两个⼈以便备份。负责⼈和组织信息可以在运维平台 (Dubbo Ops) 上看到。
在应⽤层⾯配置负责⼈、组织信息:
<dubbo:application owner=”ding.lid,william.liangf” organization=”intl” />
在服务层⾯(服务端)配置负责⼈:
<dubbo:service owner=”ding.lid,william.liangf” />
在服务层⾯(消费端)配置负责⼈:
<dubbo:reference owner=”ding.lid,william.liangf” />
配置Dubbo缓存文件
提供者列表缓存⽂件:
<dubbo:registry file=”${user.home}/output/dubbo.cache” />
注意:
-
可以根据需要调整缓存⽂件的路径,保证这个⽂件不会在发布过程中被清除;
-
如果有多个应⽤进程,请注意不要使⽤同⼀个⽂件,避免内容被覆盖;
该⽂件会缓存注册中⼼列表和服务提供者列表。配置缓存⽂件后,应⽤重启过程中,若注册中⼼不可⽤,应⽤会从该缓存⽂件读取服务提供者列表,进⼀步保证应⽤可靠性。
监控配置
-
使⽤固定端⼝暴露服务,⽽不要使⽤随机端⼝这样在注册中⼼推送有延迟的情况下,消费者通过缓存列表也能调⽤到原地址,保证调⽤成功。
-
使⽤ Dubbo Admin 监控注册中⼼上的服务提供⽅使⽤ Dubbo Admin 监控服务在注册中⼼上的状态,确保注册中⼼上有该服务的存在。
-
服务提供⽅可使⽤ Dubbo Qos 的 telnet 或 shell 监控项监控服务提供者端⼝状态: echo status | nc -i 1 20880 | grep OK | wc -l ,其中 20880 为服务端⼝
-
服务消费⽅可通过将服务强制转型为 EchoService,并调⽤ e c h o ( ) 测 试 该 服 务 的 提 供 者 是 可 ⽤ 如 a s s e r t E q a u l s ( “ O K ” , ( ( E c h o S e r v i c e ) m e m b e r S e r v i c e ) . echo() 测试该服务的提供者是可⽤如 assertEqauls(“OK”, ((EchoService)memberService). echo()测试该服务的提供者是可⽤如assertEqauls(“OK”,((EchoService)memberService).echo(“OK”));
Dubbo常见面试题
分布式事务
为什么会有分布式事务的问题?
分布式理论基础
-
CAP理论
C: 强⼀致性(⼤部分互联⽹公司会牺牲强⼀致性)
A:可⽤性 (绝⼤部份互联⽹公司需要考虑的)
P:分区容错性(绝⼤部份互联⽹公司需要考虑的)
- BASE
- Zookeeper满足CAP理论的那些? CP
分布式事务常见方案
- 2CP(XA)【两阶段提交】
- TCC(事务补偿)【try、comfire、cancel】
- 本地消息事件+消息队列【使用最多】
工作中常用的解决方案
常见的业务流程:
-
先创建订单,再发送消息?
public void processOrder() { // 订单处理(业务操作) orderService.process(); // 发送订单处理成功消息(发送消息) sendBizMsg (); }
-
先发送消息,再创建订单?
public void processOrder() { // 发送订单处理成功消息(发送消息) sendBizMsg (); // 订单处理(业务操作) orderService.process(); }
-
两步放在一个事务里,做回滚?
@Transactionnal public void processOrder() { try{ // 订单处理(业务操作) orderService.process(); // 发送订单处理成功消息(发送消息) sendBizMsg (); } catch(Exception e){ //事务回滚; } }
基于本地消息的最终一致性解决方案
如何做好系统设计确保最终一致性?
- 消息记录本地事件表
- 定时服务轮训事件状态表