一、Dubbo是什么

Dubbo 是一个分布式、高性能、透明化的 RPC 服务框架,提供服务自动注册、自动发现等高效服务治理方案, 可以和 Spring 框架无缝集成。

 

RPC 指的是远程调用协议,也就是说两个服务器交互数据。

二、Dubbo的由来

1、背景

互联网的快速发展,Web应用程序的规模不断扩大,一般会经历如下四个发展阶段。

 

单一应用架构

image

当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。

 

此时,用于简化增删改查工作量的 数据访问框架(ORM) 是关键。

 

垂直应用架构

image

当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。

 

此时,用于加速前端页面开发的 Web框架(MVC) 是关键。

 

分布式服务架构

image

当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。

 

此时,用于提高业务复用及整合的 分布式服务框架(RPC) 是关键。

 

流动计算架构

image

当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。

 

此时,用于提高机器利用率的 资源调度和治理中心(SOA) 是关键。

2、需求

image

在大规模服务化之前,应用可能只是通过RMI或Hessian等工具,简单的暴露和引用远程服务,通过配置服务的URL地址进行调用,通过F5等硬件进行负载均衡。

  • (1) 当服务越来越多时,服务URL配置管理变得非常困难,F5硬件负载均衡器的单点压力也越来越大。   此时需要一个服务注册中心,动态的注册和发现服务,使服务的位置透明。   并通过在消费方获取服务提供方地址列表,实现软负载均衡和Failover,降低对F5硬件负载均衡器的依赖,也能减少部分成本。

  • (2) 当进一步发展,服务间依赖关系变得错踪复杂,甚至分不清哪个应用要在哪个应用之前启动,架构师都不能完整的描述应用的架构关系。   这时,需要自动画出应用间的依赖关系图,以帮助架构师理清理关系。

  • (3) 接着,服务的调用量越来越大,服务的容量问题就暴露出来,这个服务需要多少机器支撑?什么时候该加机器?   为了解决这些问题,第一步,要将服务现在每天的调用量,响应时间,都统计出来,作为容量规划的参考指标。   其次,要可以动态调整权重,在线上,将某台机器的权重一直加大,并在加大的过程中记录响应时间的变化,直到响应时间到达阀值,记录此时的访问量,再以此访问量乘以机器数反推总容量。   以上是Dubbo最基本的几个需求,更多服务治理问题参见: http://code.alibabatech.com/blog/experience_1402/service-governance-process.html

3.Dubbo的主要应用场景?

透明化的远程方法调用,就像调用本地方法一样调用远程方法,只需简单配置,没有任何API侵入。   软负载均衡及容错机制,可在内网替代F5等硬件负载均衡器,降低成本,减少单点。   服务自动注册与发现,不再需要写死服务提供方地址,注册中心基于接口名查询服务提供者的IP地址,并且能够平滑添加或删除服务提供者。

4.Dubbo的核心功能?

主要就是如下3个核心功能:

  • **Remoting:**网络通信框架,提供对多种NIO框架抽象封装,包括“同步转异步”和“请求-响应”模式的信息交换方式。
  • Cluster:服务框架,提供基于接口方法的透明远程过程调用,包括多协议支持,以及软负载均衡,失败容错,地址路由,动态配置等集群支持。
  • Registry:服务注册,基于注册中心目录服务,使服务消费方能动态的查找服务提供方,使地址透明,使服务提供方可以平滑增加或减少机器。

5.Dubbo的核心组件?

image

6.Dubbo服务注册与发现的流程?

节点角色说明:

  • Provider: 暴露服务的服务提供方。

  • Consumer: 调用远程服务的服务消费方。

  • Registry: 服务注册与发现的注册中心。

  • Monitor: 统计服务的调用次调和调用时间的监控中心。

  • Container: 服务运行容器。

调用关系说明:

  • 0.服务容器负责启动,加载,运行服务提供者。

  • 1.服务提供者在启动时,向注册中心注册自己提供的服务。

  • 2.服务消费者在启动时,向注册中心订阅自己所需的服务。

  • 3.注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。

  • 4.服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。

  • 5.服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

流程说明:

  • Provider(提供者)绑定指定端口并启动服务
  • 指供者连接注册中心,将本机IP、端口、应用信息和提供服务信息发送至注册中心存储
  • Consumer(消费者),连接注册中心 ,并发送应用信息、所求服务信息至注册中心
  • 注册中心根据 消费 者所求服务信息匹配对应的提供者列表发送至Consumer 应用缓存。
  • Consumer 在发起远程调用时基于缓存的消费者列表择其一发起调用。
  • Provider 状态变更会实时通知注册中心、在由注册中心实时推送至Consumer

