Dubbo初识

http://dubbo.apache.org/zh-cn/docs/user/quick-start.html

概念

一款轻量级的rpc框架,⼜具备了服务治理功能。提供三个主要功能:

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

架构

在这里插入图片描述

节点角色说明:

节点角色说明
Provider暴露服务的服务提供方
Consumer调用远程服务的服务消费方
Registry服务注册与发现的注册中心
Monitor统计服务的调用次数和调用时间的监控中心
Container服务运行容器

调用关系说明:

  1. 服务容器负责启动,加载,运行服务提供者。
  2. 服务提供者在启动时,向注册中心注册自己提供的服务。
  3. 服务消费者在启动时,向注册中心订阅自己所需的服务。
  4. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
  5. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
  6. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

整体设计

整体架构设计

领域模型

在 Dubbo 的核心领域模型中:

  • Protocol 是服务域,它是 Invoker 暴露和引用的主功能入口,它负责 Invoker 的生命周期管理。
  • Invoker 是实体域,它是 Dubbo 的核心模型,其它模型都向它靠扰,或转换成它,它代表一个可执行体,可向它发起 invoke 调用,它有可能是一个本地的实现,也可能是一个远程的实现,也可能一个集群实现。
  • Invocation 是会话域,它持有调用过程中的变量,比如方法名,参数等。

标签配置

标签种类

标签用途解释
<dubbo:service/>服务配置用于暴露一个服务,定义服务的元信息,一个服务可以用多个协议暴露,一个服务也可以注册到多个注册中心
<dubbo:reference/>引用配置用于创建一个远程服务代理,一个引用可以指向多个注册中心
<dubbo:protocol/>协议配置用于配置提供服务的协议信息,协议由提供方指定,消费方被动接受
<dubbo:application/>应用配置用于配置当前应用信息,不管该应用是提供者还是消费者
<dubbo:module/>模块配置用于配置当前模块信息,可选
<dubbo:registry/>注册中心配置用于配置连接注册中心相关信息
<dubbo:monitor/>监控中心配置用于配置连接监控中心相关信息,可选
<dubbo:provider/>提供方配置当 ProtocolConfig 和 ServiceConfig 某属性没有配置时,采用此缺省值,可选
<dubbo:consumer/>消费方配置当 ReferenceConfig 某属性没有配置时,采用此缺省值,可选
<dubbo:method/>方法配置用于 ServiceConfig 和 ReferenceConfig 指定方法级的配置信息
<dubbo:argument/>参数配置用于指定方法参数配置

在这里插入图片描述

配置加载流程

在这里插入图片描述

覆盖策略:

  1. JVM启动-D参数优先,这样可以使用户在部署和启动时进行参数重写,比如在启动时需改变协议的端口。
  2. XML次之,如果在XML中有配置,则dubbo.properties中的相应配置项无效。
  3. Properties最后,相当于缺省值,只有XML没有配置时,dubbo.properties的相应配置项才会生效,通常用于共享公共配置,比如应用名。

标签配置项

这里以Xml配置为准,只有group,interface,version是服务的匹配条件,三者决定是不是同一个服务,其它配置项均为调优和治理参数。

所有配置项分为三大类:

  • 服务发现:表示该配置项用于服务的注册与发现,目的是让消费方找到提供方。
  • 服务治理:表示该配置项用于治理服务间的关系,或为开发测试提供便利条件。
  • 性能调优:表示该配置项用于调优性能,不同的选项对性能会产生影响。

所有配置最终都将转换为URL表示,并由服务提供方生成,经注册中心传递给消费方。

XML Schema配置参考手册

标签覆盖

以timeout为例,显示了配置的查找顺序,其它retries, loadbalance, actives等类似。

  • 方法级优先,接口级次之,全局配置再次之。
  • 如果级别一样,则消费方优先,提供方次之。

其中,服务提供方配置,通过URL经由注册中心传递给消费方。

建议由服务提供方设置超时,因为一个方法需要执行多长时间,服务提供方更清楚,如果一个消费方同时引用多个服务,就不需要关心每个服务的超时设置。
在这里插入图片描述

示例

启动时检查

Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题,默认 check=“true”。

可以通过 check=“false” 关闭检查,比如,测试时,有些服务不关心,或者出现了循环依赖,必须有一方先启动。

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

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

集群容错

