dubbo学习-入门

Posted ljming的博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了dubbo学习-入门相关的知识,希望对你有一定的参考价值。

dubbo简介

Apache Dubbo是一款高性能、轻量级的开源 Java RPC 框架,它提供了三大核心能力:

  • 面向接口的远程方法调用;

  • 可靠、智能的容错和负载均衡;

  • 服务自动注册和发现能力。

简单地说, Dubbo 是一个分布式服务框架,致力于提供高性能、透明化的 RPC 远程服务调用方案以及服务治理方案,以帮助我们解决微服务架构落地时的问题。

Dubbo 是由阿里开源,后来加入了 Apache 基金会。目前dubbo最新版本3.0,常用版本为2.7.x。

dubbo架构

节点角色

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

Consumer: 调用远程服务的服务消费者

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

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

Container: 服务运行容器

什么是长连接?

在解释长连接之前, 先看看什么是短连接。短连接分为3个步骤,建立连接 -> 数据传输 -> 断开连接。每次的网络请求,都需要会经历连接和断开事件。

长连接的指建立socket连接后,不管是否使用,都保持连接。经历的阶段一般为,建立连接 -> 数据传输 -> 保持连接 -> 数据传输 -> ..... ->关闭连接。Http请求可以通过设置connection:keep-alive设置,且http1.1默认使用长连接的形式。长连接一般用于操作频繁,点对点的及时通讯。如果每次tcp连接都要经历三次握手建立连接,会比较耗时。基于长连接的形式,处理速度会快很多。

dubbo核心组件

dubbo学习-入门

  • service:业务层。包括业务代码的接口与实现,即开发者实现的业务代码

  • config:配置层。主要围绕ServiceConfig和ReferenceConfig两个实现类展开,初始化配置信息。可以理解为该层管理个整个dubbo的配置。

  • proxy:服务代理层。在dubbo中,无论是生产者还是消费者,框架都会生成一个代理类,整个过程对上层是透明的。

  • registry:注册层。负责dubbo框架的服务注册与发现。

  • cluster:集群容错层。该层主要负责:远程调用失败时的容错策略。选择具体调用节点时的负载均衡策略;特殊调用路径的路由策略。

  • monitor:监控层。这一层主要负责监控统计调用次数和调用时间。

  • protocol:远程调用层。封装RPC调用具体过程,protocol是invoker暴露和引用的主功能入口,它负责管理invoker的整个生命周期。invoker是dubbo的核心模型,框架中所有其他模型都向它靠拢,或者转换成它,它代表一个可执行体。允许向它发起invoker调用,它可能是一个本地的接口实现,也可能是一个远程的实现,还可能是一个集群实现。

  • exchange:信息交换层。建立request-response模型,封装请求响应模式,如把同步请求转换为异步请求。

  • transport:网络传输层。把网络传输抽象为统一的接口,如Mina和Netty虽然接口不一样,但是Dubbo在它们上面又封装了统一的接口,用户也可以根据其扩展接口添加更多的网络传输方式。

  • serialize:序列化层。如果数据要通过网络进行发送,则需要先做序列化,变成二进制流。序列化层负责管理整个框架网络传输时的序列化和反序列化工作。

dubbo配置

dubbo支持多种配置方式,有基于文件的,如: 基于XML配置、基于properties文件配置。有基于api的配置、基于注解的配置、基于环境变量的配置以及基于动态配置中心的配置。

基于XML配置

<!--provider--><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-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<dubbo:application metadata-type="remote" name="demo-provider"/> <dubbo:metadata-report address="zookeeper://127.0.0.1:2181"/>
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<dubbo:protocol name="dubbo"/>
<bean id="demoService" class="org.apache.dubbo.demo.provider.DemoServiceImpl"/>
<dubbo:service interface="org.apache.dubbo.demo.DemoService" ref="demoService"> <dubbo:method name="demo" timeout="3000" loadbalance="random"> <dubbo:parameter key="name" value="ljming"/> </dubbo:method> </dubbo:service>
</beans>
<!--consuemr--><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-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<dubbo:application name="demo-consumer"/>
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<dubbo:reference id="demoService" check="false" interface="org.apache.dubbo.demo.DemoService"/>
</beans>

