【详解】Dubbo的原理以及详细原理、配置

时间:2017-09-05 19:14:09   收藏:0   阅读:6033

Dubbo的背景

随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进。

Dubbo的应用

用于大规模服务化通过在消费方获取服务提供方地址列表,实现软负载均衡,减轻硬件压力。

架构

最简单调用图

技术分享

节点角色说明:

调用关系说明:

 

简单用法

需与spring集成(也可以调用api,但官方不推荐,且代码臃肿)。Java代码跟正常功能一样,只需要一个接口和一个实现类,像正常spring一样配置。需要详细说明的是xml配置:

提供者(服务端):

技术分享

消费者(调用客户端):

技术分享

代码简单解释(配置项暂不说明):

 技术分享

这段代码就是获取spring的配置文件后,获取bean,然后远程调用,并且获取返回值。

 

使用协议

Dubbo协议

采用NIO复用单一长连接,并使用线程池并发处理请求,减少握手和加大并发效率,性能较好(推荐使用)在大文件传输时,单一连接会成为瓶颈。Dubbo协议缺省每服务每提供者每消费者使用单一长连接,如果数据量较大,可以使用多个连接。示例:

<dubbo:protocol name="dubbo" connections="2" />
<!--表示该服务使用JVM共享长连接(缺省)-->
<dubbo:service connections=”0”>或<dubbo:reference connections=”0”>
<!--表示该服务使用独立长连接-->
<dubbo:service connections=”1”>或<dubbo:reference connections=”1”>
<!--表示该服务使用独立两条长连接-->
<dubbo:service connections=”2”>或<dubbo:reference connections=”2”>
  1. 参数及返回值需实现Serializable接口
  2. 参数及返回值不能自定义实现List, Map, Number, Date, Calendar等接口,只能用JDK自带的实现,因为hessian会做特殊处理,自定义实现类中的属性值都会丢失。
  3. 只传成员属性值和值的类型,不传方法或静态变量
  4. 接口增加方法,对客户端无影响,如果该方法不是客户端需要的,客户端不需要重新部署;输入参数和结果集中增加属性,对客户端无影响,如果客户端并不需要新属性,不用重新部署;输入参数和结果集属性名变化,对客户端序列化无影响,但是如果客户端不重新部署,不管输入还是输出,属性名变化的属性值是获取不到的。

 

Rmi协议

可与原生RMI互操作,基于TCP协议偶尔会连接失败,需重建Stub。

如果服务接口继承了java.rmi.Remote接口,可以和原生RMI互操作,即:提供者用DubboRMI协议暴露服务,消费者直接用标准RMI接口调用,或者提供方用标准RMI暴露服务,消费方用DubboRMI协议调用。

如果服务接口没有继承java.rmi.Remote接口,缺省Dubbo将自动生成一个com.xxx.XxxService$Remote的接口,并继承java.rmi.Remote接口,并以此接口暴露服务,但如果设置了<dubbo:protocol name="rmi" codec="spring" />,将不生成$Remote接口,而使用SpringRmiInvocationHandler接口暴露服务,和Spring兼容。

  1. 参数及返回值需实现Serializable接口
  2. dubbo配置中的超时时间对rmi无效,需使用java启动参数设置:-Dsun.rmi.transport.tcp.responseTimeout=3000,参见下面的RMI配置。

 

Hessian协议

可与原生Hessian互操作,基于HTTP协议hessian.jar支持,http短连接的开销大

  1. 参数及返回值需实现Serializable接口.
  2. 参数及返回值不能自定义实现List, Map, Number, Date, Calendar等接口,只能用JDK自带的实现,因为hessian会做特殊处理,自定义实现类中的属性值都会丢失。

 

HTTP协议

基于http表单的远程调用协议。

  1. 参数及返回值需符合Bean规范

使用协议示例:

<dubbo:protocol name="http" port="8080" />

注意,如果使用servlet派发请求:协议的端口<dubbo:protocol port="8080" />必须与servlet容器的端口相同,协议的上下文路径<dubbo:protocol contextpath="foo" />必须与servlet应用的上下文路径相同。

 

