Dubbo开发优化注意事项

启动时检查

    Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题,默认 check="true"。另外,若是你的 Spring 容器是懒加载的,或者经过 API 编程延迟引用服务,请关闭 check,不然服务临时不可用时,会抛出异常,拿到 null 引用,若是 check="false",老是会返回引用,当服务恢复时,能自动连上。html

集群容错模式

Failover Cluster 失败自动切换,当出现失败,重试其它服务器 。一般用于读操做,但重试会带来更长延迟。可经过 retries="2" 来设置重试次数(不含第一次)。java

Failfast Cluster 快速失败,只发起一次调用,失败当即报错。用于非幂等性的写操做,好比新增记录。node

Failsafe Cluster失败安全,出现异常时,直接忽略。一般用于写入审计日志等操做。算法

Failback Cluster失败自动恢复,后台记录失败请求,定时重发。一般用于消息通知操做。spring

Forking Cluster并行调用多个服务器,只要一个成功即返回。一般用于实时性要求较高的读操做,但须要浪费更多服务资源。可经过 forks="2" 来设置最大并行数。apache

Broadcast Cluster广播调用全部提供者,逐个调用,任意一台报错则报错 2。一般用于通知全部提供者更新缓存或日志等本地资源信息。编程

负载均衡

在集群负载均衡时,Dubbo 提供了多种均衡策略,缺省为random随机调用。api

Random LoadBalance缓存

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

RoundRobin LoadBalancetomcat

  • 轮循,按公约后的权重设置轮循比率。
  • 存在慢的提供者累积请求的问题,好比:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,长此以往,全部请求都卡在调到第二台上。

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:protocol name="dubbo" dispatcher="all" threadpool="fixed" threads="100" />

Dispatcher

  • all 全部消息都派发到线程池,包括请求,响应,链接事件,断开事件,心跳等。
  • direct 全部消息都不派发到线程池,所有在 IO 线程上直接执行。
  • message 只有请求响应消息派发到线程池,其它链接断开事件,心跳等消息,直接在 IO 线程上执行。
  • execution 只请求消息派发到线程池,不含响应,响应和其它链接断开事件,心跳等消息,直接在 IO 线程上执行。
  • connection 在 IO 线程上,将链接断开事件放入队列,有序逐个执行,其它消息派发到线程池。

ThreadPool

  • fixed 固定大小线程池,启动时创建线程,不关闭,一直持有。(缺省)
  • cached 缓存线程池,空闲一分钟自动删除,须要时重建。
  • limited 可伸缩线程池,但池中的线程数只会增加不会收缩。只增加不收缩的目的是为了不收缩时忽然来了大流量引发的性能问题。
  • eager 优先建立Worker线程池。在任务数量大于corePoolSize可是小于maximumPoolSize时,优先建立Worker来处理任务。当任务数量大于maximumPoolSize时,将任务放入阻塞队列中。阻塞队列充满时抛出RejectedExecutionException。(相比于cached:cached在任务数量超过maximumPoolSize时直接抛出异常而不是将任务放入阻塞队列)

直连提供者

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

经过 XML 配置

<dubbo:reference id="xxxService" interface="com.alibaba.xxx.XxxService" url="dubbo://localhost:20890" />

 经过 -D 参数指定

在 JVM 启动参数中加入-D参数映射服务地址 ,如:

java -Dcom.alibaba.xxx.XxxService=dubbo://localhost:20890

 

只订阅

    为方便开发测试,常常会在线下共用一个全部服务可用的注册中心,这时,若是一个正在开发中的服务提供者注册,可能会影响消费者不能正常运行。可让服务提供者开发方,只订阅服务(开发的服务可能依赖其它服务),而不注册正在开发的服务,经过直连测试正在开发的服务。

禁用注册配置

<dubbo:registry address="10.20.153.10:9090" register="false" />

或者

<dubbo:registry address="10.20.153.10:9090?register=false" />

 

只注册

    若是有两个镜像环境,两个注册中心,有一个服务只在其中一个注册中心有部署,另外一个注册中心还没来得及部署,而两个注册中心的其它应用都须要依赖此服务。这个时候,可让服务提供者方只注册服务到另外一注册中心,而不从另外一注册中心订阅服务。

禁用订阅配置

<dubbo:registry id="hzRegistry" address="10.20.153.10:9090" />
<dubbo:registry id="qdRegistry" address="10.20.141.150:9090" subscribe="false" />

或者

<dubbo:registry id="hzRegistry" address="10.20.153.10:9090" />
<dubbo:registry id="qdRegistry" address="10.20.141.150:9090?subscribe=false" />

 

多协议