上面的xml是典型的provider和consumer的xml配置。

  • <dubbo:service/> : 服务配置,用于暴露一个服务,定于服务的元信息,一个服务可以用多个协议暴露,一个服务也可以注册到多个注册中心。

  • <dubbo:reference/>: 引用配置,用户创建一个远程服务代理,一个引用可以指向多个注册中心。

  • <dubbo:protocol/>: 协议配置,用户配置提供服务的协议信息,协议由提供方指定,消费方被动接受。

  • <dubbo:application/>: 应用配置,用于配置当前应用信息,不管该应用是提供者还是消费者。

  • <dubbo:registry/>: 注册中心配置,用于配置连接注册中心相关信息。

  • <dubbo:monitor/>: 监控中心信息,用户配置连接监控中心相关信息,可选。

  • <dubbo:provider/>: 提供方配置,当ProtocolConfig和ServiceConfig某属性没有配置时,可选。

  • <dubbo:consumer/>: 消费方配置,当ReferenceConfig某属性没有配置时,采用此缺省值,可选。

  • <dubbo:method/>: 方法配置,用户ServiceConfig和ReferenceConfig指定方法及的配置信息。

  • <dobbo:arguemnt/>: 参数配置,用于指定方法参数配置。

不同粒度配置的覆盖关系

覆盖关系有两个基本原则:

  • 方法级优先,接口级次之,全局配置再次之。

  • 如果级别一样,则消费者优先,提供方次之。

以timeout为例,下图展示了配置的覆盖关系,其中,服务提供方配置,通过URL经由注册中心传递给消费方。

dubbo学习-入门

建议: 由服务提供方设置超时,因为一个方法需要执行多长时间,服务提供者更清楚,如果一个消费方同时引用多个服务,就不需要关系每个服务的超时设置。理论上ReferenceConfig中除了interface这一项,其它所有配置都可以缺省不配置,框架会自动使用ConsumerConfig,ServiceConfig,ProviderConfig等提供的缺省配置。

基于properties属性文件配置

dubbo可以自动加载classpath跟目录下的dubbo.properties,同样也可以使用jvm参数来指定路径: -Dubbo.properties.file=xxx.properties。

# provider
dubbo.application.id=demo-provider
dubbo.application.name=demo-provider
dubbo.registry.address=zookeeper://localhost:2181
dubbo.protocol.name=dubbo
 
# consumer
dubbo.application.id=demo-consumer
dubbo.application.name=demo-consumer
dubbo.registry.address=zookeeper://localhost:2181
dubbo.protocol.name=dubbo
dubbo.consumer.loadbalance=roundrobin

和xml的配置相比,dubbo.application.name=demo-provider相当于<dubbo:application name="demo-provider">dubbo.registry.address=zookeeper://localhost:2181相当于<dubbo:registry address="zookeeper://localhost:2181">

优先级

dubbo学习-入门

优先级从高到低:

  • jvm -D 参数: 当启动时,可以轻易地重写配置,比如,改变了dubbo协议的端口。

  • xml: xml中的当前配置会重写dubbo.properties文件中的配置。

  • properties: 默认配置,仅仅作用于以上两者没有配置时。

基于注解的配置

dubbo 2.6.3及以上版本支持以注解的方式完成配置。

  • 使用@EnableDubbo注解,指定spring扫描指定包路径下的dubbo服务

    @EnableDubbo(scanBasePackages = "com.xxx")
    public class DubboProviderApplication {

       public static void main(String[] args) {
           SpringApplication.run(DubboProviderApplication.class, args);
           System.out.println("start finished");
      }
    }
  • 使用 org.apache.dubbo.config.annotatio包下的 @Service 注解暴露一个dubbo服务

    @Service(provider = "demoService", version = "1.0.0", timeout = 3000)public class DemoServiceImpl implements DemoService {
    @Override public void demo() { System.out.println("hello dubbo"); }}

    为了不和 org.springframework.stereotyp 包下的 @Service 注解产生冲突,在dubbo2.7.7版本及以后,org.apache.dubbo.config.annotation 包下的 @Service 注解被标注为已废弃,改用@DubboService替代。

  • 使用 org.apache.dubbo.config.annotatio 包下的 @Reference 注解引用dubbo服务

    @RestControllerpublic class DemoController { @Reference(interfaceClass = DemoService.class, loadbalance = "consistenthash") private DemoService demoService;}

    在dubbo2.7.7版本及以后,org.apache.dubbo.config.annotation 包下的 @Reference 注解被标注为已废弃,改用@DubboReference替代。

其它几种不常用配置方式

在实际开发中,常用的配置方式一般以xml,properties以及注解的方式进行dubbo配置。

api的配置形式