设计的原因:

  • Consumer 与Provider 解偶,双方都可以横向增减节点数。
  • 注册中心对本身可做对等集群,可动态增减节点,并且任意一台宕掉后,将自动切换到另一台
  • 去中心化,双方不直接依懒注册中心,即使注册中心全部宕机短时间内也不会影响服务的调用
  • 服务提供者无状态,任意一台宕掉后,不影响使用

image

7.Dubbo的架构设计?

image.png

Dubbo框架设计一共划分了10个层: Dubbo框架设计一共划分了10个层,而最上面的Service层是留给实际想要使用Dubbo开发分布式服务的开发者实现业务逻辑的接口层。图中左边淡蓝背景的为服务消费方使用的接口,右边淡绿色背景的为服务提供方使用的接口, 位于中轴线上的为双方都用到的接口。

 

下面,结合Dubbo官方文档,我们分别理解一下框架分层架构中,各个层次的设计要点:

  • 服务接口层(Service):该层是与实际业务逻辑相关的,根据服务提供方和服务消费方的业务设计对应的接口和实现。

  • 配置层(Config):对外配置接口,以ServiceConfig和ReferenceConfig为中心,可以直接new配置类,也可以通过spring解析配置生成配置类。

  • 服务代理层(Proxy):服务接口透明代理,生成服务的客户端Stub和服务器端Skeleton,以ServiceProxy为中心,扩展接口为ProxyFactory。

  • 服务注册层(Registry):封装服务地址的注册与发现,以服务URL为中心,扩展接口为RegistryFactory、Registry和RegistryService。可能没有服务注册中心,此时服务提供方直接暴露服务。

  • 集群层(Cluster):封装多个提供者的路由及负载均衡,并桥接注册中心,以Invoker为中心,扩展接口为Cluster、Directory、Router和LoadBalance。将多个服务提供方组合为一个服务提供方,实现对服务消费方来透明,只需要与一个服务提供方进行交互。

  • 监控层(Monitor):RPC调用次数和调用时间监控,以Statistics为中心,扩展接口为MonitorFactory、Monitor和MonitorService。

  • 远程调用层(Protocol):封将RPC调用,以Invocation和Result为中心,扩展接口为Protocol、Invoker和Exporter。Protocol是服务域,它是Invoker暴露和引用的主功能入口,它负责Invoker的生命周期管理。Invoker是实体域,它是Dubbo的核心模型,其它模型都向它靠扰,或转换成它,它代表一个可执行体,可向它发起invoke调用,它有可能是一个本地的实现,也可能是一个远程的实现,也可能一个集群实现。

  • 信息交换层(Exchange):封装请求响应模式,同步转异步,以Request和Response为中心,扩展接口为Exchanger、ExchangeChannel、ExchangeClient和ExchangeServer。

  • 网络传输层(Transport):抽象mina和netty为统一接口,以Message为中心,扩展接口为Channel、Transporter、Client、Server和Codec。

  • 数据序列化层(Serialize):可复用的一些工具,扩展接口为Serialization、 ObjectInput、ObjectOutput和ThreadPool。

从上图可以看出,Dubbo对于服务提供方和服务消费方,从框架的10层中分别提供了各自需要关心和扩展的接口,构建整个服务生态系统(服务提供方和服务消费方本身就是一个以服务为中心的)。

 

根据官方提供的,对于上述各层之间关系的描述,如下所示: 在RPC中,Protocol是核心层,也就是只要有Protocol + Invoker + Exporter就可以完成非透明的RPC调用,然后在Invoker的主过程上Filter拦截点。

 

图中的Consumer和Provider是抽象概念,只是想让看图者更直观的了解哪些类分属于客户端与服务器端,不用Client和Server的原因是Dubbo在很多场景下都使用Provider、Consumer、Registry、Monitor划分逻辑拓普节点,保持统一概念。

 

而Cluster是外围概念,所以Cluster的目的是将多个Invoker伪装成一个Invoker,这样其它人只要关注Protocol层Invoker即可,加上Cluster或者去掉Cluster对其它层都不会造成影响,因为只有一个提供者时,是不需要Cluster的。

 