Webservice协议

基于CXFfrontend-simpletransports-http实现,可以和原生WebService服务互操作,即:提供者用DubboWebService协议暴露服务,消费者直接用标准WebService接口调用,或者提供方用标准WebService暴露服务,消费方用DubboWebService协议调用。

  1. 参数及返回值需实现Serializable接口
  2. 参数尽量使用基本类型和POJO

 

多协议

不同服务不同协议

<!-- 多协议配置 -->
<dubbo:protocol name="dubbo" port="20880" />
<dubbo:protocol name="rmi" port="1099" />
<!-- 使用dubbo协议暴露服务 -->
<dubbo:service interface="com.ailk.uchannel.service.interfaces.HelloService" version="1.0.0" ref="helloService" protocol="dubbo" />
<!-- 使用rmi协议暴露服务 -->
<dubbo:service interface="com.ailk.uchannel.service.interfaces.DemoService" version="1.0.0" ref="demoService" protocol="rmi" />

 

多协议暴露服务

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

Dubbo注册中心

最简单的Multicast注册中心

不需要注册中心,只要广播地址一样,就能相互发现。只是依赖于网络拓普和路由,跨机房有风险

组播受网络结构限制,只适合小规模应用或开发阶段使用。

示例:

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

流程说明:

注意:

  1. multicast地址不能配成127.0.0.1,也不能配成机器的IP地址,必须是D段广播地址,也就是:224.0.0.0239.255.255.255之间的任意地址。
  2. 为了减少广播量,Dubbo缺省使用单播发送提供者地址信息给消费者,如果一个机器上同时启了多个消费者进程,消费者需声明unicast=false,否则只会有一个消费者能收到消息。

 

zookeeper 推荐的注册中心

可用集群,官方建议不用复杂配置,也可以选择淘宝的支持。

使用示例:

简单:

<dubbo:registry protocol="zookeeper" address="192.168.109.130:2181"/>

集群的zookeeper

<dubbo:registry protocol="zookeeper" address="10.20.153.10:2181,10.20.153.11:2181" />

流程说明:

支持以下功能:

zookeeper配置:

在安装路径下的conf目录里, zoo_sample.cfg 改名为 zoo.cfg

  需配置项:

    tickTime=2000

    dataDir=D:/devtools/zookeeper-3.2.2/build

    clientPort=2181

  1. tickTime:这个时间是作为 Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个 tickTime 时间就会发送一个心跳。
  2. dataDir:顾名思义就是 Zookeeper 保存数据的目录,默认情况下,Zookeeper 将写数据的日志文件也保存在这个目录里。
  3. clientPort:这个端口就是客户端连接 Zookeeper 服务器的端口,Zookeeper 会监听这个端口,接受客户端的访问请求。

  集群模式除了上面的三个配置项还要增加下面几个配置项:

  initLimit=5

  syncLimit=2

  server.1=192.168.211.1:2888:3888

  server.2=192.168.211.2:2888:3888

  1. initLimit:这个配置项是用来配置 Zookeeper 接受客户端(这里所说的客户端不是用户连接 Zookeeper 服务器的客户端,而是 Zookeeper 服务器集群中连接到 Leader Follower 服务器)初始化连接时最长能忍受多少个心跳时间间隔数。当已经超过 10 个心跳的时间(也就是 tickTime)长度后 Zookeeper 服务器还没有收到客户端的返回信息,那么表明这个客户端连接失败。总的时间长度就是 5*2000=10
  2. syncLimit:这个配置项标识 Leader Follower 之间发送消息,请求和应答时间长度,最长不能超过多少个 tickTime 的时间长度,总的时间长度就是 2*2000=4
  3. server.A=BCD:其中 A 是一个数字,表示这个是第几号服务器;B 是这个服务器的 ip 地址;C 表示的是这个服务器与集群中的 Leader 服务器交换信息的端口;D 表示的是万一集群中的 Leader 服务器挂了,需要一个端口来重新进行选举,选出一个新的 Leader,而这个端口就是用来执行选举时服务器相互通信的端口。如果是伪集群的配置方式,由于 B 都是一样,所以不同的 Zookeeper 实例通信端口号不能一样,所以要给它们分配不同的端口号。

  除了以上配置,在data目录下还要放置myid文件:(上面zoo.cfg中的dataDir)这个文件里面就有一个数据就是 A 的值,Zookeeper 启动时会读取这个文件,拿到里面的数据与 zoo.cfg 里面的配置信息比较从而判断到底是那个 server

 