dubbo的api属性和配置项是一一对应的,那ApplicationConfig为例:

ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("demo-provider");

api配置的方式不建议使用,因为需要通过大量的编码来完成配置,而且ReferenceConfig实例很重,它内部封装了和注册中心的连接以及服务提供者的连接,还涉及到连接信息的缓存,如果不进行缓存,可能造成内存和连接的泄漏。api的使用范围一般是OpenAPI,ESB,Test,Mock等系统集成。实际开发场景,建议使用xml的配置方式。

其对应的是<dubbo:application name="demo-provider"/>

使用配置中心

可以使用业界比较成熟的分布式配置中心如 Apollo、Nacos等。在内容上和dubbo.properties无差别。默认配置中心的优先级高于本地配置

环境变量加载

dubbo2.7.3版本及以后,dubbo会自动从约定的key中读取配置,并将配置以K-V的形式写入到URL中。支持的key有以下两个:

  • dubbo.labels: 指定一些配置到URL中的键值对,通常通过java -D或系统变量指定

    # jvm-Ddubbo.labels="tag1=value1;tag2=value2"# 环境变量DUBBO_LABELS="tag1=value1;tag2=value2"

    最终会生成的URL会包含tag1、tag2两个key: dubbo://xxx?tag1=value2&tag2=value2

  • duubbo.env.keys: 指定环境变量key值,dubbo会尝试从环境变量加载每个key值

    # JVM-Ddubbo.env.keys = "DUBBO_TAG1, DUBBO_TAG2"# 环境变量DUBBO_ENV_KEYS = "DUBBO_TAG1, DUBBO_TAG2"

    最终生成的 URL 会包含 DUBBO_TAG1、DUBBO_TAG2 两个 key: dubbo://xxx?DUBBO_TAG1=value1&DUBBO_TAG2=value2

配置加载流程

dubbo的配置读取总体上遵循了以下几个原则:

  • dubbo支持了多层级的配置,并按预先优先级自动实现配置间的覆盖,最终所有配置汇总到数据总线URL后驱动后续的服务暴露、引用等流程。

  • ApplicationConfig、ServiceConfig、ReferenceConfig 可以被理解为配置来源的一种,是直接面向用户编程的配置采集方式。

  • 配置格式以properties为主,在配置内容上以遵循path-based的命名规范。

配置来源

首先,从Dubbo支持的配置来源说起,默认有四种配置来源:

  • JVM System Properties,-D 参数

  • Externalized Configuration,外部化配置

  • ServiceConfig、ReferenceConfig 等编程接口采集的配置

  • 本地配置文件 dubbo.properties

覆盖关系

下图展示了配置覆盖关系的优先级,从上到下优先级依次降低:


常用配置示例

启动时检查

Dubbo默认设置check=true,在启动时检查服务是否可用,不可用时会抛异常,阻止spring的初始化,这样做的好处,在于上线时,能及早的发现问题。但是如果在测试环境,某些服务不关系,或者出现循环依赖,必须有一方先启动,可以设置check=false来关闭检查。

另外,如spring容器是懒加载的,或者是通过api编程延迟引用服务,请关闭check,否则服务临时不可用时,会抛异常,拿到null引用,如果设置check=false,总是会返回引用,当服务恢复时,能自动连上。

# 关闭某个服务的启动时检查 (没有提供者时报错):
<dubbo:reference interface="com.foo.BarService" check="false" />
# 关闭所有服务的启动时检查 (没有提供者时报错):
<dubbo:consumer check="false" />
# 关闭注册中心启动时检查 (注册订阅失败时报错):
<dubbo:registry check="false" />

dubbo.registry.check=false,前面两个都是指订阅成功,但提供者列表是否为空是否报错,如果注册订阅失败时,也允许启动,需使用此选项,将在后台定时重试。

dubbo.consumer.check=false,是设置 check 的缺省值,如果配置中有显式的声明,如:<dubbo:reference check="true"/>,不会受影响。

dubbo.reference.check=false,强制改变所有 reference 的 check 值,就算配置中有声明,也会被覆盖。

集群容错

dubbo提供一下集中集群容错实现,用户也可以使用dubbo的spi机制,对其进行扩展。

