本篇博文参考dubbo官方文档
本编博文参考javaguide之rpc
文章目录
- 一.RPC
- 1.1 什么是 RPC?
- 1.2 为什么要用 RPC?
- 1.3 RPC 能帮助我们做什么呢?
- 1.4 RPC 的原理是什么?
- 1.5 常见的 RPC 框架总结
- 二.既有 HTTP ,为啥用 RPC 进行服务调用?
- 2.1 RPC只是一种设计而已
- 2.2 HTTP和TCP
- 三.Dubbo2.7简介
- 3.1 简介(了解即可)
- 3.1.1 背景(介绍了网站应用的演进)
- 3.1.2 需求
- 3.1.3 Dubbo架构
- 3.1.4 Dubbo架构具有以下几个特点
- 3.2 快速开始使用dubbo
- 3.2.1 整合示例
- 3.3 Dubbo 依赖基本介绍
- 3.3.1 缺省依赖
- 3.3.2 可选依赖
- 3.4 Dubbo 的 XML Schema 配置参考手册
- 3.5 Dubbo 注册中心参考手册
- 3.5.1 dubbo 协议
- 3.5.2 其余协议请参看官方文档
- 3.6 Dubbo 注册中心参考手册
- 3.6.1 Zookeeper 注册中心参考手册
- 3.6.2 Nacos 注册中心参考手册
- 四.Eclipse与IntelliJ IDEA比较
- 五 不同粒度配置的覆盖关系
- 六 动态配置中心
- 6.1 外部化配置
- 6.1.1 Zookeeper
- 6.1.2 Apollo
- 6.1.3 自己加载外部化配置
- 6.2 服务治理
- 6.2.1 Zookeeper
- 6.2.2 Apollo
- 七 属性配置
- 八 API配置
- 九 注解配置
- 十 配置加载流程
- 十一 用法手册
- 11.1 在启动时检查依赖的服务是否可用
- 11.2 配置dubbo服务重试次数
- 11.3 集群容错
- 11.3.1 集群容错模式Failfast Cluster
- 11.4 负载均衡
- 11.5 配置 Dubbo 中的线程模型
- 11.6 直连提供者
- 11.7 只订阅不注册
- 11.8 在 Dubbo 中配置多协议
- 11.9 多注册中心(在 Dubbo 中把同一个服务注册到多个注册中心上)
- 11.9.1 多注册中心注册
- 11.9.2 不同服务使用不同注册中心
- 11.9.3 多注册中心引用
- 11.10 服务分组
- 11.11 多版本(在 Dubbo 中为同一个服务配置多个版本)
- 11.12 分组聚合
- 11.13 参数验证(在 Dubbo 中进行参数验证)
- 11.14 指定Ip Port调用Provider
- 11.15 收集Dubbo广播响应
- 11.16 结果缓存
- 11.17 使用泛化调用
- 11.17.1 通过 Spring 使用泛化调用
- 11.17.2 通过 API 方式使用泛化调用
- 11.18 实现泛化调用
- 11.18.1 通过Spring 暴露泛化实现
- 11.18.2 通过 API 方式暴露泛化实现
- 11.19 上下文信息
- 11.20 Dubbo 服务提供方的异步执行
- 11.20.1 定义 CompletableFuture 签名的接口
- 11.21 在 Dubbo 中发起异步调用
- 11.21.1 使用 CompletableFuture 签名的接口
- 11.22 参数回调(通过参数回调从服务器端调用客户端逻辑)
- 11.23 事件通知(在调用之前、调用之后、出现异常时的事件通知)
- 11.24 本地伪装(如何在 Dubbo 中利用本地伪装实现服务降级)
- 11.25 优雅停机(让 Dubbo 服务完成优雅停机)
一.RPC
1.1 什么是 RPC?
RPC(Remote Procedure Call) 即远程过程调用,通过名字我们就能看出 RPC 关注的是远程调用而非本地调用。
1.2 为什么要用 RPC?
因为,两个不同的服务器上的服务提供的方法不在一个内存空间,所以,需要通过网络编程才能传递方法调用所需要的参数。并且,方法调用的结果也需要通过网络编程来接收。但是,如果我们自己手动网络编程来实现这个调用过程的话工作量是非常大的,因为,我们需要考虑底层传输方式(TCP还是UDP)、序列化方式等方面。
1.3 RPC 能帮助我们做什么呢?
简单来说,通过 RPC 可以帮助我们调用远程计算机上某个服务的方法,这个过程就像调用本地方法一样简单。并且!我们不需要了解底层网络编程的具体细节。举个例子:两个不同的服务 A、B 部署在两台不同的机器上,服务 A 如果想要调用服务 B 中的某个方法的话就可以通过 RPC 来做。一言蔽之:RPC 的出现就是为了让你调用远程方法像调用本地方法一样简单。
1.4 RPC 的原理是什么?
下面再贴一个网上的时序图,辅助理解:
1.5 常见的 RPC 框架总结
- RMI(JDK自带):JDK自带的RPC,有很多局限性,不推荐使用。
- Dubbo:Dubbo是 阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成。目前 Dubbo 已经成为 Spring Cloud Alibaba 中的官方组件。
- gRPC:gRPC是可以在任何环境中运行的现代开源高性能RPC框架。它可以通过可插拔的支持来有效地连接数据中心内和跨数据中心的服务,以实现负载平衡,跟踪,运行状况检查和身份验证。它也适用于分布式计算的最后一英里,以将设备,移动应用程序和浏览器连接到后端服务。
- Hessian:Hessian是一个轻量级的remoting on http工具,使用简单的方法提供了RMI的功能。 相比WebService,Hessian更简单、快捷。采用的是二进制RPC协议,因为采用的是二进制协议,所以它很适合于发送二进制数据。
- Thrift:Apache Thrift是Facebook开源的跨语言的RPC通信框架,目前已经捐献给Apache基金会管理,由于其跨语言特性和出色的性能,在很多互联网公司得到应用,有能力的公司甚至会基于thrift研发一套分布式服务框架,增加诸如服务注册、服务发现等
二.既有 HTTP ,为啥用 RPC 进行服务调用?
RPC框架功能更齐全,成熟的RPC框架还提供好了"服务自动注册与发现"、“智能负载均衡”、“可视化的服务治理和运维”、"运行期流量调度"等等功能,这些也算是选择RPC进行服务注册和发现的一方面原因吧!
2.1 RPC只是一种设计而已
RPC只是一种概念、一种设计,就是为了解决不同服务之间的调用问题,它一般会包含有传输协议和序列化协议这两个。 但是,HTTP是一种协议,RPC框架可以使用 HTTP协议作为传输协议或者直接使用TCP作为传输协议,使用不同的协议一般也是为了适应不同的场景。
2.2 HTTP和TCP
1.我们通常谈计算机网络的五层协议的体系结构是指:应用层、传输层、网络层、数据链路层、物理层。
2.应用层(application-layer)的任务是通过应用进程间的交互来完成特定网络应用。 HTTP 属于应用层协议,它会基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。HTTP协议工作于客户端-服务端架构上。浏览器作为HTTP客户端通过 URL 向HTTP服务端即WEB服务器发送所有请求。Web服务器根据接收到的请求后,向客户端发送响应信息。HTTP协议建立在 TCP 协议之上。
3.传输层(transport layer)的主要任务就是负责向两台主机进程之间的通信提供通用的数据传输服务。TCP是传输层协议,主要解决数据如何在网络中传输。相比于UDP、TCP 提供的是面向连接的,可靠的数据传输服务。
三.Dubbo2.7简介
Dubbo 提供了六大核心能力:
3.1 简介(了解即可)
3.1.1 背景(介绍了网站应用的演进)
请参看官方文档,这里不详细介绍
3.1.2 需求
本文介绍了 Dubbo 要解决的需求:
3.1.3 Dubbo架构
3.1.4 Dubbo架构具有以下几个特点
3.2 快速开始使用dubbo
1.Dubbo 采用全 Spring 配置方式,透明化接入应用,对应用没有任何 API 侵入,只需用 Spring 加载 Dubbo 的配置即可,Dubbo 基于 Spring 的 Schema 扩展 进行加载。
2.如果不想使用 Spring 配置,可以通过 API 的方式 进行调用
3.2.1 整合示例
1.服务提供者和消费者的pom.xml添加
<dependency><groupId>com.mashirro</groupId><artifactId>provider-api</artifactId><version>1.0-SNAPSHOT</version></dependency><!-- SpringBoot Web容器 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- dubbo--><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo</artifactId><version>3.0.5</version></dependency><!-- Curator 是 Netflix 开源的一个 Zookeeper 客户端实现。 --><dependency><groupId>org.apache.curator</groupId><artifactId>curator-x-discovery</artifactId><version>5.2.0</version></dependency>
2.provider-api定义服务接口
//定义服务接口
public interface DemoService {String sayHello();
}
3.在provider服务提供方实现接口
//在服务提供方实现接口
@Service("demoService") //和本地bean一样实现服务
public class DemoServiceImpl implements DemoService {@Value("${spring.application.name}")private String applicationName;@Overridepublic String sayHello() {return String.format("Hello com From %s", applicationName);}
}
4.在provider服务提供方用 Spring 配置声明暴露服务,dubbo-provider.xml
<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"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"><!-- 提供方应用信息,用于计算依赖关系 --><dubbo:application name="provider"/><!-- 使用zookeeper注册中心暴露服务地址 --><dubbo:registry address="zookeeper://${dubbo.registry.address}"/><!-- 用dubbo协议在20880端口暴露服务 --><dubbo:protocol name="dubbo" port="${dubbo.registry.port}"/><!-- 声明需要暴露的服务接口 --><dubbo:service interface="org.provider.api.DemoService" ref="demoService"/>
</beans>
5.在consumer消费方通过 Spring 配置引用远程服务,dubbo-consumer.xml
<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"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"><!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 --><dubbo:application name="consumer"/><!-- 使用zookeeper注册中心暴露发现服务地址 --><dubbo:registry address="zookeeper://${dubbo.registry.address}"/><!-- 生成远程服务代理,可以和本地bean一样使用demoService --><dubbo:reference id="demoService" interface="org.provider.api.DemoService"/>
</beans>
6.在provider服务提供方和consumer消费方的启动类上添加注解来分别加载各自的dubbo配置文件
/*** Dubbo采用全Spring配置方式,透明化接入应用,对应用没有任何API侵入,* 只需用Spring加载Dubbo的配置即可,Dubbo基于Spring的Schema扩展进行加载。*/
@SpringBootApplication
@ImportResource(locations = {"classpath:dubbo-consumer.xml"})
public class ConsumerApplication {public static void main(String[] args) {SpringApplication.run(ConsumerApplication.class, args);}
}
3.3 Dubbo 依赖基本介绍
3.3.1 缺省依赖
3.3.2 可选依赖
3.4 Dubbo 的 XML Schema 配置参考手册
1.所有配置项分为三大类:
- 服务发现:表示该配置项用于服务的注册与发现,目的是让消费方找到提供方。
- 服务治理:表示该配置项用于治理服务间的关系,或为开发测试提供便利条件。
- 性能调优:表示该配置项用于调优性能,不同的选项对性能会产生影响。
- 所有配置最终都将转换为 URL 表示,并由服务提供方生成,经注册中心传递给消费方,各属性对应 URL 的参数。URL 格式:protocol://username:password@host:port/path?key=value&key=value
2.dubbo:application应用信息配置。
- name属性:必填。当前应用名称,用于注册中心计算应用间依赖关系,注意:消费者和提供者应用名不要一样,此参数不是匹配条件,你当前项目叫什么名字就填什么,和提供者消费者角色无关。
- version:可选。当前应用的版本
- logger:可选,缺省值slf4j。日志输出方式,可选:slf4j、jcl、log4j、log4j2、jdk。
- compiler:可选,缺省值javassist。Java字节码编译器,用于动态类的生成,可选:jdk或javassist。
3.dubbo:registry注册中心配置。同时如果有多个不同的注册中心可以声明多个dubbo:registry标签。
- id属性:可选。注册中心引用BeanId,可以在< dubbo:service registry=“”>或< dubbo:reference registry=“”>中引用此ID。
- address属性:必填。注册中心服务器地址,如果地址没有端口缺省为9090,同一集群内的多个地址用逗号分隔,如:ip:port,ip:port,不同集群的注册中心,请配置多个< dubbo:registry>标签
- protocol属性:可选,缺省值dubbo。注册中心地址协议,支持dubbo、multicast、zookeeper、redis、consul(2.7.1)、sofa(2.7.2)、etcd(2.7.2)、nacos(2.7.2)等协议
- port属性:可选,缺省值9090。注册中心缺省端口,当address没有带端口时使用此端口做为缺省值
- username属性:可选。登录注册中心用户名,如果注册中心不需要验证可不填
- password属性:可选。登录注册中心密码,如果注册中心不需要验证可不填
- transport属性:可选,缺省值netty。网络传输方式,可选mina、netty
- timeout属性:可选,缺省值5000。注册中心请求超时时间(毫秒)
- sessiont属性:可选,缺省值60000。注册中心会话超时时间(毫秒),用于检测提供者非正常断线后的脏数据,比如用心跳检测的实现,此时间就是心跳间隔,不同注册中心实现不一样
- file:可选。使用文件缓存注册中心地址列表及服务提供者列表,应用重启时将基于此文件恢复,注意:两个注册中心不能使用同一文件存储
- wait:可选,缺省值0。停止时等待通知完成时间(毫秒)
- check属性:可选,缺省值true。注册中心不存在时,是否报错
- register属性:可选,缺省值true。是否向此注册中心注册服务,如果设为false,将只订阅,不注册
- subscribe属性:可选,缺省值true。是否向此注册中心订阅服务,如果设为false,将只注册,不订阅
- dynamic属性:可选,缺省值true。服务是否动态注册,如果设为false,注册后将显示为disable状态,需人工启用,并且服务提供者停止时,也不会自动取消注册,需人工禁用。
- group属性:可选,缺省值dubbo。服务注册分组,跨组的服务不会相互影响,也无法相互调用,适用于环境隔离。
4.dubbo:consumer服务消费者缺省值配置。同时该标签为 dubbo:reference 标签的缺省值设置。
- timeout属性:可选,缺省值1000。远程服务调用超时时间(毫秒)。
- retries属性:可选,缺省值2。远程服务调用重试次数,不包括第一次调用,不需要重试请设为0,仅在cluster为failback/failover时有效。
- cluster:可选,缺省值failover。集群方式,可选:failover/failfast/failsafe/failback/forking
- loadbalance属性:可选,缺省值random。负载均衡策略,可选值:random,roundrobin,leastactive,分别表示:随机,轮询,最少活跃调用属性
- async:可选,缺省值false。是否缺省异步执行,不可靠异步,只是忽略返回值,不阻塞执行线程
- connections:可选,缺省值100。每个服务对每个提供者的最大连接数,rmi、http、hessian等短连接协议支持此配置,dubbo协议长连接不支持此配置
- generic:可选,缺省值false。是否缺省泛化接口,如果为泛化接口,将返回GenericService
- registry属性:可选,缺省向所有registry注册。向指定注册中心注册,在多个注册中心时使用,值为dubbo:registry的id属性,多个注册中心ID用逗号分隔,如果不想将该服务注册到任何registry,可将值设为N/A
- check属性:可选,缺省值true。启动时检查提供者是否存在,true报错,false忽略
- proxy属性:可选,缺省值javassist。生成动态代理方式,可选:jdk/javassist
- actives属性:可选,缺省值0。每服务消费者每服务每方法最大并发调用数
- filter属性:可选。服务消费方远程调用过程拦截器名称,多个名称用逗号分隔
- listener属性:可选。服务消费方引用服务监听器名称,多个名称用逗号分隔
- init属性:可选,缺省值false。是否在afterPropertiesSet()时饥饿初始化引用,否则等到有人注入或引用该实例时再初始化。
- cache属性:可选。以调用参数为key,缓存返回结果,可选:lru, threadlocal, jcache等
- validation:可选。是否启用JSR303标准注解验证,如果启用,将对方法参数上的注解进行校验
5.dubbo:reference服务消费者引用服务配置。其余同dubbo:consumer
- id属性:必填。服务引用BeanId
- interface属性:必填。服务接口名
- version属性:可选。服务版本,与服务提供者的版本一致
- group属性:可选。服务分组,当一个接口有多个实现,可以用分组区分,必需和服务提供方一致
- url:可选。点对点直连服务提供者地址,将绕过注册中心
- stub:可选。服务接口客户端本地代理类名,用于在客户端执行本地逻辑,如本地缓存等,该本地代理类的构造函数必须允许传入远程代理对象,构造函数如:public XxxServiceLocal(XxxService xxxService)
- mock:可选。服务接口调用失败Mock实现类名,该Mock类必须有一个无参构造函数,与Local的区别在于,Local总是被执行,而Mock只在出现非业务异常(比如超时,网络异常等)时执行,Local在远程调用之前执行,Mock在远程调用后执行。
- protocol:可选。只调用指定协议的服务提供方,其它协议忽略。
6.dubbo:provider服务提供者缺省值配置。同时该标签为 dubbo:service 和 dubbo:protocol 标签的缺省值设置。
- id属性:可选,缺省值dubbo。协议BeanId,可以在< dubbo:service proivder=“”>中引用此ID
- protocol属性:可选,缺省值dubbo。协议名称
- host属性:可选。服务主机名,多网卡选择或指定VIP及域名时使用,为空则自动查找本机IP,建议不要配置,让Dubbo自动获取本机IP
- threads属性:可选,缺省值200。服务线程池大小(固定大小)
- payload属性:可选,缺省值8388608(=8M)。请求及响应数据包大小限制,单位:字节
- path属性:可选。提供者上下文路径,为服务path的前缀
- server属性:可选,dubbo协议缺省为netty,http协议缺省为servlet。协议的服务器端实现类型,比如:dubbo协议的mina,netty等,http协议的jetty,servlet等
- client属性:可选,dubbo协议缺省为netty。协议的客户端实现类型,比如:dubbo协议的mina,netty等
- codec属性:可选,缺省值dubbo。协议编码方式
- serialization属性:可选,dubbo协议缺省为hessian2,rmi协议缺省为java,http协议缺省为json。协议序列化方式,当协议支持多种序列化方式时使用,
- default属性:可选,缺省值false。是否为缺省协议,用于多协议
- filter属性:可选。服务提供方远程调用过程拦截器名称,多个名称用逗号分隔
- listener属性:可选。服务提供方导出服务监听器名称,多个名称用逗号分隔
- threadpool属性:可选,缺省值fixed。线程池类型,可选:fixed/cached/limit(2.5.3以上)/eager(2.6.x以上)
- accepts属性:可选,缺省值0。服务提供者最大可接受连接数
- version属性:可选。服务版本,建议使用两位数字版本,如:1.0,通常在接口不兼容时版本号才需要升级
- group属性:可选。服务分组,当一个接口有多个实现,可以用分组区分
- delay:可选,缺省值0。延迟注册服务时间(毫秒)- ,设为-1时,表示延迟到Spring容器初始化完成时暴露服务
- timeout属性:可选,缺省值1000。远程服务调用超时时间(毫秒)
- retries属性:可选,缺省值2。远程服务调用重试次数,不包括第一次调用,不需要重试请设为0
- connections属性:可选,缺省值0。对每个提供者的最大连接数,rmi、http、hessian等短连接协议表示限制连接数,dubbo等长连接协表示建立的长连接个数
- loadbalance属性:可选,缺省值random。负载均衡策略,可选值:random,roundrobin,leastactive,分别表示:随机,轮询,最少活跃调用属性
- async属性:可选,缺省值false。是否缺省异步执行,不可靠异步,只是忽略返回值,不阻塞执行线程
- stub属性:可选,缺省值false。设为true,表示使用缺省代理类名,即:接口名 + Local后缀。
- mock属性:可选,缺省值false。设为true,表示使用缺省Mock类名,即:接口名 + Mock后缀。
- token属性:可选,缺省值false。令牌验证,为空表示不开启,如果为true,表示随机生成动态令牌
- registry属性:可选,缺省向所有registry注册。向指定注册中心注册,在多个注册中心时使用,值为dubbo:registry的id属性,多个注册中心ID用逗号分隔,如果不想将该服务注册到任何registry,可将值设为N/A
- dynamic属性:可选,缺省值true。服务是否动态注册,如果设为false,注册后将显示后disable状态,需人工启用,并且服务提供者停止时,也不会自动取消册,需人工禁用。
- accesslog属性:可选,缺省值false。设为true,将向logger中输出访问日志,也可填写访问日志文件路径,直接把访问日志输出到指定文件
- weight属性:可选。服务权重
- executes属性:可选,缺省值0。服务提供者每服务每方法最大可并行执行请求数
- actives属性:可选,缺省值0。每服务消费者每服务每方法最大并发调用数
- proxy属性:可选,缺省值javassist。生成动态代理方式,可选:jdk/javassist
- cluster属性:可选,缺省值failover。集群方式,可选:failover/failfast/failsafe/failback/forking
- deprecated属性:可选,缺省值false。服务是否过时,如果设为true,消费方引用时将打印服务过时警告error日志
- queues属性:可选,缺省值0。线程池队列大小,当线程池满时,排队等待执行的队列大小,建议不要设置,当线程池满时应立即失败,重试其它服务提供机器,而不是排队,除非有特殊需求。
- charset属性:可选,缺省值UTF-8。序列化编码
- buffer属性:可选,缺省值8192。网络读写缓冲区大小
- iothreads属性:可选,缺省值 CPU + 1。IO线程池,接收网络读写中断,以及序列化和反序列化,不处理业务,业务线程池参见threads配置,此线程池和CPU相关,不建议配置。
- telnet属性:可选。所支持的telnet命令,多个命令用逗号分隔
7.dubbo:protocol服务提供者协议配置。如果需要支持多协议,可以声明多个 dubbo:protocol 标签,并在 dubbo:service 中通过 protocol 属性指定使用的协议。其余同dubbo:provider。
- id属性:可选,缺省值dubbo。协议BeanId,可以在< dubbo:service protocol=“”>中引用此ID,如果ID不填,缺省和name属性值一样,重复则在name后加序号。
- name属性:必填,缺省值dubbo。协议名称
- port属性:可选,dubbo协议缺省端口为20880;如果没有配置port,则自动采用默认端口,如果配置为-1,则会分配一个没有被占用的端口。服务端口
- transporter属性:可选,dubbo协议缺省为netty。协议的服务端和客户端实现类型
- dispatcher属性:可选,dubbo协议缺省为all。协议的消息派发方式,用于指定线程模型,比如:dubbo协议的all, direct, message, execution, connection等
- heartbeat属性:可选,缺省值0。心跳间隔,对于长连接,当物理层断开时,比如拔网线,TCP的FIN消息来不及发送,对方收不到断开事件,此时需要心跳来帮助检查连接是否已断开
8.dubbo:service服务提供者暴露服务配置。其余同dubbo:provider。
- interface:必填。服务接口名
- ref:必填。服务对象实现引用
- version属性:可选。服务版本,建议使用两位数字版本,如:1.0,通常在接口不兼容时版本号才需要升级
- group属性:可选。服务分组,当一个接口有多个实现,可以用分组区分
9.dubbo:method方法级配置。同时该标签为 < dubbo:service> 或 < dubbo:reference> 的子标签,用于控制到方法级。
- name属性:必填。方法名
- sent属性:可选,缺省值true。异步调用时,标记sent=true时,表示网络已发出数据
- return属性:可选,缺省值true。方法调用是否需要返回值,async设置为true时才生效,如果设置为true,则返回future,或回调onreturn等方法,如果设置为false,则请求发送成功后直接返回Null
- oninvoke属性:可选。方法执行前拦截
- onreturn属性:可选。方法执行返回后拦截
- onthrow属性:可选。方法执行有异常拦截
3.5 Dubbo 注册中心参考手册
Dubbo 协议参考手册如下
3.5.1 dubbo 协议
1.Dubbo 缺省协议采用单一长连接和 NIO 异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。反之,Dubbo 缺省协议不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。
2.特性和约束
3.5.2 其余协议请参看官方文档
协议参考手册
3.6 Dubbo 注册中心参考手册
具体请参看Dubbo 注册中心参考手册
3.6.1 Zookeeper 注册中心参考手册
1.Zookeeper 是 Apache Hadoop 的子项目,是一个树型的目录服务,支持变更推送,适合作为 Dubbo 服务的注册中心,工业强度较高,可用于生产环境,并推荐使用。
2.流程说明:
- 服务提供者启动时:向 /dubbo/com.foo.BarService/providers 目录下写入自己的 URL 地址。
- 服务消费者启动时:订阅 /dubbo/com.foo.BarService/providers 目录下的提供者 URL 地址。并向 /dubbo/com.foo.BarService/consumers 目录下写入自己的 URL 地址。
- 监控中心启动时:订阅 /dubbo/com.foo.BarService 目录下的所有提供者和消费者 URL 地址。
3.支持以下功能:
- 当提供者出现断电等异常停机时,注册中心能自动删除提供者信息
- 当注册中心重启时,能自动恢复注册数据,以及订阅请求
- 当会话过期时,能自动恢复注册数据,以及订阅请求
- 当设置 <dubbo:registry check=“false” /> 时,记录失败注册和订阅请求,后台定时重试
- 可通过 <dubbo:registry username=“admin” password=“1234” /> 设置 zookeeper 登录信息
- 可通过 <dubbo:registry group=“dubbo” /> 设置 zookeeper 的根节点,不配置将使用默认的根节点。
- 支持 * 号通配符 <dubbo:reference group=“* " version=”* " />,可订阅服务的所有分组和所有版本的提供者
4.如何使用
Dubbo 支持 zkclient 和 curator 两种 Zookeeper 客户端实现。注意:在2.7.x的版本中已经移除了zkclient的实现,如果要使用zkclient客户端,需要自行拓展。我这里我们介绍使用 curator 客户端。
1. Curator 是 Netflix 开源的一个 Zookeeper 客户端实现。
<dubbo:registry ... client="curator" />
或者
dubbo.registry.client=curator
或者
zookeeper://10.20.153.10:2181?client=curator
3. 需依赖curator-framework, curator-recipes:
<dependency><groupId>org.apache.curator</groupId><artifactId>curator-framework</artifactId><version>${zookeeper.version}</version>
</dependency>
<dependency><groupId>org.apache.curator</groupId><artifactId>curator-recipes</artifactId><version>${zookeeper.version}</version>
</dependency>
5.Zookeeper 单机配置
<dubbo:registry address="zookeeper://10.20.153.10:2181" />
或
<dubbo:registry protocol="zookeeper" address="10.20.153.10:2181" />
6.Zookeeper 集群配置
<dubbo:registry address="zookeeper://10.20.153.10:2181?backup=10.20.153.11:2181,10.20.153.12:2181" />
或
<dubbo:registry protocol="zookeeper" address="10.20.153.10:2181,10.20.153.11:2181,10.20.153.12:2181" />
7.同一 Zookeeper,分成多组注册中心:
<dubbo:registry id="chinaRegistry" address="zookeeper://10.20.153.10:2181" group="china" />
<dubbo:registry id="intlRegistry" address="zookeeper://10.20.153.10:2181" group="intl" />
3.6.2 Nacos 注册中心参考手册
待补充…
四.Eclipse与IntelliJ IDEA比较
五 不同粒度配置的覆盖关系
以 timeout 为例,下图显示了配置的查找顺序,其它 retries,loadbalance,actives 等类似:
- 方法级优先,接口级次之,全局配置再次之。
- 如果级别一样,则消费方优先,提供方次之。
六 动态配置中心
详细内容请参看官方文档
1.配置中心(v2.7.0)在 Dubbo 中承担两个职责:
- 外部化配置。启动配置的集中式存储 (简单理解为 dubbo.properties 的外部化存储)。
- 服务治理。服务治理规则的存储与通知。
2.启用动态配置,以 Zookeeper 为例
<dubbo:config-center address="zookeeper://127.0.0.1:2181"/>
或者
dubbo.config-center.address=zookeeper://127.0.0.1:2181
或者
ConfigCenterConfig configCenter = new ConfigCenterConfig();
configCenter.setAddress("zookeeper://127.0.0.1:2181");
6.1 外部化配置
1.外部化配置目的之一是实现配置的集中式管理,这部分业界已经有很多成熟的专业配置系统如 Apollo, Nacos 等,Dubbo 所做的主要是保证能配合这些系统正常工作。
2.外部化配置和其他本地配置在内容和格式上并无区别,可以简单理解为 dubbo.properties 的外部化存储,配置中心更适合将一些公共配置如注册中心、元数据中心配置等抽取以便做集中管理。
- 优先级:外部化配置默认较本地配置有更高的优先级,因此这里配置的内容会覆盖本地配置值
- 作用域:外部化配置有全局和应用两个级别,全局配置是所有应用共享的,应用级配置是由每个应用自己维护且只对自身可见的。当前已支持的扩展实现有Zookeeper、Apollo。
6.1.1 Zookeeper
6.1.2 Apollo
这里不详细介绍
6.1.3 自己加载外部化配置
6.2 服务治理
6.2.1 Zookeeper
6.2.2 Apollo
请参看官方文档
七 属性配置
1.如果你的应用足够简单,例如,不需要多注册中心或多协议,并且需要在spring容器中共享配置,那么,我们可以直接使用 dubbo.properties 作为默认配置。
2.Dubbo 可以自动加载 classpath 根目录下的 dubbo.properties
dubbo.application.name=foo
dubbo.application.owner=bar
dubbo.registry.address=10.20.153.10:9090
八 API配置
以API 配置的方式来配置你的 Dubbo 应用,请参看官方文档
九 注解配置
以注解配置的方式来配置你的 Dubbo 应用,点此查看完整示例
十 配置加载流程
1.此篇文档主要讲在应用启动阶段,Dubbo框架如何将所需要的配置采集起来(包括应用配置、注册中心配置、服务配置等),以完成服务的暴露和引用流程。
2.默认有四种配置来源:
- JVM System Properties,-D 参数
- Externalized Configuration,外部化配置
- ServiceConfig、ReferenceConfig 等编程接口采集的配置
- 本地配置文件 dubbo.properties
3.覆盖关系
十一 用法手册
详细内容请参看官方文档
11.1 在启动时检查依赖的服务是否可用
Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题,默认 check=“true”。
11.2 配置dubbo服务重试次数
Dubbo 服务在尝试调用一次之后,如出现非业务异常(服务突然不可用、超时等),Dubbo 默认会进行额外的最多2次重试.
11.3 集群容错
11.3.1 集群容错模式Failfast Cluster
- Failover Cluster:缺省配置。失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过 retries=“2” 来设置重试次数(不含第一次)。
- Failfast Cluster:快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。
- Failsafe Cluster:失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。
- Failback Cluster:失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
11.4 负载均衡
在集群负载均衡时,Dubbo 提供了多种均衡策略,缺省为 random 随机调用。
11.5 配置 Dubbo 中的线程模型
11.6 直连提供者
1.在开发及测试环境下,经常需要绕过注册中心,只测试指定服务提供者,这时候可能需要点对点直连,点对点直连方式,将以服务接口为单位,忽略注册中心的提供者列表,A 接口配置点对点,不影响 B 接口从注册中心获取列表。
<dubbo:reference id="xxxService" interface="com.alibaba.xxx.XxxService" url="dubbo://localhost:20890" />
11.7 只订阅不注册
1.为方便开发测试,经常会在线下共用一个所有服务可用的注册中心,这时,如果一个正在开发中的服务提供者注册,可能会影响消费者不能正常运行。
2.可以让服务提供者开发方,只订阅服务(开发的服务可能依赖其它服务),而不注册正在开发的服务,通过直连测试正在开发的服务。
<dubbo:registry address="10.20.153.10:9090" register="false" />
11.8 在 Dubbo 中配置多协议
不同服务在性能上适用不同协议进行传输,比如大数据用短连接协议,小数据大并发用长连接协议
<!-- 多协议配置 --><dubbo:protocol name="dubbo" port="20880" /><dubbo:protocol name="rmi" port="1099" /><!-- 使用dubbo协议暴露服务 --><dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService" protocol="dubbo" /><!-- 使用rmi协议暴露服务 --><dubbo:service interface="com.alibaba.hello.api.DemoService" version="1.0.0" ref="demoService" protocol="rmi" />
11.9 多注册中心(在 Dubbo 中把同一个服务注册到多个注册中心上)
Dubbo 支持同一服务向多注册中心同时注册,或者不同服务分别注册到不同的注册中心上去,甚至可以同时引用注册在不同注册中心上的同名服务。
11.9.1 多注册中心注册
<!-- 多注册中心配置 --><dubbo:registry id="hangzhouRegistry" address="10.20.141.150:9090" /><dubbo:registry id="qingdaoRegistry" address="10.20.141.151:9010" default="false" /><!-- 向多个注册中心注册 --><dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService" registry="hangzhouRegistry,qingdaoRegistry" />
11.9.2 不同服务使用不同注册中心
<!-- 多注册中心配置 --><dubbo:registry id="chinaRegistry" address="10.20.141.150:9090" /><dubbo:registry id="intlRegistry" address="10.20.154.177:9010" default="false" /><!-- 向中文站注册中心注册 --><dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService" registry="chinaRegistry" /><!-- 向国际站注册中心注册 --><dubbo:service interface="com.alibaba.hello.api.DemoService" version="1.0.0" ref="demoService" registry="intlRegistry" />
11.9.3 多注册中心引用
<!-- 多注册中心配置 --><dubbo:registry id="chinaRegistry" address="10.20.141.150:9090" /><dubbo:registry id="intlRegistry" address="10.20.154.177:9010" default="false" /><!-- 引用中文站服务 --><dubbo:reference id="chinaHelloService" interface="com.alibaba.hello.api.HelloService" version="1.0.0" registry="chinaRegistry" /><!-- 引用国际站站服务 --><dubbo:reference id="intlHelloService" interface="com.alibaba.hello.api.HelloService" version="1.0.0" registry="intlRegistry" />
11.10 服务分组
当一个接口有多种实现时,可以用 group 区分。
服务
dubbo:service group="feedback" interface="com.xxx.IndexService" />
<dubbo:service group="member" interface="com.xxx.IndexService" />
引用
<dubbo:reference id="feedbackIndexService" group="feedback" interface="com.xxx.IndexService" />
<dubbo:reference id="memberIndexService" group="member" interface="com.xxx.IndexService" />
11.11 多版本(在 Dubbo 中为同一个服务配置多个版本)
11.12 分组聚合
通过分组对结果进行聚合并返回聚合后的结果,比如菜单服务,用group区分同一接口的多种实现,现在消费方需从每种group中调用一次并返回结果,对结果进行合并之后返回,这样就可以实现聚合菜单项。
11.13 参数验证(在 Dubbo 中进行参数验证)
1.参数验证功能是基于 JSR303 实现的,用户只需标识 JSR303 标准的验证 annotation,并通过声明 filter 来实现验证。
<dependency><groupId>javax.validation</groupId><artifactId>validation-api</artifactId><version>1.0.0.GA</version>
</dependency>
<dependency><groupId>org.hibernate</groupId><artifactId>hibernate-validator</artifactId><version>4.2.0.Final</version>
</dependency>
11.14 指定Ip Port调用Provider
1.当多个Provider注册到注册中心时,可以通过在RpcContext中动态的指定其中一个实例的Ip,Port进行Dubbo调用。
2.用法示例:
(1)假定提供2个provider注册于注册中心,分别为10.220.47.253:20880;10.220.47.253:20881;
// 10.220.47.253:20880
@Service(interfaceClass = TestService.class)
public class TestServiceImpl implements TestService {@Overridepublic String sayHello(String name) {return "Hello "+name+" i am provider1";}
}// 10.220.47.253:20881
@Service(interfaceClass = TestService.class)
public class TestServiceImpl implements TestService {@Overridepublic String sayHello(String name) {return "Hello "+name+" i am provider2";}
}
(2)@DubboReference引入provider,其中设定parameters = {“router”,“address”},指定address路由方式。对于要调用的实例,指定Ip,Port构造Address对象,并设置RpcContext键为"address",value为该对象。
@RestController
public class TestServiceConsumer {@DubboReference(interfaceClass = TestService.class,group = "dev",parameters = {"router","address"})private TestService testService;@GetMapping("/invokeByIpPortSpecified")public String invokeByIp(){try {// 根据provider的ip,port创建Address实例Address address = new Address("10.220.47.253", 20880);RpcContext.getContext().setObjectAttachment("address", address);return testService.sayHello("Tom");}catch (Throwable ex){return ex.getMessage();}}
}
11.15 收集Dubbo广播响应
1.适用场景:对于一个dubbo消费者,广播调用多个dubbo 提供者,该消费者可以收集所有服务提供者的响应结果。
2.使用示例:
(1)@Reference引入服务提供者,其中,令cluster=“broadcast2”,代表进行一个收集响应结果的广播调用。广播调用所有服务提供者,逐个调用,并且可以完整的返回所有服务提供者的执行结果(正确或异常),并将所有服务提供者的响应结果存于RpcContext。
@RestController
public class TestServiceConsumer {@Reference(interfaceClass = DubboHealthService.class,cluster = "broadcast2")private DubboHealthService dubboHealthService;@GetMapping("/health")public String broadCast(){try{dubboHealthService.health();}catch (Exception e){Map<String, String> m = RpcContext.getServerContext().getAttachments();return m.toString()+"|"+"fail";}Map<String, String> m = RpcContext.getServerContext().getAttachments();return m.toString()+"|"+"success";}
}
(2)provider demo
@Service
public class DubboHealthServiceImpl implements DubboHealthService {@Overridepublic String health() {
// int i = 1/0;return "i am provider2";}
}
(3)执行结果
所有provider全部成功:
>curl http://localhost:8081/health
>{broadcast.results=[{"ip":"10.220.47.253","port":20880,"data":"i am provider1"},{"ip":"10.220.47.253","port":20881,"data":"i am provider2"}]}|success%
令其中一个provider执行除以零:
>curl http://localhost:8081/health
>{broadcast.results=[{"ip":"10.220.47.253","port":20880,"data":"i am provider1"},{"ip":"10.220.47.253","port":20881,"exceptionMsg":"/ by zero"}]}|success%
11.16 结果缓存
<dubbo:reference interface="com.foo.BarService"><dubbo:method name="findBar" cache="lru" />
</dubbo:reference>
11.17 使用泛化调用
泛化接口调用方式主要用于客户端没有 API 接口及模型类元的情况,参数及返回值中的所有 POJO 均用 Map 表示,通常用于框架集成,比如:实现一个通用的服务测试框架,可通过 GenericService 调用所有服务实现。
11.17.1 通过 Spring 使用泛化调用
1.在 Spring 配置申明 generic=“true”:
<dubbo:reference id="barService" interface="com.foo.BarService" generic="true" />
2.在 Java 代码获取 barService 并开始泛化调用:
GenericService barService = (GenericService) applicationContext.getBean("barService");
Object result = barService.$invoke("sayHello", new String[] { "java.lang.String" }, new Object[] { "World" });
11.17.2 通过 API 方式使用泛化调用
import org.apache.dubbo.rpc.service.GenericService;
... // 引用远程服务
// 该实例很重量,里面封装了所有与注册中心及服务提供方连接,请缓存
ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();
// 弱类型接口名
reference.setInterface("com.xxx.XxxService");
reference.setVersion("1.0.0");
// 声明为泛化接口
reference.setGeneric(true); // 用org.apache.dubbo.rpc.service.GenericService可以替代所有接口引用
GenericService genericService = reference.get(); // 基本类型以及Date,List,Map等不需要转换,直接调用
Object result = genericService.$invoke("sayHello", new String[] {"java.lang.String"}, new Object[] {"world"}); // 用Map表示POJO参数,如果返回值为POJO也将自动转成Map
Map<String, Object> person = new HashMap<String, Object>();
person.put("name", "xxx");
person.put("password", "yyy");
// 如果返回POJO将自动转成Map
Object result = genericService.$invoke("findPerson", new String[]
{"com.xxx.Person"}, new Object[]{person}); ...
11.18 实现泛化调用
泛接口实现方式主要用于服务器端没有 API 接口及模型类元的情况,参数及返回值中的所有 POJO 均用 Map 表示,通常用于框架集成,比如:实现一个通用的远程服务 Mock 框架,可通过实现 GenericService 接口处理所有服务请求。
在 Java 代码中实现 GenericService 接口:
package com.foo;
public class MyGenericService implements GenericService {public Object $invoke(String methodName, String[] parameterTypes, Object[] args) throws GenericException {if ("sayHello".equals(methodName)) {return "Welcome " + args[0];}}
}
11.18.1 通过Spring 暴露泛化实现
<bean id="genericService" class="com.foo.MyGenericService" />
<dubbo:service interface="com.foo.BarService" ref="genericService" />
11.18.2 通过 API 方式暴露泛化实现
...
// 用org.apache.dubbo.rpc.service.GenericService可以替代所有接口实现
GenericService xxxService = new XxxGenericService(); // 该实例很重量,里面封装了所有与注册中心及服务提供方连接,请缓存
ServiceConfig<GenericService> service = new ServiceConfig<GenericService>();
// 弱类型接口名
service.setInterface("com.xxx.XxxService");
service.setVersion("1.0.0");
// 指向一个通用服务实现
service.setRef(xxxService); // 暴露及注册服务
service.export();
11.19 上下文信息
1.上下文中存放的是当前调用过程中所需的环境信息。所有配置信息都将转换为 URL 的参数
2.RpcContext 是一个 ThreadLocal 的临时状态记录器,当接收到 RPC 请求,或发起 RPC 请求时,RpcContext 的状态都会变化。比如:A 调 B,B 再调 C,则 B 机器上,在 B 调 C 之前,RpcContext 记录的是 A 调 B 的信息,在 B 调 C 之后,RpcContext 记录的是 B 调 C 的信息。
11.20 Dubbo 服务提供方的异步执行
1.Provider端异步执行将阻塞的业务从Dubbo内部线程池切换到业务自定义线程,避免Dubbo线程池的过度占用,有助于避免不同服务间的互相影响。异步执行无益于节省资源或提升RPC响应性能,因为如果业务执行需要阻塞,则始终还是要有线程来负责执行。
11.20.1 定义 CompletableFuture 签名的接口
1.服务接口定义:
public interface AsyncService {CompletableFuture<String> sayHello(String name);
}
2.服务实现:
public class AsyncServiceImpl implements AsyncService {@Overridepublic CompletableFuture<String> sayHello(String name) {RpcContext savedContext = RpcContext.getContext();// 建议为supplyAsync提供自定义线程池,避免使用JDK公用线程池// 通过 return CompletableFuture.supplyAsync() ,业务执行已从 Dubbo 线程切换到业务线程,避免了对 Dubbo 线程池的阻塞。return CompletableFuture.supplyAsync(() -> {System.out.println(savedContext.getAttachment("consumer-key1"));try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}return "async response from provider.";});}
}
11.21 在 Dubbo 中发起异步调用
1.从 2.7.0 开始,Dubbo 的所有异步编程接口开始以 CompletableFuture 为基础
2.基于 NIO 的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服务,相对多线程开销较小。
11.21.1 使用 CompletableFuture 签名的接口
1.需要服务提供者事先定义 CompletableFuture 签名的服务
2.XML引用服务:
<dubbo:reference id="asyncService" timeout="10000" interface="com.alibaba.dubbo.samples.async.api.AsyncService"/>
3.调用远程服务:
// 调用直接返回CompletableFuture
CompletableFuture<String> future = asyncService.sayHello("async call request");
// 增加回调
future.whenComplete((v, t) -> {if (t != null) {t.printStackTrace();} else {System.out.println("Response: " + v);}
});
// 早于结果输出
System.out.println("Executed before response return.");
11.22 参数回调(通过参数回调从服务器端调用客户端逻辑)
参数回调方式与调用本地 callback 或 listener 相同,只需要在 Spring 的配置文件中声明哪个参数是 callback 类型即可。Dubbo 将基于长连接生成反向代理,这样就可以从服务器端调用客户端逻辑。
11.23 事件通知(在调用之前、调用之后、出现异常时的事件通知)
在调用之前、调用之后、出现异常时,会触发 oninvoke、onreturn、onthrow 三个事件,可以配置当事件发生时,通知哪个类的哪个方法。
11.24 本地伪装(如何在 Dubbo 中利用本地伪装实现服务降级)
1.本地伪装通常用于服务降级,比如某验权服务,当服务提供方全部挂掉后,客户端不抛出异常,而是通过 Mock 数据返回授权失败。
2.在 spring 配置文件中按以下方式配置:
<dubbo:reference interface="com.foo.BarService" mock="com.foo.BarServiceMock" />
3.在工程中提供 Mock 实现 :
package com.foo;
public class BarServiceMock implements BarService {public String sayHello(String name) {// 你可以伪造容错数据,此方法只在出现RpcException时被执行return "容错数据";}
}
11.25 优雅停机(让 Dubbo 服务完成优雅停机)
1.Dubbo 是通过 JDK 的 ShutdownHook 来完成优雅停机的,所以如果用户使用 kill -9 PID 等强制关闭指令,是不会执行优雅停机的,只有通过 kill PID 时,才会执行。