Dubbo 容许配置多协议,在不一样服务上支持不一样协议或者同一服务上同时支持多种协议。

不一样服务在性能上适用不一样协议进行传输,好比大数据用短链接协议,小数据大并发用长链接协议。

<!-- 多协议配置 -->
    <dubbo:protocol name="dubbo" port="20880" />
    <dubbo:protocol name="hessian" port="8080" />
    <!-- 使用多个协议暴露服务 -->
    <dubbo:service id="helloService" interface="com.alibaba.hello.api.HelloService" version="1.0.0" protocol="dubbo,hessian" />
</beans>

 

多注册中心

    Dubbo 支持同一服务向多注册中心同时注册,或者不一样服务分别注册到不一样的注册中心上去,甚至能够同时引用注册在不一样注册中心上的同名服务。另外,注册中心是支持自定义扩展的 。

<!-- 多注册中心配置 -->
<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" />

 

服务分组

当一个接口有多种实现时,能够用 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" />

任意组 1

<dubbo:reference id="barService" interface="com.foo.BarService" group="*" />

 

多版本

当一个接口实现,出现不兼容升级时,能够用版本号过渡,版本号不一样的服务相互间不引用。

能够按照如下的步骤进行版本迁移:

  1. 在低压力时间段,先升级一半提供者为新版本
  2. 再将全部消费者升级为新版本
  3. 而后将剩下的一半提供者升级为新版本

version控制

分组聚合

    按组合并返回结果,好比菜单服务,接口同样,但有多种实现,用group区分,如今消费方需从每种group中调用一次返回结果,合并结果返回,这样就能够实现聚合菜单项。

搜索全部分组

<dubbo:reference interface="com.xxx.MenuService" group="*" merger="true" />

合并指定分组

<dubbo:reference interface="com.xxx.MenuService" group="aaa,bbb" merger="true" />

指定方法合并结果,其它未指定的方法,将只调用一个 Group

<dubbo:reference interface="com.xxx.MenuService" group="*">
    <dubbo:method name="getMenuItems" merger="true" />
</dubbo:service>

某个方法不合并结果,其它都合并结果

<dubbo:reference interface="com.xxx.MenuService" group="*" merger="true">
    <dubbo:method name="getMenuItems" merger="false" />
</dubbo:service>

指定合并策略,缺省根据返回值类型自动匹配,若是同一类型有两个合并器时,需指定合并器的名称 2

<dubbo:reference interface="com.xxx.MenuService" group="*">
    <dubbo:method name="getMenuItems" merger="mymerge" />
</dubbo:service>

指定合并方法,将调用返回结果的指定方法进行合并,合并方法的参数类型必须是返回结果类型自己

<dubbo:reference interface="com.xxx.MenuService" group="*">
    <dubbo:method name="getMenuItems" merger=".addAll" />
</dubbo:service>

结果缓存

    结果缓存,用于加速热门数据的访问速度,Dubbo 提供声明式缓存,以减小用户加缓存的工做量。

 缓存类型

  • lru 基于最近最少使用原则删除多余缓存,保持最热的数据被缓存。
  • threadlocal 当前线程缓存,好比一个页面渲染,用到不少 portal,每一个 portal 都要去查用户信息,经过线程缓存,能够减小这种多余访问。
  • jcache 与 JSR107 集成,能够桥接各类缓存实现。

配置

<dubbo:reference interface="com.foo.BarService" cache="lru" />

或:

<dubbo:reference interface="com.foo.BarService">
    <dubbo:method name="findBar" cache="lru" />
</dubbo:reference>

 

隐式参数

    能够经过 RpcContext 上的 setAttachment 和 getAttachment 在服务消费方和提供方之间进行参数的隐式传递。

/user-guide/images/context.png

异步调用

    基于 NIO 的非阻塞实现并行调用,客户端不须要启动多线程便可完成并行调用多个远程服务,相对多线程开销较小。

/user-guide/images/future.jpg

在 consumer.xml 中配置:

<dubbo:reference id="fooService" interface="com.alibaba.foo.FooService">
      <dubbo:method name="findFoo" async="true" />
</dubbo:reference>

设置是否等待消息发出: 

  • sent="true" 等待消息发出,消息发送失败将抛出异常。
  • sent="false" 不等待消息发出,将消息放入 IO 队列,即刻返回。
<dubbo:method name="findFoo" async="true" sent="true" />

若是只是想异步,彻底忽略返回值,能够配置 return="false",以减小 Future 对象的建立和管理成本:

<dubbo:method name="findFoo" async="true" return="false" />

本地调用

    本地调用使用了 injvm 协议,是一个伪协议,它不开启端口,不发起远程调用,只在 JVM 内直接关联,但执行 Dubbo 的 Filter 链。