Proxy层封装了所有接口的透明化代理,而在其它层都以Invoker为中心,只有到了暴露给用户使用时,才用Proxy将Invoker转成接口,或将接口实现转成Invoker,也就是去掉Proxy层RPC是可以Run的,只是不那么透明,不那么看起来像调本地服务一样调远程服务。

 

而Remoting实现是Dubbo协议的实现,如果你选择RMI协议,整个Remoting都不会用上,Remoting内部再划为Transport传输层和Exchange信息交换层,Transport层只负责单向消息传输,是对Mina、Netty、Grizzly的抽象,它也可以扩展UDP传输,而Exchange层是在传输层之上封装了Request-Response语义。

 

Registry和Monitor实际上不算一层,而是一个独立的节点,只是为了全局概览,用层的方式画在一起。

8.设计的优势

(1) 连通性:

  • 注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小

  • 监控中心负责统计各服务调用次数,调用时间等,统计先在内存汇总后每分钟一次发送到监控中心服务器,并以报表展示

  • 服务提供者向注册中心注册其提供的服务,并汇报调用时间到监控中心,此时间不包含网络开销

  • 服务消费者向注册中心获取服务提供者地址列表,并根据负载算法直接调用提供者,同时汇报调用时间到监控中心,此时间包含网络开销

  • 注册中心,服务提供者,服务消费者三者之间均为长连接,监控中心除外

  • 注册中心通过长连接感知服务提供者的存在,服务提供者宕机,注册中心将立即推送事件通知消费者

  • 注册中心和监控中心全部宕机,不影响已运行的提供者和消费者,消费者在本地缓存了提供者列表

  • 注册中心和监控中心都是可选的,服务消费者可以直连服务提供者

(2) 健状性:

  • 监控中心宕掉不影响使用,只是丢失部分采样数据

  • 数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务

  • 注册中心对等集群,任意一台宕掉后,将自动切换到另一台

  • 注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯

  • 服务提供者无状态,任意一台宕掉后,不影响使用

  • 服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复

(3) 伸缩性:

  • 注册中心为对等集群,可动态增加机器部署实例,所有客户端将自动发现新的注册中心

  • 服务提供者无状态,可动态增加机器部署实例,注册中心将推送新的服务提供者信息给消费者

(4) 升级性:

  • 当服务集群规模进一步扩大,带动IT治理结构进一步升级,需要实现动态部署,进行流动计算,现有分布式服务架构不会带来阻力:

 image

9.Dubbo的服务调用流程?

image

10.Dubbo支持哪些协议,每种协议的应用场景,优缺点?

  • dubbo: 单一长连接和NIO异步通讯,适合大并发小数据量的服务调用,以及消费者远大于提供者。传输协议TCP,异步,Hessian序列化;
  • rmi: 采用JDK标准的rmi协议实现,传输参数和返回参数对象需要实现Serializable接口,使用java标准序列化机制,使用阻塞式短连接,传输数据包大小混合,消费者和提供者个数差不多,可传文件,传输协议TCP。 多个短连接,TCP协议传输,同步传输,适用常规的远程服务调用和rmi互操作。在依赖低版本的Common-Collections包,java序列化存在安全漏洞;
  • webservice: 基于WebService的远程调用协议,集成CXF实现,提供和原生WebService的互操作。多个短连接,基于HTTP传输,同步传输,适用系统集成和跨语言调用;
  • http: 基于Http表单提交的远程调用协议,使用Spring的HttpInvoke实现。多个短连接,传输协议HTTP,传入参数大小混合,提供者个数多于消费者,需要给应用程序和浏览器JS调用;
  • hessian: 集成Hessian服务,基于HTTP通讯,采用Servlet暴露服务,Dubbo内嵌Jetty作为服务器时默认实现,提供与Hession服务互操作。多个短连接,同步HTTP传输,Hessian序列化,传入参数较大,提供者大于消费者,提供者压力较大,可传文件;
  • memcache: 基于memcached实现的RPC协议
  • redis: 基于redis实现的RPC协议

11.dubbo推荐用什么协议?

默认使用dubbo协议

 

12.Dubbo有些哪些注册中心?

  • Multicast注册中心: Multicast注册中心不需要任何中心节点,只要广播地址,就能进行服务注册和发现。基于网络中组播传输实现;
  • Zookeeper注册中心: 基于分布式协调系统Zookeeper实现,采用Zookeeper的watch机制实现数据变更;
  • redis注册中心: 基于redis实现,采用key/Map存储,住key存储服务名和类型,Map中key存储服务URL,value服务过期时间。基于redis的发布/订阅模式通知数据变更;
  • Simple注册中心