简易监控中心

Simple监控中心,用来监控与统计框架运行情况,挂掉不会影响到ConsumerProvider之间的调用,所以用于生产环境不会有风险。

管理控制台 

依托于web容器(可以直接放到tomcat里),开源部分主要包含:路由规则,动态配置,服务降级,访问控制,权重调整,负载均衡,等管理功能,可以查看提供者和消费者的一些信息,并可以对此进行操作。

配置:

需要查看修改/ROOT/WEB-INF/dubbo.properties文件,配置注册中心和登陆用户密码。

dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.admin.root.password=root
dubbo.admin.guest.password=guest

集群使用

特性

容错模式

Failover Cluster

Failfast Cluster

Failsafe Cluster

Failback Cluster

Forking Cluster

Broadcast Cluster

集群模式配置

<dubbo:service cluster="failsafe" />或者<dubbo:reference cluster="failsafe" />

 

 

负载均衡

Random LoadBalance

RoundRobin LoadBalance

LeastActive LoadBalance

ConsistentHash LoadBalance

负载均衡配置:

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

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

可以在方法上配置

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

服务容器

服务端不需要web容器,只是一个简单的main方法,加载spring容器,用于暴露服务。

服务容器的加载内容可以扩展,内置了spring, jetty, log4j等加载,可通过Container扩展点进行扩展。

自动加载META-INF/spring目录下的所有Spring配置。

配置:(配在java命令-D参数或者dubbo.properties)