在集群调用失败时,Dubbo 提供了多种容错方案,缺省为 failover 重试。
cluster
各节点关系:

  • 这里的 Invoker 是 Provider 的一个可调用 Service 的抽象,Invoker 封装了 Provider 地址及 Service 接口信息。
  • Directory 代表多个 Invoker,可以把它看成 List < Invoker > ,但与 List 不同的是,它的值可能是动态变化的,比如注册中心推送变更。
  • Cluster 将 Directory 中的多个 Invoker 伪装成一个 Invoker,对上层透明,伪装过程包含了容错逻辑,调用失败后,重试另一个。
  • Router 负责从多个 Invoker 中按路由规则选出子集,比如读写分离,应用隔离等。
  • LoadBalance 负责从多个 Invoker 中选出具体的一个用于本次调用,选的过程包含了负载均衡算法,调用失败后,需要重选。

集群容错模式

  • Failover Cluster:失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过retries (默认值为2)来设置重试次数(不含第一次),注意幂等性。
<dubbo:service retries="1" />
  • Failfast Cluster:快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。

  • Failsafe Cluster:失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。

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

  • Forking Cluster:并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks=“2” 来设置最大并行数。

  • Broadcast Cluster:广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息。

使用

<dubbo:service cluster="failsafe" />

负载均衡

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

负载均衡策略

Random LoadBalance

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

RoundRobin LoadBalance

  • 轮询,按公约后的权重设置轮询比率。
  • 存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。

LeastActive LoadBalance

  • 最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。
  • 使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。

ConsistentHash LoadBalance

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

使用

<dubbo:service interface="..." loadbalance="roundrobin" />

服务分组

当一个接口有多种实现时,可以用 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" />
<!-- 任意组选择 -->
<dubbo:reference id="barService" interface="com.foo.BarService" group="*" />

若同一接口的多个实现使用同一个group进行分组,则dubbo将按照xml的配置顺序从前往后进行覆盖,消费者只能使用最后一个组册在该group下的服务。

协议

dubbo://

Dubbo 缺省协议采用单一长连接和 NIO 异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。

反之,Dubbo 缺省协议不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。

dubbo-protocol.jpg

  • Transporter: mina, netty, grizzy
  • Serialization: dubbo, hessian2, java, json
  • Dispatcher: all, direct, message, execution, connection
  • ThreadPool: fixed, cached

项目快速搭建

服务提供者

  1. 定义服务接口
public interface DemoService {
    String sayHello(String name);
}
  1. 在服务提供方实现接口
public class DemoServiceImpl implements DemoService {
    public String sayHello(String name) {
        return "Hello " + name;
    }
}
  1. 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="demo-provider"  />
 
    <!-- 使用zookeeper注册中心暴露服务地址 -->
    <dubbo:registry id="zk" protocol="zookeeper" address="zookeeper://127.0.0.1:2181"/>
 
    <!-- 用dubbo协议在20880端口暴露服务 -->
    <dubbo:protocol name="dubbo" port="20880" />
 
    <!-- 声明需要暴露的服务接口 -->
    <dubbo:service interface="org.apache.dubbo.demo.DemoService" ref="demoService" version="1.0.0" 
    registry="zk" retries="1" timeout="500" loadbalance="random" actives="0" executes="200" />
 
    <!-- 和本地bean一样实现服务 -->
    <bean id="demoService" class="org.apache.dubbo.demo.provider.DemoServiceImpl" />
</beans>
  1. 加载xml配置,注册服务
public class Provider {
    public static void main(String[] args) throws Exception {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-provider.xml");
        context.start();
        System.in.read(); // 按任意键退出
    }
}

服务消费者

  1. 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="demo-consumer"  />
 
    <!-- 使用zookeeper注册中心暴露服务地址 -->
    <dubbo:registry id="zk" protocol="zookeeper" address="zookeeper://127.0.0.1:2181"/>
 
    <!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
    <dubbo:reference id="demoService" interface="org.apache.dubbo.demo.DemoService" version="1.0.0" check=“false” />
</beans>
  1. 加载xml配置,并调用远程服务
public class Consumer {
    public static void main(String[] args) throws Exception {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(“spring/dubbo-consumer.xml”);
        context.start();
        DemoService demoService = (DemoService)context.getBean("demoService"); // 获取远程服务代理
        String hello = demoService.sayHello("world"); // 执行远程方法
        System.out.println( hello ); // 显示调用结果
    }
}

SPI扩展实现

概念

SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类,可以通过 SPI 机制为程序提供拓展功能。