配置

定义 injvm 协议

<dubbo:protocol name="injvm" />

设置默认协议

<dubbo:provider protocol="injvm" />

设置服务协议

<dubbo:service protocol="injvm" />

优先使用 injvm

<dubbo:consumer injvm="true" .../>
<dubbo:provider injvm="true" .../>

<dubbo:reference injvm="true" .../>
<dubbo:service injvm="true" .../>

注意:服务暴露与服务引用都须要声明 injvm="true"

 

事件通知

    在调用以前、调用以后、出现异常时,会触发 oninvokeonreturnonthrow 三个事件,能够配置当事件发生时,通知哪一个类的哪一个方法 。

服务消费者 Callback 配置

<bean id ="demoCallback" class = "com.alibaba.dubbo.callback.implicit.NofifyImpl" />
<dubbo:reference id="demoService" interface="com.alibaba.dubbo.callback.implicit.IDemoService" version="1.0.0" group="cn" >
      <dubbo:method name="get" async="true" onreturn = "demoCallback.onreturn" onthrow="demoCallback.onthrow" />
</dubbo:reference>

callback 与 async 功能正交分解,async=true 表示结果是否立刻返回,onreturn 表示是否须要回调。

二者叠加存在如下几种组合状况 :

  • 异步回调模式:async=true onreturn="xxx"
  • 同步回调模式:async=false onreturn="xxx"
  • 异步无回调 :async=true
  • 同步无回调 :async=false

本地存根

    远程服务后,客户端一般只剩下接口,而实现全在服务器端,但提供方有些时候想在客户端也执行部分逻辑,好比:作 ThreadLocal 缓存,提早验证参数,调用失败后伪造容错数据等等,此时就须要在 API 中带上 Stub,客户端生成 Proxy 实例,会把 Proxy 经过构造函数传给 Stub 1,而后把 Stub 暴露给用户,Stub 能够决定要不要去调 Proxy。

/user-guide/images/stub.jpg

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

<dubbo:service interface="com.foo.BarService" stub="true" />

<dubbo:service interface="com.foo.BarService" stub="com.foo.BarServiceStub" />

Demo

package com.foo;
public class BarServiceStub implements BarService { 
    private final BarService barService;

    // 构造函数传入真正的远程代理对象
    public (BarService barService) {
        this.barService = barService;
    }

    public String sayHello(String name) {
        // 此代码在客户端执行, 你能够在客户端作ThreadLocal本地缓存,或预先验证参数是否合法,等等
        try {
            return barService.sayHello(name);
        } catch (Exception e) {
            // 你能够容错,能够作任何AOP拦截事项
            return "容错数据";
        }
    }
}

 

本地假装

本地假装 1 一般用于服务降级,好比某验权服务,当服务提供方所有挂掉后,客户端不抛出异常,而是经过 Mock 数据返回受权失败。

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

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

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

Demo 在工程中提供 Mock 实现 :

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

 

延迟暴露

若是你的服务须要预热时间,好比初始化缓存,等待相关资源就位等,可使用 delay 进行延迟暴露。

延迟 5 秒暴露服务

<dubbo:service delay="5000" />

延迟到 Spring 初始化完成后,再暴露服务 1

<dubbo:service delay="-1" />

 

并发控制

务器端并发执行(或占用线程池线程数)不能超过 10 个:

<dubbo:service interface="com.foo.BarService" executes="10" />

指定方法服务器端并发执行(或占用线程池线程数)不能超过 10 个:

<dubbo:service interface="com.foo.BarService">
    <dubbo:method name="sayHello" executes="10" />
</dubbo:service>

客户端并发执行(或占用链接的请求数)不能超过 10 个:

<dubbo:service interface="com.foo.BarService" actives="10" />
<dubbo:reference interface="com.foo.BarService" actives="10" />

Load Balance 均衡

配置服务的客户端的 loadbalance 属性为 leastactive,此 Loadbalance 会调用并发数最小的 Provider(Consumer端并发数)。

<dubbo:reference interface="com.foo.BarService" loadbalance="leastactive" />
<dubbo:service interface="com.foo.BarService" loadbalance="leastactive" />

 

链接控制

服务端链接控制

限制服务器端接受的链接不能超过 10 个 1

<dubbo:provider protocol="dubbo" accepts="10" />

<dubbo:protocol name="dubbo" accepts="10" />

客户端链接控制

限制客户端服务使用链接不能超过 10 个 2

<dubbo:reference interface="com.foo.BarService" connections="10" />

<dubbo:service interface="com.foo.BarService" connections="10" />

若是 <dubbo:service> 和 <dubbo:reference> 都配了 connections,<dubbo:reference> 优先

 

