目录
一、dubbo简介
1.1 dubbo是什么
dubbo是一款高性能、轻量级的开源Java RPC框架
1.2 dubbo主要功能
-
面向接口的远程方法调用
-
智能容错和负载均衡
-
以及服务自动注册和发现
1.3 dubbo结构
角色:注册中心,服务提供者,服务消费者,dubbo框架容器,监控中心
dubbo启动流程:
0)dubbo容器启动,加载、运行服务提供者
1)服务提供者启动时,向注册中心注册自己的服务
2)服务消费者启动时,从注册中心订阅自己所需的服务
3)注册中心返回服务提供者的地址列表给消费者,当服务提供者发生变化时;注册中心将基于长连接推送变更数据给消费者
4)服务消费者基于软负载均衡策略执行服务调用
5)服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心
1.4 dubbo发展历程
独家专访阿里高级技术专家北纬:Dubbo开源重启半年来的快意江湖
二、dubbo环境搭建&快速启动
2.1 注册中心(zookeeper)安装
wget http://mirrors.cnnic.cn/apache/zookeeper/zookeeper-3.4.14/zookeeper-3.4.14.tar.gz #嫌慢的化可以直接下载附件
tar -zxvf zookeeper-3.4.14.tar.gz
cd zookeeper-3.4.14
cp conf/zoo_sample.cfg conf/zoo.cfg #解压包里面只有zoo_sample.cfg,而没有zoo.cfg,zookeeper的启动依赖zoo.cfg
#按照自己需求配置zookeeper启动属性
bash zkServer.sh start #默认启动端口为2181
zookeeper-3.4.14.tar.gz
注册中心 | 成熟度 | 优点 | 缺点 | 建议 |
Zookeeper注册中心 | Stable | 支持基于网络的集群方式,有广泛周边开源产品,建议使用dubbo-2.3.3以上版本(推荐使用) | 依赖于Zookeeper的稳定性 | 可用于生产环境 |
Redis注册中心 | Stable | 支持基于客户端双写的集群方式,性能高 | 要求服务器时间同步,用于检查心跳过期脏数据 | 可用于生产环境 |
Multicast注册中心 | Tested | 去中心化,不需要安装注册中心 | 依赖于网络拓扑和路由,跨机房有风险 | 小规模应用或开发测试环境 |
Simple注册中心 | Tested | Dogfooding,注册中心本身也是一个标准的RPC服务 | 没有集群支持,可能单点故障 | 试用 |
2.2 dubbo管理控制台安装
SpringBoot + dubbo-admin-server + dubbo-admin-ui:(可选,不影响使用。安装管理控制台,可以可视化的管理服务)
-
下载管理控制台 https://github.com/apache/incubator-dubbo-admin (springboot项目)
-
启动dubbo-admin-server(后面需要在本地启动服务提供中和服务消费者,为了防止端口冲突,此处配置端口为8888)
-
启动dubbo-admin-ui,前端使用的是vue,需要Node环境
nvm install 8.4.0 nvm use 8.4.0 cd dubbo-admin-ui npm install #更改config/index.js的target端口号为8888(dubbo-admin-server配置的端口号) npm run dev #浏览器访问localhost:8081
2.3 dubbo服务提供者
-
pom.xml
<dependencies> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo-bom</artifactId> <version>2.6.4</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.6.4</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.10.RELEASE</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>2.12.0</version> </dependency> </dependencies>
-
provider.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" 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"> <!--指定服务名字--> <dubbo:application name="hello-world-app"></dubbo:application> <!--指定注册中心位置--> <dubbo:registry address="zookeeper://127.0.0.1:2181"></dubbo:registry> <!--指定通信规则--> <dubbo:protocol name="dubbo" port="20882"></dubbo:protocol> <!--暴露服务:interface指定接口名,ref指向实现类--> <dubbo:service interface="com.sankuai.api.HelloWorldService" ref="helloWorldService"></dubbo:service> <!--服务实现--> <bean id="helloWorldService" class="com.sankuai.impl.HelloWorldServiceImpl" ></bean> </beans>
-
provider代码
3.1 HelloWorldService.java
public interface HelloWorldService { String sayHello(String name); }
3.2 HelloWorldServiceImpl.java
import com.sankuai.api.HelloWorldService; import org.springframework.stereotype.Service; @Service("helloWorldService") public class HelloWorldServiceImpl implements HelloWorldService { @Override public String sayHello(String name) { System.out.println("hello" + name); return "hello" + name; } }
3.3 加载spring配置
import org.springframework.context.support.ClassPathXmlApplicationContext; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("provider.xml"); ctx.start(); System.in.read(); } }
3.4 在监控中心观察到服务
2.4 dubbo消费者
-
pom.xml
<dependencies> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo-bom</artifactId> <version>2.6.4</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.6.4</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.10.RELEASE</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>2.12.0</version> </dependency> <dependency> <groupId>com.sankuai</groupId> <artifactId>dubbo.studying</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
-
consumer.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" 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 http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="com.sankuai.consumer.impl"></context:component-scan> <!--消费方appkey --> <dubbo:application name="consumer-of-helloworld-app" /> <!-- 注册中心地址 --> <dubbo:registry address="zookeeper://127.0.0.1:2181" /> <!-- 指定需要使用的服务 --> <dubbo:reference id="helloWorldService" interface="com.sankuai.api.HelloWorldService" /> </beans>
-
consumer代码
3.1 Conmunicate.java
public interface Conmunicate { String communicate(String name) ; }
3.2 ConmunicateImpl.java
import com.sankuai.api.HelloWorldService; import com.sankuai.consumer.api.Conmunicate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service("conmunicateService") public class ConmunicateImpl implements Conmunicate { @Autowired HelloWorldService helloWorldService; public String communicate(String name) { return helloWorldService.sayHello(name); } }
3.3 加载spring配置,并调用远程服务
import com.sankuai.consumer.impl.ConmunicateImpl; import org.springframework.context.support.ClassPathXmlApplicationContext; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("consumer.xml"); ConmunicateImpl conmunicate = (ConmunicateImpl) ctx.getBean("conmunicateService"); String s = conmunicate.communicate("zhuyafeng"); System.out.println(s); System.in.read(); } }
3.4 监控中心观察消费者信息
2.5 dubbo监控中心安装
三、dubbo配置
3.1 配置之间的依赖关系
3.2 dubbo标签配置
标签 | 用途 | 解释 | 备注 |
---|---|---|---|
<dubbo:service/> | 服务配置 | 用于暴露一个服务,定义服务的元信息,一个服务可以用多个协议暴露,一个服务也可以注册到多个注册中心 |
|
<dubbo:reference/> | 引用配置 | 用于创建一个远程服务代理,一个引用可以指向多个注册中心 | 引用缺省是延迟初始化的,只有引用被注入到其它 Bean,或被 getBean() 获取,才会初始化。如果需要饥饿加载,即没有人引用也立即生成动态代理,可以配置:<dubbo:reference ... init="true" /> |
<dubbo:protocol/> | 协议配置 | 用于配置提供服务的协议信息,协议由提供方指定,消费方被动接受 |
|
<dubbo:application/> | 应用配置 | 用于配置当前应用信息,不管该应用是提供者还是消费者 |
|
<dubbo:module/> | 模块配置 | 用于配置当前模块信息,可选 |
|
<dubbo:registry/> | 注册中心配置 | 用于配置连接注册中心相关信息 |
|
<dubbo:monitor/> | 监控中心配置 | 用于配置连接监控中心相关信息,可选 |
|
<dubbo:provider/> | 提供方配置 | 当 ProtocolConfig 和 ServiceConfig 某属性没有配置时,采用此缺省值,可选 |
|
<dubbo:consumer/> | 消费方配置 | 当 ReferenceConfig 某属性没有配置时,采用此缺省值,可选 |
|
<dubbo:method/> | 方法配置 | 用于 ServiceConfig 和 ReferenceConfig 指定方法级的配置信息 |
|
<dubbo:argument/> | 参数配置 | 用于指定方法参数配置 |
|
3.3 常用dubbo配置(以xml配置为例)
功能 | 功能 | 配置方式 | 备注 |
---|---|---|---|
启动时检查 | Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题,默认 check="true" | <dubbo:reference check="false> 关闭某个服务检查 <dubbo:consumer check="false> 关闭所有服务检查 <dubbo:register check="false"> 关闭注册中心检查 |
|
集群容错 | 在集群调用失败时,Dubbo 提供了多种容错方案,缺省为 failover 重试 | 集群容错模式 <dubbo:service cluster="failover" /> failover Cluster:失败自动切换,当出现失败,重试其它服务器 Failfast Cluster:快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。 Failsafe Cluster:失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。 Failback Cluster:失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作 Forking Cluster:并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。
| 默认为failover,配合重试使用<dubbo:service retries="2" />(支持服务级,reference级和方法级配置) |
负载均衡 | 在集群负载均衡时,Dubbo 提供了多种均衡策略,缺省为 random 随机调用 | 服务端服务级别 <dubbo:service interface="..." loadbalance="roundrobin" /> 客户端服务级别 <dubbo:reference interface="..." loadbalance="roundrobin" /> 服务端方法级别 <dubbo:service interface="..."> <dubbo:method name="..." loadbalance="roundrobin"/> </dubbo:service> 客户端方法级别 <dubbo:reference interface="..."> <dubbo:method name="..." loadbalance="roundrobin"/> </dubbo:reference> |
|
直连服务提供者 | 在开发及测试环境下,经常需要绕过注册中心,只测试指定服务提供者,这时候可能需要点对点直连,点对点直连方式,将以服务接口为单位,忽略注册中心的提供者列表,A 接口配置点对点,不影响 B 接口从注册中心获取列表。 | <dubbo:reference id="xxxService" interface="com.alibaba.xxx.XxxService" url="dubbo://localhost:20890" /> | 不要在线上使用!!! |
多协议暴露 | Dubbo 允许配置多协议,在不同服务上支持不同协议或者同一服务上同时支持多种协议。 |
|
|
服务分组 | 当一个接口有多种实现时,可以用 group 区分 | 服务提供者 服务消费者 | dubbo2.2.0以上版本,服务消费者支持使用任意版本
|
多版本 | 当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。 可以按照以下的步骤进行版本迁移:
| 老版本服务提供者配置: <dubbo:service interface="com.foo.BarService" version="1.0.0" /> 新版本服务提供者配置: <dubbo:service interface="com.foo.BarService" version="2.0.0" /> 老版本服务消费者配置: <dubbo:reference id="barService" interface="com.foo.BarService" version="1.0.0" /> 新版本服务消费者配置: <dubbo:reference id="barService" interface="com.foo.BarService" version="2.0.0" /> 如果不需要区分版本,可以按照以下的方式配置 [1]: <dubbo:reference id="barService" interface="com.foo.BarService" version="*" /> |
Consumer只能消费与自己 “接口+分组+版本号” 一致的Provider提供的服务。
|
结果缓存 | 结果缓存 ,用于加速热门数据的访问速度,Dubbo 提供声明式缓存,以减少用户加缓存的工作量 | 接口级设置 方法级设置
|
|
上下文信息 | 获取当前RPC的上下文信息 |
|
|
provider异步执行 |
| 1.定义CompletableFuture签名的接口 通过return CompletableFuture.supplyAsync(),业务执行已从Dubbo线程切换到业务线程,避免了对Dubbo线程池的阻塞。 2.使用AsyncContext Dubbo提供了一个类似Serverlet 3.0的异步接口AsyncContext,在没有CompletableFuture签名接口的情况下,也可以实现Provider端的异步执行。
|
|
consumer异步调用 |
| 1.使用CompletableFuture签名的接口,此方法依赖于服务提供者事先定义CompletableFuture签名的服务
2.使用RpcContext
3. 利用Java 8提供的default接口实现,重载一个带有带有CompletableFuture签名的方法 | 方法1是直接使用服务端实现的异步执行
方法2,3是在客户端执行异步调用
Provider端异步执行和Consumer端异步调用是相互独立的,你可以任意正交组合两端配置
|
本地伪装 | 本地伪装通常用于服务降级,比如某验权服务,当服务提供方全部挂掉后,客户端不抛出异常,而是通过 Mock 数据返回授权失败。 |
|
|
本地伪装 | 本地伪装通常用于服务降级,比如某验权服务,当服务提供方全部挂掉后,客户端不抛出异常,而是通过 Mock 数据返回授权失败。 |
|
|
令牌验证 | 通过令牌验证在注册中心控制权限,以决定要不要下发令牌给消费者,可以防止消费者绕过注册中心访问提供者,另外通过注册中心可灵活改变授权方式,而不需修改或升级提供者 | 可以全局设置开启令牌验证: <!--随机token令牌,使用UUID生成--> <dubbo:provider interface="com.foo.BarService" token="true" /> 或 <!--固定token令牌,相当于密码--> <dubbo:provider interface="com.foo.BarService" token="123456" /> 也可在服务级别设置: <!--随机token令牌,使用UUID生成--> <dubbo:service interface="com.foo.BarService" token="true" /> 或 <!--固定token令牌,相当于密码--> <dubbo:service interface="com.foo.BarService" token="123456" /> |
|
线程栈自动dump | 当业务线程池满时,我们需要知道线程都在等待哪些资源、条件,以找到系统的瓶颈点或异常点。dubbo通过Jstack自动导出线程堆栈来保留现场,方便排查问题
| 默认策略:
指定导出路径: # dubbo.properties dubbo.application.dump.directory=/tmp <dubbo:application ...> <dubbo:parameter key="dump.directory" value="/tmp" /> </dubbo:application>
|
|
序列化 | dubbo支持多种序列化方式,支持使用高效的Java序列化(Kryo和FST) | <dubbo:protocol name="dubbo" serialization="kryo"/> <dubbo:protocol name="dubbo" serialization="fst"/> | 性能:dubbo>hession2>json>java 其中dubbo序列化为阿里自研,不建议生产环境使用 |
3.3 dubbo配置属性的覆盖策略
dubbo的属性配置可以出现在狠多地方,关于这些配置的查找顺序符合以下规则:
-
方法级优先,接口级次之,全局配置再次之。
-
如果级别一样,则消费方优先,提供方次之。
四、Dubbo执行过程
4.1 服务注册时序图
4.2 服务发现时序图
4.3 服务调用
五、横向对比
5.1 各大互联网公司使用的RPC框架
公司 | RPC框架 |
---|---|
阿里巴巴 | HSF、Dubbo |
腾讯 | |
百度 | |
美团点评 | mtthrift(基于thrift)、pigeon |
微博 | |
京东 | JSF(基于Dubbo) |
网易考拉 | Dubbok(基于Dubbo) |
当当 | Dubbox(基于Dubbo) |
| |
| |
|
5.2 pigeon&dubbo对比
| Dubbo | pigeon | 备注 |
---|---|---|---|
开发语言 | Java | Java |
|
分布式(服务治理) | dubbo monitor | pigeon |
|
序列化方式 | dubbo(基于hession2)、hession、json、jdk fst、kryo 支持扩展 | hessian、json、protobuf3、thrift、jdk fst 支持扩展 |
|
注册中心 | zookeeper、redis、multicast、simple | zookeeper->mns(基于zookeeper) |
|
跨编程语言 | 不支持 | 不支持 |
|
配置方式 | 注解配置、schema配置、api配置 | 注解配置、schema配置、属性配置、api配置 |
|
服务通信协议 | Dubbo 协议、 Rmi 协议、 Hessian 协议、 HTTP 协议、 WebService 协议、Dubbo Thrift 协议、Memcached 协议 | HTTP协议,TCP协议 |
|
负载均衡 | RandomLoadBalance (默认)、RoundRobinLoadBalance 、 ConsistentHash、LeastActive | WeightedAutoaware(默认,根据客户端到服务节点的在途请求数,结合有效权重,重新计算请求容量,挑选请求容量最小的服务节点进行随机负载)、Random、RoundRobin |
|
服务容错 | Failover(默认策略,重试次数2) Failfast(只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录) Failsafe(失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作) Failback (失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作) Forking(调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数) Broadcast(广播调用所有提供者,逐个调用,任意一台报错则报错 。通常用于通知所有提供者更新缓存或日志等本地资源信息) | failfast(默认,失败之后直接返回) failover(失败转移策略,当调用一个节点失败后,会调用另一个服务节点,重试次数默认为-1,不显示设置retry则不会重试) failsafe(调用失败后,不抛异常,会返回默认值null) forking(并行策略,并行同时调用多个服务节点,以最先返回的结果为最终结果返) hedged(发起调用后,超过hedgedDelay时间后未返回结果,会再次向其他服务节点发送一个请求,以最先返回的结果为结果返回,主要用在解决服务调用长尾问题)
|
|
服务调用方式 | 同步(默认,通过配置asyn属性) 异步 oneway(通过方法里面配置return=“false”属性,同时可以使用sent="true"来等待消息发出,发送失败则抛异常) callback
事件通知:Consumer 端在调用之前、调用之后或出现异常时,触发 oninvoke、onreturn、onthrow 三个事件。
| sync 默认 oneway 提交请求,无需等待,不需要返回结果 future 请求提交给pigeon后立即返回,不等待返回结果,由pigeon负责等待返回结果,客户端可以自行决定何时何地来取返回结果 callback 回调方式,客户端将请求提交给pigeon后立即返回,也不等待返回结果,它与future方式的区别是,callback必须提供一个实现了pigeon提供的ServiceCallback接口的回调对象给pigeon,pigeon负责接收返回结果并传递回给这个回调对象 | 其实不管是dubbo还是pigeon,同步调用的底层IO也是异步实现的。客户端发起同步调用之后,会得到一个future对象,只不过同步调用的线程会阻塞到timeout,如果超时没有还没有返回,则返回调用失败
调用方式判断:DubboInvoker<T> |