Dubbo 的扩展点加载从 JDK 标准的 SPI 扩展点发现机制加强而来,核心是利用ExtensionLoader.getExtensionLoader方法。

  • 扩展者实现指定接口,然后创建扩展点配置文件:META-INF/dubbo/接口全限定名,内容为:配置名=扩展实现类全限定名,多个实现类用换行符分隔。
  • Dubbo会查找在接⼝打了@SPI注解的接⼝类的扩展点实现,依次从以下⼏个文件中读取扩展点:
    • META-INF/dubbo/internal/
    • META-INF/dubbo/
    • META-INF/services/

调用拦截扩展

说明

服务提供方和服务消费方调用过程拦截,Dubbo 本身的大多功能均基于此扩展点实现,每次远程方法执行,该拦截都会被执行,请注意对性能的影响。

扩展接口:org.apache.dubbo.rpc.Filter(已打上@SPI注解)

约定:

  • 用户自定义 filter 默认在内置 filter 之后。
  • 特殊值 default,表示缺省扩展点插入的位置。比如:filter=“xxx,default,yyy”,表示 xxx 在缺省 filter 之前,yyy 在缺省 filter 之后。
  • 特殊符号 -,表示剔除。比如:filter="-foo1",剔除添加缺省扩展点 foo1。比如:filter="-default",剔除添加所有缺省扩展点。
  • provider 和 service 同时配置的 filter 时,累加所有 filter,而不是覆盖。比如:<dubbo:provider filter=“xxx,yyy”/> 和 <dubbo:service filter=“aaa,bbb” />,则 xxx,yyy,aaa,bbb 均会生效。如果要覆盖,需配置:<dubbo:service filter="-xxx,-yyy,aaa,bbb" />

使用

  1. 实现Filter接口:XxxFilter.java
package com.xxx;
public class XxxFilter implements Filter {
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        // before filter ...
        Result result = invoker.invoke(invocation);
        // after filter ...
        return result;
    }
}
  1. 创建扩展点配置文件:META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter
xxx=com.xxx.XxxFilter
  1. 使用扩展配置
<!-- 提供方调用过程拦截 -->
<dubbo:service filter="xxx" />
<!-- 提供方调用过程缺省拦截器,将拦截所有service -->
<dubbo:provider filter="xxx"/>

推荐用法

在 Provider 端尽量多配置 Consumer 端属性

  • 作服务的提供方,比服务消费方更清楚服务的性能参数,如调用的超时时间、合理的重试次数等
  • 在 Provider 端配置后,Consumer 端不配置则会使用 Provider 端的配置,即 Provider 端的配置可以作为 Consumer 的缺省值。否则,Consumer 会使用 Consumer 端的全局设置,这对于 Provider 是不可控的,并且往往是不合理的
  • Provider 端尽量多配置 Consumer 端的属性,让 Provider 的实现者一开始就思考 Provider 端的服务特点和服务质量等问题。

示例:

<dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService"
    timeout="300" retry="2" loadbalance="random" actives="0" />
 
<dubbo:service interface="com.alibaba.hello.api.WorldService" version="1.0.0" ref="helloService"
    timeout="300" retry="2" loadbalance="random" actives="0" >
    <dubbo:method name="findAllPerson" timeout="10000" retries="9" loadbalance="leastactive" actives="5" />
<dubbo:service/>

建议在 Provider 端配置的 Consumer 端属性有:

  1. timeout:方法调用的超时时间。
  2. retries:失败重试次数,缺省是 2 。
  3. loadbalance:负载均衡算法,缺省是随机 random。还可以配置轮询 roundrobin、最不活跃优先leastactive 和一致性哈希 consistenthash 等。
  4. actives:消费者端的最大并发调用限制,即当 Consumer 对一个服务的并发调用到上限后,新调用会阻塞直到超时,在方法上配置 dubbo:method 则针对该方法进行并发限制,在接口上配置 dubbo:service,则针对该服务进行并发限制。

在 Provider 端配置合理的 Provider 端属性

<dubbo:protocol threads="200" /> 
<dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService"
    executes="200" >
    <dubbo:method name="findAllPerson" executes="50" />
</dubbo:service>

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

  1. threads:服务线程池大小
  2. executes:一个服务提供者并行执行请求上限,即当 Provider 对一个服务的并发调用达到上限后,新调用会阻塞,此时 Consumer 可能会超时。在方法上配置 dubbo:method 则针对该方法进行并发限制,在接口上配置 dubbo:service,则针对该服务进行并发限制

序列化

  • 所有返回给客户端的类包括它的属性,都需要实现Serializable接口,否则会导致序列化错误
  • 服务的参数和返回结果不要含有枚举类型,这对序列化非常不友好
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值