<dubbo:service cluster="failsafe" />
<dubbo:reference cluster="failsafe" />
  • Failover cluster: duboo缺省配置。失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长的延迟。可以通过retries=2来设置重试次数(不包含第一次)。

    <dubbo:service retries="2" />
  • Broadcast cluster: 广播调用。广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知服务提供者更新缓存或者日志等本地资源信息。

  • ForKing cluster: 并行调用。只要一个成功即返回结果。通常用于实时性较高的读操作。但需要浪费很多服务资源,可以通过forks="2"来设置最大并行数。

  • Failback cluster: 失败自动回复。后台记录失败请求,定时重发。通常用于消息通知等操作。

  • Failsafe cluster: 失败安全。发生异常,直接忽略。通常用于写入审计日志等操作。

  • Failfast cluster: 快速失败。只发起一次请求,失败立即报错。通常使用非幂等行的写操作,比如新增记录。

负载均衡

在集群负载均衡配置上,dubbo提供了多种策略,缺省为random随机调用。也可以使用dubbo的spi机制,对其进行扩展。

random loadbalance

  • 随机,按权重设置随机概率。

  • 在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。

roundRobin loadbalance

  • 轮询,按公约后的权重设置轮询比率。

  • 存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。

LeastActive LoadBalance

  • 最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。

  • 使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。

ConsistentHash LoadBalance

  • 一致性 Hash,相同参数的请求总是发到同一提供者。

  • 当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。

  • 算法参见:http://en.wikipedia.org/wiki/Consistent_hashing

  • 缺省只对第一个参数 Hash,如果要修改,请配置 <dubbo:parameter key="hash.arguments" value="0,1" />

  • 缺省用 160 份虚拟节点,如果要修改,请配置 <dubbo:parameter key="hash.nodes" value="320" />

线程模型

dubbo提供了不同的派发策略和不同的线程池配置来应对不同的事件处理请求。

  • 如果事件处理的逻辑能迅速完成,并不会发起新的IO的请求,比如只是在内存中几个标识,则直接在IO线程上处理更快,因为减少了线程池调度。

  • 如果事件处理逻辑较慢,或者需要发起新的IO请求,比如查询数据库,则必须派发到线程池,否则IO线程阻塞,将导致不能接受其它请求。

  • 如果使用IO线程处理事件,又在事件处理过程中发起新的IO请求,比如在连接事件中发起登录事件,会报”可能引发死锁“异常,但不会真的死锁。

我们可以通过以下方式配置dubbo的线程模型:

<dubbo:protocol name="dubbo" dispatcher="all" threadpool="fixed" threads="100" />

dispatcher

  • all: 表示所有事件都派发到线程池,包括请求、响应、连接、断开、心跳等。

  • direct: 所有消息都不派发到线程池,全部在IO线程上执行。

  • message: 只有请求响应事件派发到线程池,其它连接、断开、心跳等消息,直接在IO线程上执行。

  • execution: 只有请求消息派发到线程池,不包含响应事件。响应事件和其它连接断开,心跳等事件,直接在IO线程上执行。

  • connection: 在IO线程上,将连接断开事件放入队列,有序逐个执行,其它消息派发到线程池。

threadpool

  • fixed: 固定大小线程池,启动时创建好,不关闭,一直持有。(缺省配置)

  • cache: 缓存线程池,空闲一分钟自动删除,需要时重建。

  • limited: 可伸缩线程池,但池中的线程数只会增长但不会收缩。只增长不收缩的目的是为了避免收缩时突然来了大流量引起性能问题。

  • eager: 优先创建Worker线程池。在任务数量大于corePoolSize但是小于maximumPoolSize时,优先创建Woker来处理任务。当任务数量大于maximumPoolSize时,将任务放入阻塞队列中。阻塞队列充满时抛出RejectExecutionException。

直连提供者

在开发及测试环境下,经常需要绕过注册中心,只测试指定服务提供者,这时候可能需要点对点直连,点对点直连方式,将以服务接口为单位,忽略注册中心的提供者列表,A接口配置点对点,不影响B接口从注册中心获取列表。

<dubbo:referenceid="xxxService"interface="com.apache.xxx.XxxService"url="dubbo://localhost:20890"/>

服务分组

使用服务分组区分服务接口的不同的实现。当一个接口有多种实现,可以用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" />

事件通知

在调用之前、调用之后、出现异常时,会出发oninvoke、onreturn、onthrow三个事件,可以配置当事件发生时,通知那个类的哪个方法。

<bean id ="notify" class = "com.xxx.xxx.service.impl.NotifyImpl" />
<dubbo:reference id="demoService" interface="com.xxx.xxx.dubboservice.DemoService" version="1.0.0">
     <dubbo:method name="demo" async="true" onreturn = "notify.onreturn" onthrow="notify.onthrow" />
