dubbo

开篇–什么是RPC

允许一台机器上的程序调用另一台机器上的程序,当A机器上的程序调用B机器上的程序时, A机器上的进程会波挂起,而B机器接着执行; B机器在返回后,会把运算结果的数据传回给A机器的进程, A机器获得调用结果后再接着执行,这种机制就叫作RPC ( Remote Procedure Call,远程过程调用),其对应的协议被称为RPC协议。
dubbo_第1张图片

主流的RPC框架

1.thrift.
2.gRPC:
3.dubbo

什么是dubbo

Dubbo是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA(面向服务架构)服务治理方案。
其核心部分包含:
1. 远程通讯: 提供对多种基于长连接的NIO框架抽象封装,包括多种线程模型,序列化,以及“请求-响应”模式
的信息交换方式。
2. 集群容错: 提供基于接口方法的透明远程过程调用,包括多协议支持,以及软负载均衡,失败容错,地
址路由,动态配置等集群支持。
3. 自动发现: 基于注册中心目录服务,使服务消费方能动态的查找服务提供方,使地址透明,使服务提供
方可以平滑增加或减少机器。

dubbo的依赖关系

dubbo_第2张图片

节点角色说明:
Provider: 暴露服务的服务提供方。
Consumer: 调用远程服务的服务消费方。
Registry: 服务注册与发现的注册中心。
Monitor: 统计服务的调用次调和调用时间的监控中心。
Container: 服务运行容器。
调用关系说明:
1. 服务提供者在启动时,向注册中心注册自己提供的服务。
2. 服务消费者在启动时,向注册中心订阅自己所需的服务。
3. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
4. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
5. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

配置之间的关系

dubbo_第3张图片

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

Dubbo的四种配置方式

1. XML配置
2. 属性配置
3. API配置
4. 注解配置

覆盖策略

dubbo_第4张图片

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

配置覆盖关系

dubbo_第5张图片

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

XML配置

提供者


    
    

    
    

    
    

    
    

调用者


    

    

    

    
    

    
    

API配置

一般不用,可以用户Test、Mock测试。

生产者
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(12345);
protocol.setThreads(200);
 
// 注意:ServiceConfig为重对象,内部封装了与注册中心的连接,以及开启服务端口
 
// 服务提供者暴露服务配置
ServiceConfig service = new ServiceConfig(); // 此实例很重,封装了与注册中心的连接,请自行缓存,否则可能造成内存和连接泄漏
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();
消费者
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(); // 注意:此代理对象内部封装了所有通讯细节,对象较重,请缓存复用

注解配置

@Service(timeout = 1000 ,version = "1.0.0")
public class BootServiceImpl implements BootService {

    @Override
    public String sayHello(String name) {
        return name+"hello";
    }
}

  // 注意:这里如果有版本号信息,一定要和生产者保持一致,否则会出错,mock指定mock的实现类
    @Reference(version = "1.0.0",mock = "com.bobo.dubbo.mock.BootServiceMock")
    public BootService bootServiceImpl;

    public String  sayHello(String name)  throws RpcException{
        System.out.println("执行了,执行了====================================");
        return bootServiceImpl.sayHello(name);
    }

properties配置

需要在classpath根目录下新建dubbo.properties,dubbo会自动加载这个文件,或者也可以使用-Ddubbo.properties.file=dubbo.properties来指定配置文件的位置。

服务的注册和发现

注册中心

dubbo注册多注册中心,而且支持同时向多个注册中心注册,支持的注册中心有如下四种:
1.Multicast注册中心,该注册中心不需要启动任何中心节点,只要广播地址一样,就可以相互发现。
2. Zookeeper注册中心,是树型的目录服务。
3. redis注册中心。
4. Simple注册中心

zookeeper注册中心

我们在服务提供者(Provider)和服务消费者(Consumer)的配置文件中使用dubbo:registry/标签指定ZooKeeper的地址后,就可以使用ZooKeeper注册中心了,配置代码如下:

或者

dubbo目前支持zkclient和curator这两种ZooKeeper客户端实现,在默认情况下使用的是zkclient,如果想使用curator,则可以指定client=“curator” ,同时需要在工程中添 加zkclient或curator相关的Jar包的依赖。具体的使用代码如下:在默认情况下client为zkclient:

同时在Gradle配置文件中添加依赖的Jar包(Maven的配置同理):
compile group: ‘org.apache.zookeeper’, name: ‘2ookeeper’, version: 13.4.11’
或者指定使用curator:

同时在Gradle配置文件中添加依赖的Jar包(Maven配置同理):
compile group: ord.ana g.apache.curator’, name: ‘curator-framework’, version: ‘4.0.0’

ZooKeeper分成多组注册中心,代码如下:
<dubbo: registry id-"hangzhouRegistry" address="127.0.0.1:2181" /><dubbo:registry id="shanghaiRegistry" address="127.0.0.1:2182" default="false"/>

结构图:
dubbo_第6张图片