13.Dubbo的服务治理?

image

  • 过多的服务URL配置困难

  • 负载均衡分配节点压力过大的情况下也需要部署集群

  • 服务依赖混乱,启动顺序不清晰

  • 过多服务导致性能指标分析难度较大,需要监控

14.Dubbo的注册中心集群挂掉,发布者和订阅者之间还能通信么?

可以的,启动dubbo时,消费者会从zookeeper拉取注册的生产者的地址接口等数据,缓存在本地。

 

每次调用时,按照本地存储的地址进行调用。

 

15.Dubbo与Spring的关系?

Dubbo采用全Spring配置方式,透明化接入应用,对应用没有任何API侵入,只需用Spring加载Dubbo的配置即可,Dubbo基于Spring的Schema扩展进行加载。

 

16.Dubbo使用的是什么通信框架?

默认使用NIO Netty框架

 

17.Dubbo集群提供了哪些负载均衡策略?

  • Random LoadBalance: 随机选取提供者策略,有利于动态调整提供者权重。截面碰撞率高,调用次数越多,分布越均匀;
  • RoundRobin LoadBalance: 轮循选取提供者策略,平均分布,但是存在请求累积的问题;
  • LeastActive LoadBalance: 最少活跃调用策略,解决慢提供者接收更少的请求;
  • ConstantHash LoadBalance: 一致性Hash策略,使相同参数请求总是发到同一提供者,一台机器宕机,可以基于虚拟节点,分摊至其他提供者,避免引起提供者的剧烈变动;

缺省时为Random随机调用

 

18.Dubbo的集群容错方案有哪些?

  • Failover Cluster
  • 失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。
  • Failfast Cluster
  • 快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。
  • Failsafe Cluster
  • 失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。
  • Failback Cluster
  • 失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
  • Forking Cluster
  • 并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks=”2″ 来设置最大并行数。
  • Broadcast Cluster
  • 广播调用所有提供者,逐个调用,任意一台报错则报错 。通常用于通知所有提供者更新缓存或日志等本地资源信息。

更多Dubbo集群容错配置请参考: Dubbo学习笔记7:Dubbo的集群容错与负载均衡策略

Dubbo学习源码总结系列四--集群容错机制

Dubbo集群容错方案

19.Dubbo的默认集群容错方案?

  • Failover Cluster
  • retries重试次数为2次,不包括第一次

SpringBoot配置为:

#dubbo集群容错6种方案failover,failfast,failsafe,failback,forking,froadcast,
#默认为failover 默认重试次数为2次
spring.dubbo.reference.cluster=failover
spring.dubbo.reference.retries=2

Dubbo服务配置的优先级别 客户端的配置优先于服务端

  • 1、方法级别优先,然后是接口,最后是全局配置
  • 2、如果级别是一样的,客户端优先 retries(客户端)、LoadBalance(客户端)、cluster(客户端)、timeout(服务端)

 

Dubbo服务降级 降级的目的是为了保证核心服务可用

 

dubbo的降级方式:mock

 

本地伪装通常用于服务降级,比如某验权服务,当服务提供方全部挂掉后,客户端不抛出异常,而是通过 Mock 数据返回授权失败。

 

在 spring 配置文件中按以下方式配置:

<dubbo:reference interface="com.foo.BarService" mock="true" />

<dubbo:reference interface="com.foo.BarService" mock="com.foo.BarServiceMock" />

在工程中提供 Mock 实现:

package com.foo;
public class BarServiceMock implements BarService {
    public String sayHello(String name) {
        // 你可以伪造容错数据,此方法只在出现RpcException时被执行
        return "容错数据";
    }
}

如果服务的消费方经常需要 try-catch 捕获异常,如:

Offer offer = null;
try {
    offer = offerService.findOffer(offerId);
} catch (RpcException e) {
   logger.error(e);
}

请考虑改为 Mock 实现,并在 Mock 实现中 return null。如果只是想简单的忽略异常,在 2.0.11 以上版本可用:

<dubbo:reference interface="com.foo.BarService" mock="return null" />

更多Dubbo降级用法请参考Dubbo官方文档: http://dubbo.apache.org/zh-cn/docs/user/demos/local-mock.html

Dubbo的SPI机制 Dubbo 的扩展点加载从 JDK 标准的 SPI (Service Provider Interface) 扩展点发现机制加强而来。

 

