Dubbo的高级特性
1、dubbo序列化
1.1、序列化、反序列化的介绍
- 序列化:把对象转换为字节序列的过程称为对象的序列化。
- 反序列化:把字节序列恢复为对象的过程称为对象的反序列化。
先来思考两个问题:
- 普通的Java对象的生命周期是仅限于一个JVM中的,只要JVM停止,这个对象也就不存在了,下次JVM启动我们还想使用这个对象怎么办呢?
- 或者我们想要把一个对象传递给另外一个JVM的时候,应该怎么做呢?
这两个问题的答案分别是:
- 将该对象进行序列化,然后保存在文件中,
- 将对象序列化后通过网络传输到另一个JVM,由另外一个JVM反序列化成一个对象,然后供JVM使用。
对象序列化就是将一个存在内存中的对象,转换为可存储或者可传输的二进制流,并且根据序列化的规则可以进行反序列化。
(一般来说需要保存的对象信息包括类的全限定名称、未被transparent修饰的字段值,将这些信息按照一定的规则转换为二进制之后进行网络传输,在接收到的一端,就可以根据这些信息先实例化这个类对应的对象,然后将对应的属性值填入新构造的对象,在使用者看来就是使用的原来的对象。)
1.2、dubbo序列化
dubbo支持很多种通信协议,其中dubbo协议作为默认的通信协议。dubbo 调用是需要跨 JVM,需要进行网络通信,这就需要使用到序列化与反序列化。
dubbo 的消费者和服务提供者之间进行传递的数据都需要进行序列化。我们有时可能看到 dubbo 的消费者和提供者使用字符串进行数据传递,好像不需要做什么序列化操作,实际上这是因为 String 类本身就已经实现了序列化接口了。所以如果传递的参数即消费者传递的参数或者提供者返回的数据是我们自己定义的一些实体类的话,我们需要对该类手动实现序列化。
dubbo 中进行序列化的方式很简单,只需该类实现 Serializable 即可。
比如消费者和提供者之间通过 User 类来进行数据传递,比如提供者方法接收一个 User 类做参数,或者提供者返回的数据是一个 User 类,则 User 类需要实现序列化。示例如下:
import java.io.Serializable; public class User implements Serializable { private String name; private int age; private int id; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
dubbo 中消费者和提供者之间传递的数据未实现序列化的话,调用提供者的接口会直接报错。
2、dubbo服务本地缓存(地址缓存)
根据官方图,dubbo调用者需要通过注册中心(例如:ZK)注册信息,获取提供者,但是如果频繁往ZK获取信息,肯定会存在单点故障问题(即注册中心挂了就全挂了),所以dubbo提供了将提供者信息缓存在本地的方法。
Dubbo在订阅注册中心的回调处理逻辑当中会保存服务提供者信息到本地缓存文件当中(同步/异步两种方式),以url纬度进行全量保存。Dubbo在服务引用过程中会创建registry对象并加载本地缓存文件,会优先订阅注册中心,订阅注册中心失败后会访问本地缓存文件内容获取服务提供信息。
所以说如果注册中心挂了,服务仍然是可以访问和被访问的。当如果是访问新服务的话就不行,因为并没有把新服务的地址缓存起来。
3、超时与重试
3.1、服务超时
如果服务器的提供者发生了故障,服务消费者在调用服务提供者的时候会发生阻塞、等待的情形,如果服务消费者一直等待下去,消费者会浪费大量资源,可能导致消费者也不可用。dubbo 利用超时机制来解决这个问题,设置一个超时时间,在这个时间段内,无法完成服务访问,则自动断开连接。
超时原因:
- 消费者:当消费者发起一次请求后,如果在规定时间内未得到服务端的响应则直接返回超时异常,但服务端的代码依然在执行。
- 生产者:消费端发起一次请求后,一直等待服务端的响应,服务端在方法执行到指定时间后如果未执行完,此时返回一个超时异常给到消费端。
在 dubbo 中使用 timeout 属性来配置超时时间,默认值是 1000,单位毫秒。在服务端和消费端都可以设置超时时间,但是服务消费者在调用的时候,会根据 timeout 配置的优先级,来获取最终的超时配置时间。
超时配置优先级为:
- 消费者Method > 提供者method > 消费者Reference > 提供者Service > 消费者全局配置provider > 提供者全局配置consumer。
3.1.1、配置服务超时时间
1)在接口层面配置
- 在服务提供者的实现上配置
@Service(timeout = 10000) public class HelloServiceImpl implements HelloService { { ... }
- 在消费者调用方加
@Reference(timeout = 10000) private HelloService helloService;
2)全局配置
配置分别作为生产者和消费者时的超时时间,如下:
dubbo.provider.timeout = 10000
dubbo.consumer.timeout = 10000
3.2、重试
Dubbo 服务消费者在尝试调用一次服务之后,如出现非业务异常(服务突然不可用、超时等),Dubbo 默认会进行额外的最多2次重试。默认是重试2次,加上初始一次,即总共实际上会调用提供者3次。
3.2.1、配置服务超时时间
重试次数可以使用 retries=重试次数
来设置。
1)在接口层面配置
- 在服务提供者的实现上配置
@Service(retries=2) public class HelloServiceImpl implements HelloService { ... }
- 在消费者调用方加
@Reference(retries= 10000) private HelloService helloService;
2)全局配置
配置分别作为生产者和消费者时的重试次数,如下:
spring.dubbo.consumer.retries=1 spring.dubbo.provider.retries=2
4、服务多版本
在 dubbo 中,如果某个服务的功能出现变更或者出了新功能,可以通过版本号来控制先让一部分用户使用新功能,用户反馈没问题时,再将所有用户迁移到新功能。dubbo 中使用version 属性来设置和调用同一个接口的不同版本。
服务提供者通过 version 实现一个服务多个版本,如下:
@Service(version = "v1.0") public class UserServiceImpl implements UserService { ... }
@Service(version = "v2.0") public class UserServiceImpl2 implements UserService { ... }
服务消费者通过 version 来调用指定版本的接口,如下:
@Reference(version = "v2.0")//远程注入 private UserService userService;
5、服务负载均衡
Dubbo提供了4种均衡策略,默认为Random(随机调用)
- Random LoadBalance(随机,按照权重的设置随机概率)
- RoundRobin LoadBalance(轮询,按照权重设置轮询比率)
- LeastActive LoadBalance(最少活跃数,响应快的提供者接受越多请求,响应慢的接受越少请求)
- ConsistentHash LoadBalance(一致性Hash,相同参数的请求总是发到同一个服务提供者(相同参数默认是指请求的第一个参数)。携带相同的参数总是发送的同一个服务提供者,若服务挂了,则会基于虚拟节点平摊到其他提供者上)
负载均衡策略配置参考:https://blog.csdn.net/liuhenghui5201/article/details/108519253
xml 方式配置:
1)服务端配置
- 服务级别
<dubbo:service interface="..." loadbalance="roundrobin" />
- 方法级别
<dubbo:reference interface="..." loadbalance="roundrobin" />
2)消费端配置
- 服务级别
<dubbo:reference interface="..." loadbalance="roundrobin" />
- 方法级别
<dubbo:reference interface="..."> <dubbo:method name="..." loadbalance="roundrobin"/> </dubbo:reference>
6、集群容错
在微服务环境中,为了保证服务的高可用,很少会有单点服务出现,服务通常都是以集群的形式出现。在dubbo远程调用过程中,被调用的远程服务并不是每时刻都保持良好的状态,当某个服务调用出现异常时候(比如网络抖动、服务短暂不可用),都需要进行容错,就需要到了集群容错机制。
dubbo 中有以下集群容错模式:
- Failover Cluster:失败重试。默认值。当出现失败,重试其它服务器 ,默认重试2次,使用 retries 配置。一般用于读操作
- Failfast Cluster :快速失败,只发起一次调用,失败立即报错。通常用于写操作。
- Failsafe Cluster :失败安全,出现异常时,直接忽略。返回一个空结果。
- Failback Cluster :失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
- Forking Cluster :并行调用多个服务器,只要一个成功即返回。
- Broadcast Cluster :广播调用所有提供者,逐个调用,任意一台报错则报错。
可参考官网:https://dubbo.apache.org/zh/docsv2.7/user/examples/fault-tolerent-strategy/
7、服务降级
dubbo开发中,通常是微服务架构,那么在使用过程中可能会遇到多种问题:
- 多个服务之间可能由于服务没有启动或者网络不通,调用中会出现远程调用失败
- 服务请求过大,需要停止调用其他部分不重要服务以保证本身核心业务的正常运行;
以上两个问题可以使用Dubbo的服务降级来实现,即:在其他服务宕掉或者并发数太高导致的RpcException异常时,进行友好的处理或者提示,而不是内部报错导致系统不可用。
7.1、配置服务降级
Dubbo可以通过服务降级功能临时屏蔽某个出错的非关键性服务,并定义降级后的返回策略。 我们可以向注册中心写入动态配置覆盖规则:
RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension(); Registry registry = registryFactory.getRegistry(URL.valueOf("zookeeper://10.20.153.10:2181")); registry.register(URL.valueOf("override://0.0.0.0/com.foo.BarService?category=configurators&dynamic=false&application=foo&mock=force:return+null"));
也可以通过配置文件进行定义。
<dubbo:reference id="iUser" interface="com.dubbosample.iface.IUser" timeout="1000" check="false" mock="return null">
配置 mock 有以下几种形式:
<dubbo:reference mock="true" .../> <dubbo:reference mock="com.xxxx" .../> <dubbo:reference mock="return null" .../> <dubbo:reference mock="throw xxx" .../> <dubbo:reference mock="force:return .." .../> <dubbo:reference mock="force:throw ..." .../>
mock="force:return null":
表示消费方对该服务的方法调用都直接返回null值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。- 还可以改为
mock="fail:return null":
表示消费方对该服务的方法调用在失败后,再返回null。用来容忍不重要服务不稳定时对调用方的影响。
也可通过 dubbo 的管理控制台(如 dubbo-admin)进行配置。
参考:https://www.cnblogs.com/hup666/p/13472829.html