流程说明:

    服务提供者启动时: 向 /dubbo/com.foo.BarService/providers 目录下写入自己的 URL 地址
    服务消费者启动时: 订阅 /dubbo/com.foo.BarService/providers 目录下的提供者 URL 地址。并向 /dubbo/com.foo.BarService/consumers 目录下写入自己的 URL 地址
    监控中心启动时: 订阅 /dubbo/com.foo.BarService 目录下的所有提供者和消费者 URL 地址。
支持以下功能:

    当提供者出现断电等异常停机时,注册中心能自动删除提供者信息
    当注册中心重启时,能自动恢复注册数据,以及订阅请求
    当会话过期时,能自动恢复注册数据,以及订阅请求
    当设置  时,记录失败注册和订阅请求,后台定时重试
    可通过  设置 zookeeper 登录信息
    可通过  设置 zookeeper 的根节点,不设置将使用无根树
    支持 * 号通配符 ,可订阅服务的所有分组和所有版本的提供者
redis注册中心
Simple注册中心
Multicast 注册中心

服务暴露


    

    
    

主要介绍一下这几个参数:

   	 标签中:
           
           delay:服务延迟暴露,一般作用于服务预热,比如初始化缓存、等待相关资源。
           executes: 限制HelloService接口的每个方法,服务器端的并发执行不能超过10个。
           actives:限制每个客户端的并发执行不能超过10个。
        标签中:
           accepts:限制服务端的连接数不能超过10个

在服务暴露的配置过程中,除了常用的控制并发、控制连接数,服务隔离也是非常重要的一项措施。服务隔离是为了在系统发生故障时限定传播范围和影响范围,从而保证只有出问题的服务不可用,其他服务还是正常的。隔离一般有线程隔离、进程隔离、读写隔离、集群隔离和 机房隔离,而Dubbo还提供了分组隔离,即使用group属性分组。

另外,服务暴露还提供了很多有用的配置属性,比如version版本号、timeout服务请求超时、retries服务请求失败重试次数、weight服务权重、cluster集群方式等。若想了解更多的配置属性,则可以查看官网"schema配置参考手册”中dubbo:service/标签的内容。

服务引用

简单的方法就是:
 
    

注意一下这个异步:
dubbo_第7张图片

由于消费者每次远程调用服务时都会有网络开销,而某些热门数据的访问量又很大, t不是经常变动的数据,所以Dubbo为了加速热门数据的访问速度,提供了声明式缓存,以减少用户额外添加缓存的工作量。可以通过在消费者方配置cache属性来开启缓存功能,其用治如下:

 <dubbo:reference id="helloService"  interface="com.bobo.dubbo.api.HelloService" cache="lru"/>

cache=“Lru” 这个可以开启缓存,缓存的使用方式:
Lru:基于最近最少使用原则删除多余的缓存,保持最热的数据被缓存。
Threadlocal: 当前的线程缓存,比如在一个页面渲染中会用到很多portal,每个portal都要去查用户的信息,通过线程缓存可以减少这种多余的访问。
jcache:与JSR107集成,可以桥接各种缓存实现。

Dubbo通信协议和序列化

Dubbo支持的协议

Dubbo支持多种协议,如下所述。
Dubbo协议:为Dubbo默认的协议,采用单一长连接和NIO异步通信,适合小数据量大并发的服务调用,以及服务消费者的机器数远大于服务提供者的机器数的情况。
Hessian协议:用于集成Hessian的服务, Hessian底层采用HTTP通信,采用Servlet暴露服务, Dubbo默认内嵌Jetty作为服务器的实现。
HTTP:基于HTTP表单的远程调用协议,采用Spring的Httplnvoker实现。
RMI协议:采用JDK标准的java.rmi.*实现,采用阻塞式短连接和JDK标准序列化方式。
WebService协议:基于WebService的远程调用协议,基于Apache CXF的frontend-simple和transports-http实现
Thrift协议:当前Dubbo支持的Thrift协议是对Thrift原生协议的扩展,在原生协议的基础添加了一些额外的头信息,比如service name,magic number等。使用Dubbo Thrift协议时同样需要使用Thrift的IDL compiler编译生成相应的Java代码,在后续的版本中会在这方面做一些增强。
Memcached协议:基于Memcached实现的RPC协议。
Redis协议:基于Redis实现的RPC协议。

dubbo协议

dubbo_第8张图片

基本原理如下所示:

  • 1.client一个线程调用远程接口,生成一个唯一的ID(比如一段随机字符串,UUID等),Dubbo是使用AtomicLong从0开始累计数字的。
  • 2.将打包的方法调用信息(如调用的接口名称,方法名称,参数值列表等),和处理结果的回调对象callback,全部封装在一起,组成一个对象object。
  • 3.向专门存放调用信息的全局ConcurrentHashMap里面put(ID, object)。
  • 4.将ID和打包的方法调用信息封装成一对象connRequest,使用IoSession.write(connRequest)异步发送出去。
  • 5.当前线程再使用callback的get()方法试图获取远程返回的结果,在get()内部,则使用synchronized获取回调对象callback的锁, 再先检测是否已经获取到结果,如果没有,然后调用callback的wait()方法,释放callback上的锁,让当前线程处于等待状态。
  • 6.服务端接收到请求并处理后,将结果(此结果中包含了前面的ID,即回传)发送给客户端,客户端socket连接上专门监听消息的线程收到消息,分析结果,取到ID,再从前面的ConcurrentHashMap里面get(ID),从而找到callback,将方法调用结果设置到callback对象里。
  • 7.监听线程接着使用synchronized获取回调对象callback的锁(因为前面调用过wait(),那个线程已释放callback的锁了),再notifyAll(),唤醒前面处于等待状态的线程继续执行(callback的get()方法继续执行就能拿到调用结果了),至此,整个过程结束。