Dubbo 改进了 JDK 标准的 SPI 的以下问题:

  • JDK标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。
  • 如果扩展点加载失败,连扩展点的名称都拿不到了。比如:JDK 标准的 ScriptEngine,通过 getName() 获取脚本类型的名称,但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在,导致 RubyScriptEngine 类加载失败,这个失败原因被吃掉了,和 ruby 对应不起来,当用户执行 ruby 脚本时,会报不支持 ruby,而不是真正失败的原因。
  • 增加了对扩展点 IoC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点。

SPI接口定义 定义了@SPI注解

public @interface SPI {

  String value() default ""; //指定默认的扩展点

} 

只有在接口打了@SPI注解的接口类才会去查找扩展点实现

会依次从这几个文件中读取扩展点

META-INF/dubbo/internal/   //dubbo内部实现的各种扩展都放在了这个目录了

META-INF/dubbo/

META-INF/services/

在扩展类的 jar 包内,放置扩展点配置文件 META-INF/dubbo/接口全限定名,内容为:配置名=扩展实现类全限定名,多个实现类用换行符分隔。

 

以扩展 Dubbo 的协议为例,在协议的实现 jar 包内放置文本文件:META-INF/dubbo/org.apache.dubbo.rpc.Protocol,内容为:

xxx=com.alibaba.xxx.XxxProtocol

更多关于Dubbo的SPI机制请阅读: http://dubbo.apache.org/zh-cn/docs/source_code_guide/dubbo-spi.html

2. Dubbo原理解析-Dubbo内核实现之基于SPI思想Dubbo内核实现(转)

Dubbo的SPI机制

聊聊Dubbo(五):核心源码-SPI扩展

 

20.Dubbo支持哪些序列化方式?

默认使用Hessian序列化,还有Dubbo、FastJson、Java自带序列化。

 

21.Dubbo超时时间怎样设置?

Dubbo超时时间设置有两种方式:

  • 服务提供者端设置超时时间,在Dubbo的用户文档中,推荐如果能在服务端多配置就尽量多配置,因为服务提供者比消费者更清楚自己提供的服务特性。
  • 服务消费者端设置超时时间,如果在消费者端设置了超时时间,以消费者端为主,即优先级更高。因为服务调用方设置超时时间控制性更灵活。如果消费方超时,服务端线程不会定制,会产生警告。

22.服务调用超时问题怎么解决?

dubbo在调用服务不成功时,默认是会重试两次的。

 

23.Dubbo在安全机制方面是如何解决?

Dubbo通过Token令牌防止用户绕过注册中心直连,然后在注册中心上管理授权。Dubbo还提供服务黑白名单,来控制服务所允许的调用方。

 

24.dubbo 和 dubbox 之间的区别?

dubbox 基于 dubbo 上做了一些扩展,如加了服务可 restful 调用,更新了开源组件等。

 

25.除了Dubbo还有哪些分布式框架?

大家熟知的就是Spring cloud,当然国外也有类似的多个框架。

 

26.Dubbo和Spring Cloud的关系?

  • Dubbo 是 SOA 时代的产物,它的关注点主要在于服务的调用,流量分发、流量监控和熔断。

  • Spring Cloud 诞生于微服务架构时代,考虑的是微服务治理的方方面面,另外由于依托了Spring、Spirng Boot的优势之上,两个框架在开始目标就不一致,Dubbo 定位服务治理、Spirng Cloud 是一个生态。

27.dubbo和spring cloud的区别?

最大的区别:Dubbo底层是使用Netty这样的NIO框架,是基于TCP协议传输的,配合以Hession序列化完成RPC通信。

 

而SpringCloud是基于Http协议+Rest接口调用远程过程的通信,相对来说,Http请求会有更大的报文,占的带宽也会更多。但是REST相比RPC更为灵活,服务提供方和调用方的依赖只依靠一纸契约,不存在代码级别的强依赖,这在强调快速演化的微服务环境下,显得更为合适,至于注重通信速度还是方便灵活性,具体情况具体考虑。

image

三、如何使用Dubbo

由于Dubbo无缝的集成了spring,所以我们使用起来还是很方便的,

本地服务:(Spring配置)

local.xml

<bean id=“xxxService” class=“com.xxx.XxxServiceImpl” />

 <bean id=“xxxAction” class=“com.xxx.XxxAction”>
     <property name=“xxxService” ref=“xxxService” />
 </bean>

远程服务:(Spring配置)