</dubbo:reference>

@Service(value = "notify")
public class NotifyImpl implements Notify {
   public Map<Integer, String> ret = new HashMap<>();
   public Map<Integer, Throwable> errors = new HashMap<>();

   @Override
   public void onreturn(String msg, Integer id) {
      System.out.println("onreturn:" + msg);
       ret.put(id, msg);
  }

   @Override
   public void onthrow(Throwable t, Integer id) {
       errors.put(id, t);
  }
}

asnyc表示结果是否立即返回,默认为false;onreturn表示是否需要回调。

优雅停机

dubbo是通过jdk的shutdownhook来完成优雅停机的,所有如果用户使用 kill -9 pid 等强制关闭指令,是不会执行优雅停机的。只能通过kill pid时,才会执行。

原理

服务提供方

  • 停止时,先标记为不接收新请求,新请求过来时直接报错,让客户端重试其它机器。

  • 然后,检测线程池中的线程是否正在执行,如果有,等到所有线程执行完成,除非超时,则强制关闭。

服务消费方

  • 停止时,不再发起新的调用请求,所有新的调用在客户端即报错。

  • 然后,检测有没有请求的响应还没有返回,等待响应返回,除非超时,则强制关闭。

设置优雅停机超时时间,缺省默认时间是10s,如果超时则强制关闭。

dubbo.service.shutdown.wait=10000

更多配置实例请看 dubbo官方文档

dubbo推荐用法

  1. 在provider端尽量多的配置consumer端属性

    作为服务提供方,比服务消费方更清楚服务的性能参数,如调用的超时时间、合理的重试次数等。

    在provider端配置后,consumer端不配置则会使用provider端的配置,即provider端配置可以作为consumer端的缺省值。否则的话,consumer端会使用consumer的全局设置,这对provider是不可控的,并且往往是合理的。

    建议在provider端配置的consumer端属性有:

    ①:timeout: 方法调用的超时时间

    :retries: 失败重试次数,缺省是2

    :loadbalance: 负载均衡算法,缺省是随机random。还可以配置轮询roundrobin、leastactive、consistenhash等。

    :actives: 消费者端的最大并发调用限制,即当consumer对一个服务的并发调用到上限后,新调用会阻塞知道超时,在方法上配置 dubbo:method 则对该方法进行并发限制。在接口配置 dubbo:service,则针对该服务进行并发限制。

  2. 在provider端配置合理的provider端属性

    建议在provider端配置的provider端属性有:

    : threads: 服务线程大小

    ②: executes: 一个服务提供者并行执行请求上限,即当provider对一个服务的并发调用达到上限后,新调用会阻塞,此时consumer可能会超时,在方法上配置 dubbo:method 则对该方法进行并发限制。在接口配置 dubbo:service,则针对该服务进行并发限制。

  3. 配置dubbo缓存文件

    配置dubbo缓存文件的作用在于: 缓存文件会缓存注册中心列表和服务提供者列表,应用在启动过程中,若注册中心不可用,应用会从缓存文件读取服务提供者列表,进一步保证应用的可靠性。

    <dubbo:registry file=”${user.home}/output/dubbo.cache” />

    注意:

    可以根据需要调整缓存文件的路径,保证这个文件不会在发布过成功中被清除掉。

    如果有多个应用进程,注意不要共用同一个文件,避免内容被覆盖。

  4. 监控配置

    ②: 使用dubbo admin监控注册中心上的服务提供方。

    ③: 服务提供方可使用dubbo的telnet或shell监控项。例如,监控服务提供者端口状态: echo status|nc -i 1 20880|grep OK |wc -l。20880为服务端口。

    ④: 服务消费方可通过将服务强制转型为EchoService,并调用$echo()测试该服务的提供者是否可用。

     public String echo() { EchoService echoService = (EchoService) demoService; return (String) echoService.$echo("ok"); }


  5. 推荐使用xml配置而非dubbo.properties文件配置

    从上面的配置加载流程可知,dubbo.properties文件的优先级

以上学习内容均从dubbo官方文档整理摘录。

以上是关于dubbo学习-入门的主要内容,如果未能解决你的问题,请参考以下文章

dubbo学习-入门

dubbo入门学习-----dubbo的高可用

dubbo入门学习

dubbo学习--简单的入门搭建实例

Dubbo -- 系统学习 笔记 -- 入门

2018.11.9 Dubbo入门学习