dubbo_第9张图片

协议配置的三种方式

dubbo:protocol
<dubbo:protocol name="dubbo" port="20880" server="netty" client="netty" codec="dubbo"
        serialization="hessian2" charset="UTF-8" threadpool="fixed" threads="100" queues="0"
        iothreads="9" buffer="8192" accepts="1000" payload="8388608"/>

大致有这么多属性,具体点的参数作用可以看官方文档。

dubbo:service
<dubbo:service interface="com.bobo.dubbo.api.HelloService" ref="helloService" delay="5000"
        executes="10" actives="10" protocol="dubbo"/>

可以使用这样的方式。

dubbo:provider
<dubbo:provider protocol="dubbo" server="netty"/>

dubbo协议在默认情况下每个服务所有提供者和消费者之间使用单一长连接,如果传输数据量大的情况
下可以使用多个连接:

  • 表示该服务使用 JVM 共享长连接。缺省
  • 表示该服务使用独立长连接。
  • 表示该服务使用独立两条长连接。

多协议暴露服务

dubbo不仅提供了多种协议可以供选择,还可以让不同的服务使用不同的协议或者是让相同服务 使用不同的协议。

不同的服务使用不同的协议

<dubbo:protocol name="rmi" port="1099"/>

<dubbo: service interface="com.bill.IHelloservice" version="1.0.0" ref="helloservice" protocol="dubbo"/>


同一个服务使用不同的协议

比如,有的服务不仅仅可以再服务之间调用,也需要与HTTP客户端操作。就可以为该服务同时制定多个协议。


<dubbo:protocol name="dubbo" port="20880" />
<dubbo:protocol name="hessian" port="8080"/>]


集群的容错机制和负载均衡

容错机制的原理

dubbo_第10张图片

  • 这里的 InvokerProvider 的一个可调用 Service 的抽象,Invoker 封装了 Provider 地址及 Service 接口信息
  • Directory 代表多个 Invoker,可以把它看成 List ,但与 List 不同的是,它的值可能是动态变化的,比如注册中心推送变更
  • ClusterDirectory 中的多个 Invoker 伪装成一个 Invoker,对上层透明,伪装过程包含了容错逻辑,调用失败后,重试另一个
  • Router 负责从多个 Invoker 中按路由规则选出子集,比如读写分离,应用隔离等
  • LoadBalance 负责从多个 Invoker 中选出具体的一个用于本次调用,选的过程包含了负载均衡算法,调用失败后,需要重选
如何配置
	<dubbo:service cluster="failsafe" /> 配置服务提供者
   <dubbo:reference cluster="failsafe" /> 配置消费方
   默认是Failover

Failover Cluster
失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过 retries=“2” 来设置重试次数(不含第一次)。
重试次数配置如下:

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

负载均衡

配置

dubbo_第11张图片

负载均衡策略
策略 描述
Random LoadBalance 随机,按权重设置随机概率。在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。
RoundRobin LoadBalance 轮询,按公约后的权重设置轮询比率。存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。
LeastActive LoadBalance 最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
ConsistentHash LoadBalance 一致性 Hash,相同参数的请求总是发到同一提供者。当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。

监控平台

dubbo官方开源管理后台

服务降级

dubbo是使用mock配置实现服务降级的,mock在出现非业务异常(比如超时,提供者全部挂掉或者是网络异常的时)执行,mock支持如下两种配置

boolean值

默认配置是false,如果配置为true,则使用mock的类名,即类名+mock后缀。

return null

    
这样也是可以的。

使用sentinel管理dubbo

sentinel

dubbo_第12张图片

流量控制:流量控制在网络传输中是一个常用的概念,它用于调整网络包的发送数据。然而,从系统稳定性角度考虑,在处理请求的速度上,也有非常多的讲究。任意时间到来的请求往往是随机不可控的,而系统的处理能力是有限的。我们需要根据系统的处理能力对流量进行控制。Sentinel 作为一个调配器,可以根据需要把随机的请求调整成合适的形状。
dubbo_第13张图片

熔断降级:当调用链路中某个资源出现不稳定,例如,表现为 timeout,异常比例升高的时候,则对这个资源的调用进行限制,并让请求快速失败,避免影响到其它的资源,最终产生雪崩的效果。可以通过并发线程数和响应时间进行降级。
文档地址:http://note.youdao.com/noteshare?id=9512851a045a444ec49ecb03825fa061

你可能感兴趣的:(dubbo)