在本地服务的基础上,只需做简单配置,即可完成远程化:

  • 将上面的local.xml配置拆分成两份,将服务定义部分放在服务提供方remote-provider.xml,将服务引用部分放在服务消费方remote-consumer.xml。

  • 并在提供方增加暴露服务配置dubbo:service,在消费方增加引用服务配置dubbo:reference

如下:

服务提供者:remote-provider.xml

 <bean id=“xxxService” class=“com.xxx.XxxServiceImpl” /> <!-- 和本地服务一样实现远程服务 -->

 <dubbo:service interface=“com.xxx.XxxService” ref=“xxxService” /> <!-- 增加暴露远程服务配置 -->

服务消费者:remote-consumer.xml

<dubbo:reference id=“xxxService” interface=“com.xxx.XxxService” /> <!-- 增加引用远程服务配置 -->

 <bean id=“xxxAction” class=“com.xxx.XxxAction”> <!-- 和本地服务一样使用远程服务 -->
     <property name=“xxxService” ref=“xxxService” />
 </bean>

Dubbo采用全Spring配置方式,透明化接入应用,对应用没有任何API侵入,只需用Spring加载Dubbo的配置即可,Dubbo基于Spring的Schema扩展进行加载。

1.示例

服务提供者

定义服务接口: (该接口需单独打包,在服务提供方和消费方共享)

package com.alibaba.dubbo.demo;

public interface DemoService {

    String sayHello(String name);

}

在服务提供方实现接口:(对服务消费方隐藏实现)

package com.alibaba.dubbo.demo.provider;

import com.alibaba.dubbo.demo.DemoService;

public class DemoServiceImpl implements DemoService {

    public String sayHello(String name) {
        return "Hello " + name;
    }

}

用Spring配置声明暴露服务: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://code.alibabatech.com/schema/dubbo"
      xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans.xsd        http://code.alibabatech.com/schema/dubbo        http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

      <!-- 提供方应用信息,用于计算依赖关系 -->
      <dubbo:application name="hello-world-app"  />

     <!-- 使用multicast广播注册中心暴露服务地址 -->
     <dubbo:registry address="multicast://224.5.6.7:1234" />

     <!-- 用dubbo协议在端口暴露服务 -->
     <dubbo:protocol name="dubbo" port="20880" />

     <!-- 声明需要暴露的服务接口 -->
     <dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService" />

     <!-- 和本地bean一样实现服务 -->
     <bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl" />

 </beans>

加载Spring配置:

import org.springframework.context.support.ClassPathXmlApplicationContext;

  public class Provider {

      public static void main(String[] args) throws Exception {
          ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"http://10.20.160.198/wiki/display/dubbo/provider.xml"});
          context.start();

          System.in.read(); // 按任意键退出
     }

 }

服务消费者

通过Spring配置引用远程服务: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://code.alibabatech.com/schema/dubbo"
      xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans.xsd        http://code.alibabatech.com/schema/dubbo        http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

      <!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
      <dubbo:application name="consumer-of-helloworld-app"  />

     <!-- 使用multicast广播注册中心暴露发现服务地址 -->
     <dubbo:registry address="multicast://224.5.6.7:1234" />

     <!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
     <dubbo:reference id="demoService" interface="com.alibaba.dubbo.demo.DemoService" />

 </beans>

加载Spring配置,并调用远程服务:(也可以使用IoC注入)

import org.springframework.context.support.ClassPathXmlApplicationContext;
  import com.alibaba.dubbo.demo.DemoService;

  public class Consumer {

      public static void main(String[] args) throws Exception {
          ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"http://10.20.160.198/wiki/display/dubbo/consumer.xml"});
          context.start();

         DemoService demoService = (DemoService)context.getBean("demoService"); // 获取远程服务代理
         String hello = demoService.sayHello("world"); // 执行远程方法

         System.out.println( hello ); // 显示调用结果
     }

 }

2.其他广播方式

示例中使用的是multicast广播暴露和发现服务的,当然还有其他的方式,现在使用最多的是ZooKeeper

 

如果使用ZooKeeper则配置修改为:

<dubbo:registry  address="multicast://224.5.6.7:1234" />
 替换为
 <dubbo:registry protocol="zookeeper" address="192.168.17.129:2181,192.168.17.129:2182,192.168.17.129:2183" /> 或者
 <dubbo:registry address="zookeeper://192.168.17.129:2181?backup=192.168.17.129:2182,192.168.17.129:2183" />