延迟链接

延迟链接用于减小长链接数。当有调用发起时,再建立长链接。1

<dubbo:protocol name="dubbo" lazy="true" />
1. 注意:该配置只对使用长链接的 dubbo 协议生效

 

粘滞链接

粘滞链接用于有状态服务,尽量让客户端老是向同一提供者发起调用,除非该提供者挂了,再连另外一台。

粘滞链接将自动开启延迟链接,以减小长链接数。

<dubbo:protocol name="dubbo" sticky="true" />

 

令牌验证

    经过令牌验证在注册中心控制权限,以决定要不要下发令牌给消费者,能够防止消费者绕过注册中心访问提供者,另外经过注册中心可灵活改变受权方式,而不需修改或升级提供者

/user-guide/images/dubbo-token.jpg

能够全局设置开启令牌验证:

<!--随机token令牌,使用UUID生成-->
<dubbo:provider interface="com.foo.BarService" token="true" />

<!--固定token令牌,至关于密码-->
<dubbo:provider interface="com.foo.BarService" token="123456" />

还可在服务级别、协议级别设置:

优雅停机

    Dubbo 是经过 JDK 的 ShutdownHook 来完成优雅停机的,因此若是用户使用 kill -9 PID 等强制关闭指令,是不会执行优雅停机的,只有经过 kill PID 时,才会执行。

原理

服务提供方

  • 中止时,先标记为不接收新请求,新请求过来时直接报错,让客户端重试其它机器。
  • 而后,检测线程池中的线程是否正在运行,若是有,等待全部线程执行完成,除非超时,则强制关闭。

服务消费方

  • 中止时,再也不发起新的调用请求,全部新的调用在客户端即报错。
  • 而后,检测有没有请求的响应尚未返回,等待响应返回,除非超时,则强制关闭。

设置方式

设置优雅停机超时时间,缺省超时时间是 10 秒,若是超时则强制关闭。

# dubbo.properties
dubbo.service.shutdown.wait=15000

若是 ShutdownHook 不能生效,能够自行调用,使用tomcat等容器部署的場景,建议经过扩展ContextListener等自行调用如下代码实现优雅停机

ProtocolConfig.destroyAll();

 

访问日志

若是你想记录每一次请求信息,可开启访问日志,相似于apache的访问日志。注意:此日志量比较大,请注意磁盘容量。

将访问日志输出到当前应用的log4j日志:

<dubbo:protocol accesslog="true" />

将访问日志输出到指定文件:

<dubbo:protocol accesslog="http://10.20.160.198/wiki/display/dubbo/foo/bar.log" />

 

服务容器

    服务容器是一个 standalone 的启动程序,由于后台服务不须要 Tomcat 或 JBoss 等 Web 容器的功能,若是硬要用 Web 容器去加载服务提供方,增长复杂性,也浪费资源。

    服务容器只是一个简单的 Main 方法,并加载一个简单的 Spring 容器,用于暴露服务。

    服务容器的加载内容能够扩展,内置了 spring, jetty, log4j 等加载,可经过容器扩展点进行扩展。配置配在 java 命令的 -D 参数或者 dubbo.properties 中。

容器类型

Spring Container

  • 自动加载 META-INF/spring 目录下的全部 Spring 配置。
  • 配置 spring 配置加载位置:

    dubbo.spring.config=classpath*:META-INF/spring/*.xml

Jetty Container

  • 启动一个内嵌 Jetty,用于汇报状态。
  • 配置:
    • dubbo.jetty.port=8080:配置 jetty 启动端口
    • dubbo.jetty.directory=/foo/bar:配置可经过 jetty 直接访问的目录,用于存放静态文件
    • dubbo.jetty.page=log,status,system:配置显示的页面,缺省加载全部页面

Log4j Container

  • 自动配置 log4j 的配置,在多进程启动时,自动给日志文件按进程分目录。
  • 配置:
    • dubbo.log4j.file=/foo/bar.log:配置日志文件路径
    • dubbo.log4j.level=WARN:配置日志级别
    • dubbo.log4j.subdirectory=20880:配置日志子目录,用于多进程启动,避免冲突

容器启动

缺省只加载 spring

java com.alibaba.dubbo.container.Main

经过 main 函数参数传入要加载的容器

java com.alibaba.dubbo.container.Main spring jetty log4j

经过 JVM 启动参数传入要加载的容器

java com.alibaba.dubbo.container.Main -Ddubbo.container=spring,jetty,log4j

经过 classpath 下的 dubbo.properties 配置传入要加载的容器

dubbo.container=spring,jetty,log4j
相关文章
相关标签/搜索