dubbo.spring.config=classpath*:META-INF/spring/*.xml ----配置spring配置加载位置

  启动一个内嵌Jetty,用于汇报状态。

  配置:(配在java命令-D参数或者dubbo.properties)

dubbo.jetty.port=8080 ----配置jetty启动端口

dubbo.jetty.directory=/foo/bar ----配置可通过jetty直接访问的目录,用于存放静态文件

dubbo.jetty.page=log,status,system ----配置显示的页面,缺省加载所有页面

 

  自动配置log4j的配置,在多进程启动时,自动给日志文件按进程分目录。

  配置:(配在java命令-D参数或者dubbo.properties)

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

基本配置使用说明

Xml配置

Properties文件配置

Dubbo将自动加载classpath根目录下的dubbo.properties,可以通过JVM启动参数:-Ddubbo.properties.file=xxx.properties 改变缺省配置位置。

映射规则:

  比如:dubbo.application.name=foo等价于<dubbo:application name="foo" />

  比如:dubbo.registry.address=10.20.153.10:9090等价于<dubbo:registry address="10.20.153.10:9090" />

  比如:dubbo.protocol.rmi.port=1234等价于<dubbo:protocol id="rmi" name="rmi" port="1099" /> (协议的id没配时,缺省使用协议名作为id)

  比如:dubbo.registry.china.address=10.20.153.10:9090等价于<dubbo:registry i d="china" address="10.20.153.10:9090" />

详细配置说明参考官方文档。

自带优化功能

结果缓存

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

配置如:

<dubbo:reference interface="com.ailk.uchannel.service.interfaces.TestService" cache="lru" />

或:

<dubbo:reference interface="com.ailk.uchannel.service.interfaces.TestService">

<dubbo:method name="findBar" cache="lru" />

</dubbo:reference>

 

异步调用

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

示例:

  技术分享

代码调用:

  技术分享

你也可以设置是否等待消息发出:(异步总是不等待返回)

如果只是异步调用,完全忽略返回值,可以配置return =”false”,j减少Future对象的创建和管理成本。

 

事件通知

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

示例:

  技术分享

注意,callbackasync功能正交分解:async=true,表示结果是否马上返回,onreturn 表示是否需要回调。

组合情况:(async=false 默认)

 

本地存根

客户端通常只有接口,而实现全在服务器端,但提供方有些时候想在客户端也执行部分逻辑,比如:做ThreadLocal缓存,提前验证参数,调用失败后伪造容错数据等等,此时就需要在API中带上Stub,客户端生成Proxy时,会把Proxy通过构造函数传给Stub,然后把Stub暴露给用户,Stub可以决定要不要去调Proxy

示例:

  技术分享

Stub代码

  技术分享

注意:Stub必须有可传入Proxy的构造函数!

 

 

本地伪装

 

Stub的一个子集,便于服务提供方在客户端执行容错逻辑,因经常需要在出现RpcException(比如网络失败,超时等)时进行容错,而在出现业务异常(比如登录用户名密码错误)时不需要容错,如果用Stub,可能就需要捕获并依赖RpcException类,而用Mock就可以不依赖RpcException,因为它的约定就是只有出现RpcException时才执行。

 

示例:

  技术分享

Mock代码:

  技术分享

如果只是想简单的忽略异常,直接return null

  技术分享

 

开发连调与自测

服务分组

当一个接口有多个实现时,可以用group区分。

示例:

服务端

<dubbo:service group="feedback" interface="com.ailk.uchannel.service.interfaces.IndexService" />

<dubbo:service group="member" interface="com.ailk.uchannel.service.interfaces.IndexService" />

客户端

<dubbo:reference id="feedbackIndexService" group="feedback" interface="com.ailk.uchannel.service.interfaces.IndexService" />

<dubbo:reference id="memberIndexService" group="member" interface="com.ailk.uchannel.service.interfaces.IndexService" />

任意组:

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

 

多版本

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

示例:

<dubbo:service interface="com.ailk.uchannel.service.interfaces.TestService" version="1.0.0" />

<dubbo:reference id="testService" interface="com.ailk.uchannel.service.interfaces.TestService" version="1.0.0" />

不区分版本:

<dubbo:reference id="testService" interface="com.ailk.uchannel.service.interfaces.TestService" version="*" />

 

直连提供者

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

<dubbo:reference id="xxxService" interface="com.ailk.uchannel.service.interfaces.XxxService" url="dubbo://localhost:20890" />

 

只订阅

为方便开发测试,经常会在线下共用一个所有服务可用的注册中心,这时,如果一个正在开发中的服务提供者注册,可能会影响消费者不能正常运行。

可以让服务提供者开发方,只订阅服务(开发的服务可能依赖其它服务),而不注册正在开发的服务,通过直连测试正在开发的服务。

禁用注册配置:

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

或者

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

泛化引用

泛接口调用方式主要用于客户端没有API接口及模型类元的情况,参数及返回值中的所有POJO均用Map表示,通常用于框架集成,比如:实现一个通用的服务测试框架,可通过GenericService调用所有服务实现。

示例配置:

  技术分享

Java调用代码:

  技术分享

注意:

  1. 基本类型以及Date,List,Map等不需要转换,直接调用;
  2. Map表示POJO参数,如果返回值为POJO也将自动转成Map
  3. 如果是int等类型要用java.lang.Integer等替换,双方接口都要如此。

 

获取上下文(dubbo的配置)

上下文中存放的是当前调用过程中所需的环境信息。

调用方法:RpcContext.getContext()

RpcContext是一个ThreadLocal的临时状态记录器,当接收到RPC请求,或发起RPC请求时,RpcContext的状态都会变化。

延迟暴露

如果你的服务需要一定的启动时间,比如初始化缓存,等待相关资源就位等,可以使用delay进行延迟暴露。

示例:

延迟5秒暴露服务:

<dubbo:service delay="5000" />

延迟到Spring初始化完成后,再暴露服务:(基于SpringContextRefreshedEvent事件触发暴露)

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

 

Telnet命令

启动服务后可以运行telnet命令,来查看一些信息(window下也可以)。

进入命令:

telnet localhost 20880。

 

帮助:help

ls 显示服务列表

Ps 显示服务端口列表

Cd 

cd XxxService 改变缺省服务,当设置了缺省服务,凡是需要输入服务名作为参数的命令,都可以省略服务参数。

cd / 取消缺省服务。

Pwd 显示当前缺省服务

Trace 跟踪方法的调用情况

trace XxxService 跟踪1次服务任意方法的调用情况。

trace XxxService 10 跟踪10次服务任意方法的调用情况。

trace XxxService xxxMethod 跟踪1次服务方法的调用情况

trace XxxService xxxMethod 10 跟踪10次服务方法的调用情况。

Count 统计服务的调用情况

count XxxService 统计1次服务任意方法的调用情况。

count XxxService 10 统计10次服务任意方法的调用情况。

count XxxService xxxMethod 统计1次服务方法的调用情况。

Invoke 调用方法

invoke XxxService.xxxMethod({"prop": "value"}) 调用服务的方法。

invoke xxxMethod({"prop": "value"}) 调用服务的方法(自动查找包含此方法的服务)

 

Status 显示资源状态

status 显示汇总状态,该状态将汇总所有资源的状态,当全部OK时则显示OK,只要有一个ERROR则显示ERROR,只要有一个WARN则显示WARN

status -l 显示状态列表。

  技术分享

Log 日志

log debug 修改dubbo logger的日志级别。

log 100 查看file logger的最后100字符的日志。

最佳实践

分包

粒度

版本

兼容性

枚举值

序列化

异常

调用

推荐用法

Provider上尽量多配置Consumer端属性

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

Provider配置后,Consumer不配置则会使用Provider的配置值,即Provider配置可以作为Consumer的缺省值。

否则,Consumer会使用Consumer端的全局设置,这对于Provider不可控的,并且往往是不合理的。

Provider上尽量多配置Consumer端的属性,让Provider实现者一开始就思考Provider服务特点、服务质量的问题。

Provider可以配置的Consumer端属性有:

Provider上配置合理的Provider端属性

Provider上可以配置的Provider端属性有:

配置dubbo的缓存文件

这个文件会缓存:

使用示例:

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

有了这项配置后,当应用重启过程中,Dubbo注册中心不可用时则应用会从这个缓存文件读取服务提供者列表的信息,进一步保证应用可靠性。

Dubbo优势

Dubbo劣势

注意事项

Maven依赖与jar

<dependencies>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>${spring.version}</version>
  </dependency>

  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>${spring.version}</version>
  </dependency>

  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>${spring.version}</version>
  </dependency>

  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>${spring.version}</version>
  </dependency>

  <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>dubbo</artifactId>
    <version>2.5.3</version>
  </dependency>

  <dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.15</version>
    <exclusions>
      <exclusion>
        <groupId>com.sun.jmx</groupId>
        <artifactId>jmxri</artifactId>
      </exclusion>
      <exclusion>
        <groupId>com.sun.jdmk</groupId>
        <artifactId>jmxtools</artifactId>
      </exclusion>
      <exclusion>
        <groupId>javax.jms</groupId>
        <artifactId>jms</artifactId>
      </exclusion>
    </exclusions>
  </dependency>

  <dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.5</version>
  </dependency>

  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.7</version>
    <scope>test</scope>
  </dependency>

  <dependency>
    <groupId>com.github.sgroschupf</groupId>
    <artifactId>zkclient</artifactId>
    <version>0.1</version>
  </dependency>
</dependencies>

   技术分享

 

 

总结

个人感觉dubbo适合作为服务平台改造的框架,它可以与当前渠道的工程紧密的集合,而只需要很小的代价,目前测试在资料工程测起服务,在自服务测可以正常访问,并且返回数据库中的结果!如上的工作,只需要加入两个jar包和修正一个jar包冲突(原有javassist替换成javassist-3.15.0-GA )!

如果选择dubbo,需要的是配置的优化与选择,以及后期测试的投入,开发的工作会小到最低,当然不包括开放新的接口。与当前工程的兼容也是需要重点关注的问题!

 

评论(0
© 2014 mamicode.com 版权所有 京ICP备13008772号-2  联系我们:gaon5@hotmail.com
迷上了代码!