服务提供者和服务消费者做相同的修改

3.说明

以上是Dubbo的XML配置,Dubbo还支持属性配置,注解配置,API配置,下面介绍下API配置,另外两种配置方式,感兴趣的朋友可以去Dubbo官网去看下:Dubbo

 

如果不想使用Spring配置,而希望通过API的方式进行调用(官方不推荐)则

(1) 服务提供者:

import com.alibaba.dubbo.rpc.config.ApplicationConfig;
  import com.alibaba.dubbo.rpc.config.RegistryConfig;
  import com.alibaba.dubbo.rpc.config.ProviderConfig;
  import com.alibaba.dubbo.rpc.config.ServiceConfig;
  import com.xxx.XxxService;
  import com.xxx.XxxServiceImpl;

  // 服务实现
  XxxService xxxService = new XxxServiceImpl();

 // 当前应用配置
 ApplicationConfig application = new ApplicationConfig();
 application.setName("xxx");

 // 连接注册中心配置
 RegistryConfig registry = new RegistryConfig();
 registry.setAddress("10.20.130.230:9090");
 registry.setUsername("aaa");
 registry.setPassword("bbb");

 // 服务提供者协议配置
 ProtocolConfig protocol = new ProtocolConfig();
 protocol.setName("dubbo");
 protocol.setPort();
 protocol.setThreads();

 // 注意:ServiceConfig为重对象,内部封装了与注册中心的连接,以及开启服务端口

 // 服务提供者暴露服务配置
 ServiceConfig<XxxService> service = new ServiceConfig<XxxService>(); // 此实例很重,封装了与注册中心的连接,请自行缓存,否则可能造成内存和连接泄漏
 service.setApplication(application);
 service.setRegistry(registry); // 多个注册中心可以用setRegistries()
 service.setProtocol(protocol); // 多个协议可以用setProtocols()
 service.setInterface(XxxService.class);
 service.setRef(xxxService);
 service.setVersion("1.0.0");

 // 暴露及注册服务
 service.export();

(2) 服务消费者:

  import com.alibaba.dubbo.rpc.config.ApplicationConfig;
  import com.alibaba.dubbo.rpc.config.RegistryConfig;
  import com.alibaba.dubbo.rpc.config.ConsumerConfig;
  import com.alibaba.dubbo.rpc.config.ReferenceConfig;
  import com.xxx.XxxService;

  // 当前应用配置
  ApplicationConfig application = new ApplicationConfig();
  application.setName("yyy");

 // 连接注册中心配置
 RegistryConfig registry = new RegistryConfig();
 registry.setAddress("10.20.130.230:9090");
 registry.setUsername("aaa");
 registry.setPassword("bbb");

 // 注意:ReferenceConfig为重对象,内部封装了与注册中心的连接,以及与服务提供方的连接

 // 引用远程服务
 ReferenceConfig<XxxService> reference = new ReferenceConfig<XxxService>(); // 此实例很重,封装了与注册中心的连接以及与提供者的连接,请自行缓存,否则可能造成内存和连接泄漏
 reference.setApplication(application);
 reference.setRegistry(registry); // 多个注册中心可以用setRegistries()
 reference.setInterface(XxxService.class);
 reference.setVersion("1.0.0");

 // 和本地bean一样使用xxxService
 XxxService xxxService = reference.get(); // 注意:此代理对象内部封装了所有通讯细节,对象较重,请缓存复用

四、Dubbo的一些相关疑问

1.Dubbo适用于哪些场景?

当网站变大后,不可避免的需要拆分应用进行服务化,以提高开发效率,调优性能,节省关键竞争资源等。 

 

当服务越来越多时,服务的URL地址信息就会爆炸式增长,配置管理变得非常困难,F5硬件负载均衡器的单点压力也越来越大。 

 

当进一步发展,服务间依赖关系变得错踪复杂,甚至分不清哪个应用要在哪个应用之前启动,架构师都不能完整的描述应用的架构关系。 

 

接着,服务的调用量越来越大,服务的容量问题就暴露出来,这个服务需要多少机器支撑?什么时候该加机器?等等…… 

 

在遇到这些问题时,都可以用Dubbo来解决。 

 

可参见:Dubbo的背景及需求

2.Dubbo的需求和依赖情况?

Dubbo运行JDK1.5之上,缺省依赖javassist、netty、spring等包,但不是必须依赖,通过配置Dubbo可不依赖任何三方库运行。 

 

