赞
踩
Dubbo是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。
Dubbo提供服务自动注册、自动发 现等高效服务治理方案, 可以和Spring框架无缝集成。
- 服务消费方(client)调用以本地调用方式调用服务;
- client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;
- client stub找到服务地址,并将消息发送到服务端;
- server stub收到消息后进行解码;
- server stub根据解码结果调用本地的服务;
- 本地服务执行并将结果返回给server stub;
- server stub将返回结果打包成消息并发送至消费方;
- client stub接收到消息,并进行解码;
- 服务消费方得到最终结果。
负载均衡
——同一个服务部署在不同的机器时该调用那一台机器上的服务。服务调用链路生成
——随着系统的发展,服务越来越多,服务间依赖关系变得错踪复杂,甚至分不清哪个应用要在哪个应用之前启动,架构师都不能完整的描述应用的架构关系。Dubbo可以为我们解决服务之间互相是如何调用的。服务访问压力以及时长统计、资源调度和治理
——基于访问压力实时管理集群容量,提高集群利用率。服务降级
——某个服务挂掉之后调用备用服务。
Dubbo具备的功能有:服务提供、服务调用、连接处理、通信协议、序列化方式、服务发现、服务路由、日志输出。
Dubbo核心部分包括:
远程通讯
:提供对多种基于长连接的NIO框架抽象封装,包括多种线程模型序列化,以及“请求-响应”模式的信息交换方式。集群容错
:提供基于接口方法的透明远程过程调用,包括多协议支持,以及软负载均衡、失败容错、地址路由、动态配置等集群支持。自动发现
:基于注册中心目录服务,使服务消费方能动态的查找服务器提供方,使地址透明,使服务提供方可以平滑增加或减少机器。
Dubbo能做什么?
透明化的远程方法调用
,就像调用本地方法一样调用远程方法,只需简单配置,没有任何API入侵。软负载均衡及容错机制
,可在内网替代F5等硬件负载均衡器,降低成本。服务自动注册与发现
,不再需要写死服务提供方地址,注册中心基于接口名查询服务提供者的IP地址,并且能平滑添加或删除服务提供者。- Dubbo采用全Spring配置方式,透明化接入应用,对应用没有任何API侵入,只需用Spring加载Dubbo配置即可,Dubbo基于Spring的Schema扩展进行加载。
Dubbo默认使用Netty通信框架。因此Dubbo不需要Web容器,如果硬要用Web容器,只会增加复杂性,也浪费资源。
过多的服务URL配置困难;
负载均衡分配节点压力过大的情况下也需要部署集群;
服务依赖混乱,启动顺序不清晰;
过多服务导致性能指标分析难度较大,需要监控。
Dubbo是SOA时代的产物,它的关注点主要在于服务的调用,流量分发、流量监控和熔断。而Spring Cloud诞生于微服务架构时代,考虑的是微服务治理的方方面面,另外由于依托了Spirng、Spirng Boot的优势之上,两个框架在开始目标就不一致,Dubbo定位服务治理、Spirng Cloud是一个生态。
Dubbo | Spring Cloud | |
---|---|---|
服务注册中心 | Zookeeper | Spring Cloud Netflix Eureka |
服务调用方式 | RPC | REST API |
服务网关 | 无 | Spring Cloud Netflix Zuul |
断路器 | 不完善 | Spring Cloud Netflix Hystrix |
分布式配置 | 无 | Spring CLoud Config |
服务跟踪 | 无 | Spring Cloud Sleuth |
消息总线 | 无 | Spring CLoud Bus |
数据流 | 无 | Spring Cloud Stream |
批量任务 | 无 | Spring Cloud Task |
Dubbo底层是使用Netty这样的NIO框架
,是基于TCP协议传输的,配合以Hession序列化完成RPC通信。
SpringCloud是基于Http协议+Rest接口调用远程过程的通信
,相对来说,Http请求会有更大的报文(因为HTTP协议包含大量的请求头、响应头信息),占的带宽也会更多。但是REST相比RPC更为灵活,服务提供方和调用方的依赖只依靠一纸契约,不存在代码级别的强依赖。
工作流程:
第一步:provider 向注册中心去注册。
第二步:consumer 从注册中心订阅服务,注册中心会通知 consumer 注册好的服务。
第三步:consumer 调用 provider。
第四步:consumer 和 provider 都异步通知监控中心。
Dubbo服务器注册与发现的流程:
- 1、服务容器负责启动,加载,运行服务提供者。
- 2、服务提供者在启动时,向注册中心注册自己提供的服务(发送本机IP、端口、应用信息和提供服务信息发送至注册中心存储)。
- 3、服务消费者在启动时,向注册中心订阅自己所需的服务(并发送应用信息、所求服务信息至注册中心)。
- 4、注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
- 5、服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。服务提供者状态变更会实时通知注册中心、在由注册中心实时推送至服务消费者。
- 6、服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
图中从下至上分为十层,各层均为单向依赖,右边的黑色箭头代表层之间的依赖关系,每一层都可以剥离上层被复用,其中,Service和Config层为API,其它各层均为SPI(Service Provider Interface,SPI机制的作用就是服务发现,也就是说,我们有一些服务,然后通过SPI机制,就能让这些服务被需要的人所使用)。
Dubbo默认的底层网络通讯使用的是Netty,服务提供方NettyServer使用两级线程池,其中EventLoopGroup(boss)主要用来接受客户端的链接请求,并把接受的请求分发给EventLoopGroup(worker) 来处理,boss和worker线程组我们称之为IO线程。
如果事件处理的逻辑能迅速完成,并且不会发起新的IO请求,比如只是在内存中记个标识,则直接在IO线程上处理更快,因为减少了线程池调度。
但如果事件处理逻辑较慢,或者需要发起新的IO请求,比如需要查询数据库,则必须派发到线程池,否则IO线程阻塞,将导致不能接收其它请求。
需要通过不同的派发策略和不同的线程池配置的组合来应对不同的场景:
<dubbo:protocol name="dubbo" dispatcher="all"
threadpool="fixed" threads="100" />
根据请求的消息类被IO线程处理还是被业务线程池处理,Dubbo提供了下面几种线程模型:
all
:所有消息都派发到线程池,包括请求、响应、连接事件、断开事件、心跳等。
direct
:所有消息都不派发到线程池,全部在IO线程上直接执行。
message
:只有请求响应消息派发到线程池,其它连接断开事件、心跳等消息,直接在IO线程上执行。
execution
:只请求消息派发到线程池,不含响应,响应和其它连接断开事件,心跳等消息,直接在IO线程上执行。
connection
:在IO线程上,将连接断开事件放入队列,有序逐个执行,其它消息派发到线程池。
Dubbo提供的线程池策略,扩展接口ThreadPool的SPI实现有如下几种:
fixed
:固定大小线程池,启动时建立线程,不关闭,一直持有(缺省)。
cached
:缓存线程池,空闲一分钟自动删除,需要时重建。
limited
:可伸缩线程池,但池中的线程数只会增长不会收缩。只增长不收缩的目的是为了避免收缩时突然来了大流量引起的性能问题。
Dubbo默认使用dubbo协议。支持的协议有:dubbo://(推荐)、rmi://、hessian://、http://、webservice://、thrift://、memcached://、redis://、rest://。
Dubbo的设计目的是为了满足高并发小数据量的rpc调用,在大数据量下的性能表现并不好,建议使用rmi或http协议。
这些协议对应的默认端口号:
协议 | 端口 |
---|---|
dubbo | 20880 |
http | 80 |
hessian | 80 |
rmi | 80 |
而短连接,每次要发送请求之前,需要先重新建立一次连接。
Dubbo协议适用场景:常规远程服务方法调用。
Dubbo协议实现:
连接个数:单连接;
连接方式:长连接;
传输协议:TCP;
传输方式:NIO异步传输;
序列化:Hessian二进制序列化。
Dubbo缺省协议采用单一长连接和NIO异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。
反之,Dubbo缺省协议不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。
数据通讯 | 情况 | 结果 |
---|---|---|
A -> B | 类A多一种属性(或者说类B少一种属性) | 不抛异常,A多的那个属性的值,B没有,其他正常 |
A -> B | 枚举A多一种雷剧(或者说B少一种枚举),A使用多出来的枚举进行传输 | 抛异常 |
A -> B | 枚举A多一种雷剧(或者说B少一种枚举),A不使用多出来的枚举进行传输 | 不抛异常,B正常接收数据 |
A -> B | A和B的属性名相同,但类型不同 | 抛异常 |
A -> B | 序列化ID不同 | 正常传输 |
接口增加方法,对客户端无影响,如果该方法不是客户端需要的,客户端不需要重新部署。
输入参数和结果集中增加属性,对客户端无影响,如果客户端并不需要新属性,不用重新部署。
输入参数和结果集属性名变化,对客户端序列化无影响,但是如果客户端不重新部署,不管输入还是输出,属性名变化的属性值是获取不到的。
总结:服务器端和客户端对领域对象并不需要完全一致,而是按照最大匹配原则。
各节点的关系:
- Invoker是Provider的一个可调用Service的抽象, Invoker封装了Provider地址及Service接口信息。
- Directory代表多个Invoker,可以把它看成
List<Invoker>
,但与List不同的是,它的值可能是动态变化的,比如注册中心推送变更。- Cluster将Directory中的多个Invoker伪装成一个Invoker,对上层透明,伪装过程包含了容错逻辑,调用失败后,重试另一个.
- Router负责从多个Invoker中按路由规则选出子集,比如读写分离,应用隔离等。
- LoadBalance负责从多个Invoker中选出具体的一个用于本次调用,选的过程包含了负载均衡算法,调用失败后,需要重选。
Dubbo的默认集群容错方案是Failover Cluster
。
Dubbo的集群容错方案有以下几种:
<!--2种写法-->
<dubbo:service retries="2" cluster="failover"/>
<dubbo:reference retries="2" cluster="failover"/>
cluster="failover"可以不用写,因为默认就是failover。
<!--2种写法-->
<dubbo:service cluster="failfast" />
<dubbo:reference cluster="failfast" />
cluster="failfast"和 把 cluster=“failover”、retries="0"是一样的效果,retries="0"就是不重试。
<!--2种写法-->
<dubbo:service cluster="failsafe" />
<dubbo:reference cluster="failsafe" />
<!--2种写法-->
<dubbo:service cluster="failback" />
<dubbo:reference cluster="failback" />
<!--2种写法-->
<dubbo:service cluster="forking" forks="2"/>
<dubbo:reference cluster="forking" forks="2"/>
<!--服务端服务级别-->
<dubbo:service interface="..." loadbalance="roundrobin" />
<!--客户端服务级别-->
<dubbo:reference interface="..." loadbalance="roundrobin" />
<!--服务端方法级别-->
<dubbo:service interface="..."> <dubbo:method name="..." loadbalance="roundrobin" />
<!--客户端方法级别-->
<dubbo:reference interface="..."> <dubbo:method name="..." loadbalance="roundrobin" />
Dubbo负载均衡在客户端。
Dubbo提供了多种均衡策略,默认采用Random LoadBalance策略
,可以自行扩展负载均衡策略。
<dubbo:parameter key="hash.arguments" value="0,1" />
缺省用160份虚拟节点,如果要修改,配置示例:
<dubbo:parameter key="hash.nodes" value="320" />
<!--服务端服务级别-->
<dubbo:service interface="..." loadbalance="roundrobin" />
<!--客户端服务级别-->
<dubbo:reference interface="..." loadbalance="roundrobin" />
<!--服务端方法级别-->
<dubbo:service interface="...">
<dubbo:method name="..." loadbalance="roundrobin"/>
</dubbo:service>
<!--客户端方法级别-->
<dubbo:reference interface="...">
<dubbo:method name="..." loadbalance="roundrobin"/>
</dubbo:reference>
【2、注解配置方式】
消费方基于基于注解的服务级别配置方式:
@Reference(loadbalance = "roundrobin")
HelloService helloService;
当一个接口有多种实现时,可以用group区分。
不同的组:
<dubbo:service group="feedback" interface="com.xxx.IndexService" />
<dubbo:service group="member" interface="com.xxx.IndexService" />
当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。
可以按照以下的步骤进行版本迁移:
- 在低压力时间段,先升级一半提供者为新版本;
- 再将所有消费者升级为新版本;
- 然后将剩下的一半提供者升级为新版本。
<!--老版本服务提供者配置示例-->
<dubbo:service interface="com.foo.BarService" version="1.0.0" />
<!--新版本服务提供者示例-->
<dubbo:service interface="com.foo.BarService" version="2.0.0" />
<!--老版本服务消费者示例-->
<dubbo:reference id="barService" interface="com.foo.BarService" version="1.0.0" />
<!--新版本服务消费者示例-->
<dubbo:reference id="barService" interface="com.foo.BarService" version="2.0.0" />
在调用之前、调用之后、出现异常时,会触发oninvoke、onreturn、onthrow三个事件,可以配置当事件发生时,通知哪个类的哪个方法 。
服务消费者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>
async表示结果是否马上返回,onreturn表示是否需要回调。
存在以下几种组合情况 :
异步回调模式: async=true onreturn=“xxx”
同步回调模式: async=false onreturn=“xxx”
异步无回调 : async=true
同步无回调 : async=false
本地伪装通常用于服务降级,比如某验权服务,当服务提供方全部挂掉后,客户端不抛出异常,而是通过Mock数据返回授权失败。
在Spring配置文件中按以下方式配置:
<!--2种方式-->
<dubbo:service interface="com.foo.BarService" mock="true" />
<dubbo:service interface="com.foo.BarService" mock="com.foo.BarServiceMock" />
如果服务的消费方经常需要try-catch捕获异常,如:
Offer offer = null;
try {
offer = offerService.findOffer(offerId);
} catch (RpcException e) {
logger.error(e);
}
可以考虑改为Mock实现,并在Mock实现中return null。如果只是想简单的忽略异常,在2.0.11以上版本可用:
<dubbo:service interface="com.foo.BarService" mock="return null" />
通过令牌验证在注册中心控制权限,以决定要不要下发令牌给消费者,可以防止消费者绕过注册中心访问提供者.另外通过注册中心可灵活改变授权方式,而不需修改或升级提供者。图示:
可以全局设置开启令牌验证:
<!--随机token令牌,使用UUID生成-->
<dubbo:provider interface="com.foo.BarService" token="true" />
<!--固定token令牌,相当于密码-->
<dubbo:provider interface="com.foo.BarService" token="123456" />
也可在服务级别设置:
<!--随机token令牌,使用UUID生成-->
<dubbo:service interface="com.foo.BarService" token="true" />
<!--固定token令牌,相当于密码-->
<dubbo:service interface="com.foo.BarService" token="123456" />
还可在协议级别设置:
<!--随机token令牌,使用UUID生成-->
<dubbo:protocol name="dubbo" token="true" />
<!--固定token令牌,相当于密码-->
<dubbo:protocol name="dubbo" token="123456" />
Dubbo是通过JDK的ShutdownHook来完成优雅停机的,所以如果用户使用kill -9 PID等强制关闭指令,是不会执行优雅停机的,只有通过kill PID时,才会执行。
<dubbo:application ...>
<dubbo:parameter key="shutdown.timeout" value="60000" /> <!-- 单位毫秒 -->
</dubbo:application>
Dubbo默认采用Zookeeper注册中心
。
配置 | 配置说明 | 解释 |
---|---|---|
dubbo:service | 服务配置 | 用于暴露一个服务,定义服务的元信息,一个服务可以用多个协议暴露,一个服务也可以注册到多个注册中心 |
dubbo:reference | 引用配置 | 用于创建一个远程服务代理,一个引用可以指向多个注册中心 |
dubbo:protocol | 协议配置 | 用于配置提供服务的协议信息,协议由提供方指定,消费方被动接受 |
dubbo:application | 应用配置 | 用于配置当前应用信息,不管该应用是提供者还是消费者 |
dubbo:module | 模块配置 | 用于配置当前模块信息,可选 |
dubbo:registry | 注册中心配置 | 用于配置连接注册中心相关信息 |
dubbo:monitor | 监控中心配置 | 用于配置连接监控中心相关信息,可选 |
dubbo:provider | 提供方配置 | 当 ProtocolC onfig 和 ServiceCo nfig 某属 性没有配置时,采用此缺省值,可选 |
dubbo:consumer | 消费方配置 | 当 Reference Config 某属性没有配置时,采用此缺省值,可选 |
dubbo:method | 方法配置 | 用于 ServiceCo nfig 和 Reference Config 指 定方法级的配置信息 |
dubbo:argument | 参数配置 | 用于指定方法参数配置 |
JVM System Properties,-D参数;
Externalized Configuration,外部化配置;
ServiceConfig、ReferenceConfig等编程接口采集的配置;
本地配置文件dubbo.properties。
<dubbo:reference id="xxxService" interface="com.alibaba.xxx.XxxService"
url="dubbo://localhost:20890"/>
-D配置示例:
java -D com.alibaba.xxx.XxxService=dubbo://localhost:20890
properties配置示例:
java -D dubbo.resolve.file=xxx.properties
com.alibaba.xxx.XxxService=dubbo://localhost:20890
provider.xml 示例:
<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.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<dubbo:application name="demo-provider"/>
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<dubbo:protocol name="dubbo" port="20890"/>
<bean id="demoService" class="org.apache.dubbo.samples.basic.impl.DemoServiceImpl"/>
<dubbo:service interface="org.apache.dubbo.samples.basic.api.DemoService" ref="demoService"/>
consumer.xml示例:
<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.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<dubbo:application name="demo-consumer"/>
<dubbo:registry group="aaa" address="zookeeper://127.0.0.1:2181"/>
<dubbo:reference id="demoService" check="false"
interface="org.apache.dubbo.samples.basic.api.DemoService"/>
</beans>
不同粒度配置的覆盖关系,以timeout为例,下图显示了配置的查找顺序。其它retries、loadbalance、actives等类似:
方法级优先,接口级次之,全局配置再次之。
如果级别一样,则消费方优先,提供方次之。
建议由服务提供方设置超时,因为一个方法需要执行多长时间,服务提供方更清楚,如果一个消费方同时引用多个服务,就不需要关心每个服务的超时设置。
-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" />
。
如果在xml配置中有超过一个的tag,那么你可以使用‘id’进行区分。如果你不指定id,它将作用于所有tag。
dubbo.protocol.rmi.port=1099 相当于
<dubbo:protocol id="rmi" name="rmi" port="1099" />
dubbo.registry.china.address=10.20.153.10:9090 相当于<dubbo:registry id="china" address="10.20.153.10:9090"/>
一个典型的dubbo.properties配置样例:
dubbo.application.name=foo
dubbo.application.owner=bar
dubbo.registry.address=10.20.153.10:9090
JVM -D参数,当你部署或者启动应用时,它可以轻易地重写配置,比如,改变dubbo协议端口;
XML, XML中的当前配置会重写dubbo.properties中的;
Properties,默认配置,仅仅作用于以上两者没有配置时。
1:如果在classpath下有超过一个dubbo.properties文件,比如,两个jar包都各自包含了dubbo.properties,dubbo将随机选择一个加载,并且打印错误日志。
2:如果 id 没有在 protocol 中配置,将使用 name 作为默认属性。
<dubbo:service interface="com.foo.BarService" executes="10" />
示例2:限制com.foo.BarService的sayHello方法,服务器端并发执行(或占用线程池线程数)不能超过10个:
<dubbo:service interface="com.foo.BarService">
<dubbo:method name="sayHello" executes="10" />
</dubbo:service>
示例3:限制com.foo.BarService的每个方法,客户端并发执行(或占用连接的请求数)不能超过10个:
<!--2种写法-->
<dubbo:service interface="com.foo.BarService" actives="10" />
<dubbo:reference interface="com.foo.BarService" actives="10" />
示例4:限制com.foo.BarService的sayHello方法,每客户端并发执行(或占用连接的请求数)不能超过10个:
<!--2种写法-->
<dubbo:service interface="com.foo.BarService">
<dubbo:method name="sayHello" actives="10" />
</dubbo:service>
<dubbo:reference interface="com.foo.BarService">
<dubbo:method name="sayHello" actives="10" />
</dubbo:service>
如果 <dubbo:service>
和 <dubbo:reference>
都配了actives, <dubbo:reference>
优先。
@Service
public class AnnotationServiceImpl implements AnnotationService {
@Override
public String sayHello(String name) {
return "annotation: hello, " + name;
}
}
增加应用共享配置:
# dubbo-provider.properties
dubbo.application.name=annotation-provider
dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
指定Spring扫描路径:
@Configuration
@EnableDubbo(scanBasePackages = "org.apache.dubbo.samples.simple.annotation.impl")
@PropertySource("classpath:/spring/dubbo-provider.properties")
static public class ProviderConfiguration {
}
@Component("annotationAction")
public class AnnotationAction {
@Reference
private AnnotationService annotationService;
public String doSayHello(String name) {
return annotationService.sayHello(name);
}
}
增加应用共享配置:
# dubbo-consumer.properties
dubbo.application.name=annotation-consumer
dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.consumer.timeout=3000
指定Spring扫描路径:
@Configuration
@EnableDubbo(scanBasePackages = "org.apache.dubbo.samples.simple.annotation.action")
@PropertySource("classpath:/spring/dubbo-consumer.properties")
@ComponentScan(value = {"org.apache.dubbo.samples.simple.annotation.action"})
static public class ConsumerConfiguration {
}
调用服务:
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration.class);
context.start();
final AnnotationAction annotationAction = (AnnotationAction) context.getBean("annotationAction");
String hello = annotationAction.doSayHello("world");
}
dubbo 支持 hession、Java 二进制序列化、json、SOAP 文本序列化多种序列化协议。hessian 是其默认的序列化协议。
Hessian 的对象序列化机制有 8 种原始类型:
原始二进制数据
boolean
64-bit date(64 位毫秒值的日期)
64-bit double
32-bit int
64-bit long
null
UTF-8 编码的 string
另外还包括 3 种递归类型:
list for lists and arrays
map for maps and dictionaries
object for objects
还有一种特殊的类型:ref:用来表示对共享对象的引用。
两种方式:
Dubbo在调用服务不成功时,默认是会重试两次的。这样在服务端的处理时间超过了设定的超时时间时,就会有重复请求,比如在发邮件时,可能就会发出多份重复邮件,执行注册请求时,就会插入多条重复的注册数据,那么怎么解决超时问题呢?如下对于核心的服务中心,去除dubbo超时重试机制,并重新评估设置超时时间。业务处理代码必须放在服务端,客户端只做参数验证和服务调用,不涉及业务流程处理。全局配置示例:
<dubbo:provider delay="-1" timeout="6000" retries="0"/>
当然Dubbo的重试机制其实是非常好的QOS(Quality of Service,服务质量,指一个网络能够利用各种基础技术,为指定的网络通信提供更好的服务能力,是网络的一种安全机制, 是用来解决网络延迟和阻塞等问题的一种技术)保证,它的路由机制,是会帮你把超时的请求路由到其他机器上,而不是本机尝试,所以dubbo的重试机器也能一定程度的保证服务的质量。但是请一定要综合线上的访问情况,给出综合的评估。
Dubbo通过Token令牌防止用户绕过注册中心直连,然后在注册中心上管理授权。Dubbo还提供服务黑白名单,来控制服务所允许的调用方。
Dubbo2.2.0以上版本支持。通过dubbo:reference中,设置 mock=“return null”。mock的值也可以修改为true,然后再跟接口同一个路径下实现一个Mock类,命
名规则是 “接口名称+Mock” 后缀。然后在Mock类里实现自己的降级逻辑。
服务失效踢出基于Zookeeper的临时节点原理。(服务机器会在zk上注册一个临时节点,服务失效则临时节点被删除)。
读操作建议使用Failover失败自动切换,默认重试两次其他服务器。
写操作建议使用Failfast快速失败,发一次调用失败就立即报错。
管理控制台主要包含:路由规则,动态配置,服务降级,访问控制,权重调整,负载均衡等管理功能。
dubbo 在调用服务不成功时,默认是会重试两次。
Dubbo会在 Spring实例化完bean之后,在刷新容器最后一步发布ContextRefreshEvent事件的时候,通知实现了ApplicationListener的ServiceBean类进行回调onApplicationEvent事件方法,Dubbo会在这个方法中调用ServiceBean父类ServiceConfig的export方法,而该方法真正实现了服务的(异步或者非异步)发布。
当一个接口有多种实现时,可以用group属性来分组,服务提供方和消费方都指定同一个group即可。
可以用版本号(version)过渡,多个不同版本的服务注册到注册中心,版本号不同的服务相互间不引用。这个和服务分组的概念有一点类似。
可以,Dubbo提供了声明式缓存,用于加速热门数据的访问速度,以减少用户加缓存的工作量。示例:
<dubbo:reference cache="true" />
其实比普通的配置文件就多了一个标签cache=“true”。
默认是同步等待结果阻塞的,支持异步调用。
Dubbo是基于 NIO的非阻塞实现并行调用,客户端不不需要启动多线程即可完成并行调用多个远程服务,相对多线程开销较小,异步调用会返回一个Future对象。异步调用流程图:
可以配置环境点对点直连,绕过注册中心,将以服务接口为单位,忽略注册中心的提供者列列表。
Dubbox是继Dubbo停止维护后,当当网基于Dubbo做的一个扩展项目,如加了服务可Restful调用,更新了开源组件等。
Dubbox和Dubbo本质上没有区别,Dubbox扩展了Dubbo而已,以下扩展出来的功能:
- 支持 REST 风格远程调用(HTTP + JSON/XML);
- 支持基于Kryo和FST的Java高效序列化实现;
- 支持基于Jackson的JSON序列化;
- 支持基于嵌入式Tomcat的HTTP remoting体系;
- 升级Spring至3.x;
- 升级ZooKeeper客户端;
- 支持完全基于Java代码的Dubbo配置。
JDK1.6。
CPU个数+1。
private static final Protocol protocol =
ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
Dubbo里有很多这种代码。这也是一种工厂模式,只是实现类的获取采用了JDK SPI的机制。这么实现的优点是可扩展性强,想要扩展实现,只需要在classpath下增加个文件就可以了,代码零侵入。另外,像上面的Adaptive实现,可以做到调用时动态决定调用哪个实现,但是由于这种实现采用了动态代理,会造成代码调试比较麻烦,需要分析出实际调用的实现类。
1)Spring配置方式;2)Java API配置方式。
Dubbo缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止Spring初始化完成,默认check=“true”,可以通过check="false"关闭检查。
1)timeout:方法调用超时。
2)retries:失败重试次数,默认重试2次。
3)loadbalance:负载均衡算法,默认随机。
4)actives 消费者端,最大并发调用限制。
可以的,启动Dubbo时,消费者会从zookeeper拉取注册的生产者的地址接口等数据,缓存在本地。
每次调用时,按照本地存储的地址进行调用。
注册中心对等集群,任意一台宕机后,将会切换到另一台;注册中心全部宕机后,服务的提供者和消费者仍能通过本地缓存通讯。服务提供者无状态,任一台 宕机后,不影响使用;服务提供者全部宕机,服务消费者会无法使用,并无限次重连等待服务者恢复;
挂掉是不要紧的,但前提是你没有增加新的服务,如果你要调用新的服务,则是不能办到的。
在实际生产中,假如zookeeper注册中心宕掉,一段时间内服务消费方还是能够调用提供方的服务的,实际上它使用的本地缓存进行通讯,这只是dubbo健壮性的一种体现。
dubbo的健壮性表现:
- 监控中心宕掉不影响使用,只是丢失部分采样数据
- 数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务
- 注册中心对等集群,任意一台宕掉后,将自动切换到另一台
- 注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯
- 服务提供者无状态,任意一台宕掉后,不影响使用
- 服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复
注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小。所以,我们可以完全可以绕过注册中心——采用 dubbo 直连 ,即在服务消费方配置服务提供方的位置信息。
xml配置方式:
<dubbo:reference id="userService"
interface="com.test.service.UserService"
url="dubbo://localhost:20880" />
注解方式:
@Reference(url = "127.0.0.1:20880")
HelloService helloService;
内部使用了 Netty、Zookeeper,保证了高性能高可用性。使用 Dubbo 可以将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,可用于提高业务复用灵活扩展,使前端应用能更快速的响应多变的市场需求。
最重要是,分布式架构可以承受更大规模的并发流量。
Spring Container、Jetty Container、Log4j Container。
Dubbo 的服务容器只是一个简单的 Main 方法,并加载一个简单的 Spring 容器,用于暴露服务。
推荐使用 Zookeeper 作为注册中心,还有 Redis、Multicast、Simple 注册中心,但不推荐。
配置 | 配置说明 | 解释 |
---|---|---|
dubbo:service | 服务配置 | 用于暴露一个服务,定义服务的元信息,一个服务可以用多个协议暴露,一个服务也可以注册到多个注册中心 |
dubbo:reference | 引用配置 | 用于创建一个远程服务代理,一个引用可以指向多个注册中心 |
dubbo:protocol | 协议配置 | 用于配置提供服务的协议信息,协议由提供方指定,消费方被动接受 |
dubbo:application | 应用配置 | 用于配置当前应用信息,不管该应用是提供者还是消费者 |
dubbo:module | 模块配置 | 用于配置当前模块信息,可选 |
dubbo:registry | 注册中心配置 | 用于配置连接注册中心相关信息 |
dubbo:monitor | 监控中心配置 | 用于配置连接监控中心相关信息,可选 |
dubbo:provider | 提供方配置 | 当 ProtocolC onfig 和 ServiceCo nfig 某属 性没有配置时,采用此缺省值,可选 |
dubbo:consumer | 消费方配置 | 当 Reference Config 某属性没有配置时,采用此缺省值,可选 |
dubbo:method | 方法配置 | 用于 ServiceCo nfig 和 Reference Config 指 定方法级的配置信息 |
dubbo:argument | 参数配置 | 用于指定方法参数配置 |
默认使用Hessian序列化,还有Duddo、FastJson、Java自带序列化。hessian是一个采用二进制格式传输的服务框架,相对传统soap web service,更轻量,更快速。
http的协议约定了数据传输的方式,hessian也无法改变太多:
1、 hessian中client与server的交互,基于http-post方式。
2、hessian将辅助信息,封装在http header中,比如“授权token”等,我们可以基于http-header来封装关于“安全校验”“meta数据”等。hessian提供了简单的”校验”机制。
3、对于hessian的交互核心数据,比如“调用的方法”和参数列表信息,将通过post请求的body体直接发送,格式为字节流。
4、对于hessian的server端响应数据,将在response中通过字节流的方式直接输出。
hessian的协议本身并不复杂,在此不再赘言;所谓协议(protocol)就是约束数据的格式,client按照协议将请求信息序列化成字节序列发送给server端,server端根据协议,将数据反序列化成“对象”,然后执行指定的方法,并将方法的返回值再次按照协议序列化成字节流,响应给client,client按照协议将字节流反序列话成”对象”。
集群容错方案 | 说明 |
---|---|
Failover Cluster | 失败自动切换,自动重试其他服务器(默认) |
Failfast Cluster | 快速失败,立即报错,只发起一次调用 |
Failsafe Cluster | 失败安全,出现异常时,直接忽略 |
Failback Cluster | 失败自动恢复,记录失败请求,定时重发 |
Forking Cluster | 并行调用多个服务器,只要一个成功即返回 |
Broadcast Cluster | 广播逐个调用所有提供者,任意一个报错即报错 |
dubbo服务发布之后,我们可以利用telnet命令进行调试、管理。Dubbo2.0.5以上版本服务提供端口支持telnet命令。
连接服务:
# 键入回车进入 Dubbo 命令模式
telnet localhost 8090
查看服务列表:
dubbo>ls
com.test.TestService
dubbo>ls com.test.TestService
create
delete
query
ls : 显示服务列表。
ls -l : 显示服务详细信息列表。
ls XxxService:显示服务的方法列表。
ls -l XxxService:显示服务的方法详细信息列表。
Dubbo必须依赖JDK,其他为可选。
Dubbo会在Spring实例化完Bean之后,在刷新容器最后一步发布 ContextRefreshEvent事件的时候,通知实现了ApplicationListener的ServiceBean类进行回调onApplicationEvent事件方法,Dubbo会在这个方法中调用ServiceBean父类ServiceConfig的export方法,而该方法真正实现了服务的(异步或者非异步)发布。
目前暂时不支持,可与通过tcc-transaction框架实现,tcc-transaction是开源的TCC补偿性分布式事务框架。
TCC-Transaction通过Dubbo隐式传参的功能,避免自己对业务代码的入侵。
透明化的远程方法调用:就像调用本地方法一样调用远程方法,只需简单配置, 没有任何API侵入。
软负载均衡及容错机制:可在内网替代F5等硬件负载均衡器,降低成本,减少 单点。
服务自动注册与发现:不再需要写死服务提供方地址,注册中心基于接口名查询 服务提供者的IP地址,并且能够平滑添加或删除服务提供者。
Remoting:网络通信框架,提供对多种NIO框架抽象封装,包括“同步转异 步”和“请求-响应”模式的信息交换方式。
Cluster:服务框架,提供基于接口方法的透明远程过程调用,包括多协议支 持,以及软负载均衡,失败容错,地址路由,动态配置等集群支持。
Registry:服务注册,基于注册中心目录服务,使服务消费方能动态的查找服务 提供方,使地址透明,使服务提供方可以平滑增加或减少机器。
spi:service provider interface,比如你有个接口,现在这个接口有 3 个实现类,那么在系统运行的时候对这个接口到底选择哪个实现类呢?这就需要 spi 了,需要根据指定的配置或者是默认的配置,去找到对应的实现类加载进来,然后用这个实现类的实例对象。你通过配置 接口 A = 实现 A2 ,那么在系统实际运行的时候,会加载你的配置,用实现 A2 实例化一个对象来提供服务。
spi 机制一般用在哪儿?插件扩展的场景,比如说你开发了一个给别人使用的开源框架,如果你想让别人自己写个插件,插到你的开源框架里面,从而扩展某个功能,这个时候 spi 思想就用上了。
Protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getA
Protocol 接口,在系统运行的时候,,dubbo 会判断一下应该选用这个 Protocol 接口的哪个实现类来实例化对象来使用。它会去找一个你配置的 Protocol,将你配置的 Protocol 实现类,加载到 jvm 中来,然后实例化对象,就用你的那个 Protocol 实现类就可以了。
上面那行代码就是 dubbo 里大量使用的,就是对很多组件,都是保留一个接口和多个实现,然后在系统运行的时候动态根据配置去找到对应的实现类。如果你没配置,那就走默认的实现。
服务分层(避免循环依赖)。
调用链路失败监控和报警。
服务鉴权。
每个服务的可用性的监控(接口调用成功率?几个 9?99.99%,99.9%,99%)。
public class HelloServiceMock implements HelloService {
public void sayHello() {
// 降级逻辑
}
}
<dubbo: id ="xxxx" interface ="xx" check ="true" async ="false" timeout="6000" retries="0"/>
如某个服务的接口,要耗费 5s,你这边不能干等着,你这边配置了 timeout 之后,我等待 2s,还没返回,我直接就撤了,不能干等你。
timeout :一般设置为 200ms ,我们认为不能超过 200ms 还没返回。
没有通用的一个方法,这个应该结合业务来保证幂等性。
幂等性,就是说一个接口,多次发起同一个请求,你这个接口得保证结果是准确的,比如不能多扣款、不能多插入一条数据、不能将统计值多加了 1。这就是幂等性。
保证幂等性主要是三点:
其实分布式系统接口的调用顺序,也是个问题,一般来说是不用保证顺序的。因为一旦引入顺序性保障,比如使用分布式锁,会导致系统复杂度上升,而且会带来效率低下,热点数据压力过大等问题。
如果就是要保证顺序,可以用 Dubbo 的一致性 hash 负载均衡策略,将比如某一个订单 id 对应的请求都给分发到某个机器上去,接着就是在那个机器上,因为可能还是多线程并发执行的,你可能得立即将某个订单 id 对应的请求扔一个内存队列里去,强制排队,这样来确保他们的顺序性。
但是这样引发的后续问题就很多,比如说要是某个订单对应的请求特别多。因此还是不建议保证顺序性,可以将有顺序性的多个操作合并到一个操作里。
<dubbo:service interface="com.xxx.XxxService" version="1.0" />
。
- threads:服务线程池大小。
- executes:一个服务提供者并行执行请求上限,即当Provider对一个服务的并发调用到上限后,新调用会Wait,这个时候Consumer可能会超时。在方法上配置dubbo:method则并发限制针对方法,在接口上配置dubbo:service ,则并发限制针对服务。
- 表:避免出现A服务关联B服务的表的数据操作;服务一旦划分了,那么数据库即便没分开,也要当成db表分开了来进行编码;否则AB服务难以进行垂直拆库。
- 避免服务耦合度高,依赖调用;如果出现,考虑服务调优。
- 避免分布式事务,不要拆分过细。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。