可参见:用户指南 - 依赖

3.Dubbo的性能如何?

Dubbo通过长连接减少握手,通过NIO及线程池在单连接上并发拼包处理消息,通过二进制流压缩数据,比常规HTTP等短连接协议更快。在阿里巴巴内部,每天支撑2000多个服务,30多亿访问量,最大单机支撑每天近1亿访问量。 

 

可参见:Dubbo性能测试报告

4.和淘宝HSF相比,Dubbo的特点是什么?

  • Dubbo比HSF的部署方式更轻量,HSF要求使用指定的JBoss等容器,还需要在JBoss等容器中加入sar包扩展,对用户运行环境的侵入性大,如果你要运行在Weblogic或Websphere等其它容器上,需要自行扩展容器以兼容HSF的ClassLoader加载,而Dubbo没有任何要求,可运行在任何Java环境中。 

  • Dubbo比HSF的扩展性更好,很方便二次开发,一个框架不可能覆盖所有需求,Dubbo始终保持平等对待第三方理念,即所有功能,都可以在不修改Dubbo原生代码的情况下,在外围扩展,包括Dubbo自己内置的功能,也和第三方一样,是通过扩展的方式实现的,而HSF如果你要加功能或替换某部分实现是很困难的,比如支付宝和淘宝用的就是不同的HSF分支,因为加功能时改了核心代码,不得不拷一个分支单独发展,HSF现阶段就算开源出来,也很难复用,除非对架构重写。 

  • HSF依赖比较多内部系统,比如配置中心,通知中心,监控中心,单点登录等等,如果要开源还需要做很多剥离工作,而Dubbo为每个系统的集成都留出了扩展点,并已梳理干清所有依赖,同时为开源社区提供了替代方案,用户可以直接使用。 

  • Dubbo比HSF的功能更多,除了ClassLoader隔离,Dubbo基本上是HSF的超集,Dubbo也支持更多协议,更多注册中心的集成,以适应更多的网站架构。

5.Dubbo在安全机制方面是如何解决的?

Dubbo主要针对内部服务,对外的服务,阿里有开放平台来处理安全和流控,所以Dubbo在安全方面实现的功能较少,基本上只防君子不防小人,只防止误调用。 

 

Dubbo通过Token令牌防止用户绕过注册中心直连,然后在注册中心上管理授权。Dubbo还提供服务黑白名单,来控制服务所允许的调用方。 

 

可参见:Dubbo的令牌验证

五、注册中心

 image

流程:

  • 1.服务提供者启动时向/dubbo/com.foo.BarService/providers目录下写入URL
  • 2.服务消费者启动时订阅/dubbo/com.foo.BarService/providers目录下的URL向/dubbo/com.foo.BarService/consumers目录下写入自己的URL
  • 3.监控中心启动时订阅/dubbo/com.foo.BarService目录下的所有提供者和消费者URL

支持以下功能:

  • 1.当提供者出现断电等异常停机时,注册中心能自动删除提供者信息。

  • 2.当注册中心重启时,能自动恢复注册数据,以及订阅请求。

  • 3.当会话过期时,能自动恢复注册数据,以及订阅请求。

  • 4.当设置<dubbo:registry check="false" />时,记录失败注册和订阅请求,后台定时重试。

  • 5.可通过<dubbo:registry username="admin" password="1234" />设置zookeeper登录信息。

  • 6.可通过<dubbo:registry group="dubbo" />设置zookeeper的根节点,不设置将使用无根树。

  • 7.支持号通配符<dubbo:reference group="" version="*" />,可订阅服务的所有分组和所有版本的提供者。

注:阿里内部并没有采用Zookeeper做为注册中心,而是使用自己实现的基于数据库的注册中心,即:Zookeeper注册中心并没有在阿里内部长时间运行的可靠性保障,此Zookeeper桥接实现只为开源版本提供,其可靠性依赖于Zookeeper本身的可靠性。

 

更多springboot-dubbo配置可以参考: https://github.com/JeffLi1993/springboot-learning-example/blob/master/springboot-dubbo-server/DubboProperties.md

更多springboot的Demo演示案例: https://github.com/JeffLi1993/springboot-learning-example

 

参考: https://www.jianshu.com/p/0cd474485302

https://blog.51cto.com/developerycj/2050104

https://www.cnblogs.com/study-everyday/p/6742350.html

https://www.jianshu.com/p/a1301e227a4c

https://www.cnblogs.com/chafe/p/5945209.html