dubbo全解

来源:互联网 发布:网络销售策划书 编辑:程序博客网 时间:2024/05/18 00:38

首先先提出几个问题:

1、关于dubbo的几个问题:dubbo是什么?dubbo能干什么?dubbo怎么用——即dubbo工作原理?使用dubbo应注意哪些地方

2、dubbo如何实现集群容错

3、dubbo如何实现负载均衡

4、服务之间如何实现通信

5、多服务,怎么找呢

然后我们带着问题去认识dubbo,寻找答案。。。

1、dubbo是什么?

dubbo是一个分布式的服务框架,致力于提高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。

简言之,dubbo就是一个服务框架,如果没有分布式的需求,其实不需要用的,只有分布式的时候,才需要dubbo这样的分布式框架

本质里,dubbo就是个服务调用的东东。。

说白了就是个远程服务调用的分布式框架(告别webservice模式中的wsdl,以服务者与消费者的方式在dubbo上注册)

——再来一个问题:6、dubbo和webservice比较

dubbo可以和spring无缝集成

2、dubbo能干什么?

1)透明化的远程方法调用,就像调用本地方法一样,只需简单配置,没有任何API侵入

2)软负载均衡及容错机制,可在内网替代F5等硬件负载均衡器,降低成本,减少单点

3)服务自动注册与发现,不再需要写死服务提供方地址,注册中心基于接口名查询服务提供者的IP地址,并且能够平滑添加或删除服务提供者

4)dubbo采用全spring配置方式,透明化接入应用,对应用没有任何API侵入,只需spring加载dubbo的配置即可,dubbo基于spring的schema扩展进行加载

3、dubbo怎么用——即dubbo工作原理?

dubbo的核心:

1)远程通讯:提供多种基于长连接的NIO框架封装,包括多线程模型,序列号,以及“请求-响应”模式的信息交换方式

2)集群容错:提供基于接口方法的透明远程过程调用,包括多协议支持,以及软负载均衡,失败容错,地址路由,动态配置等集群支持

3)自动发现:基于注册中心目录服务,使服务消费方能够动态查找服务提供方,使地址透明,使服务提供方可以平滑增加或减少机器

dubbo主要核心部件

Remoting:网络通信框架,实现了sync-over-async和request-response消息机制。

RPC:一个远程过程调用的抽象,支持负载均衡、容灾和集群功能。

Registry:服务目录框架用于服务的注册和服务事件发布和订阅。(类似第一篇文章中的点菜宝)

dubbo核心架构图:


角色说明:

provider:服务提供方

consumer:服务消费方

registry:服务注册与发现的注册中心

monitor:统计服务的调用次数和调用时间的监控中心

container:服务运行容器

调用关系说明:

0.服务器负责启动、加载、运行服务提供者

1.服务提供者在启动时,向注册中心注册自己提供的服务

2.服务消费者在启动时,向注册中心订阅自己所需的服务

3.注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者

4.服务消费者,从提供者列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,更换另一台调用

5.服务消费者和服务提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心

4、dubbo怎么用?

dubbo基于spring的schema扩展进行加载

服务提供者:

1)下载zookeeper注册中心

2)定义服务接口(该接口需单独打包,在服务提供方和消费方共享)

3)spring配置声明暴露服务

<bean id="demoSevice" class="..."/> //具体的实现Bean

<dubbo:application name="x_provider" /> //提供方信息

<dubbo:egister address="multicast://ip" />//multicast广播注册中心,暴露服务地址

<dubbo:register address="zookeeper://ip:2181" />//zookeeper注册中心暴露服务地址

<dubbo:protocol name="dubbo" port="20880" />//用dubbo协议在20880端口暴露服务

<dubbo:service interface="..." ref="demoService"/>//声明需暴露的服务接口

服务消费者:

applicationContext-dubbo.xml:注册自己需要调用的接口(业务多了,按照业务将拆分成N多个applicationContext-dubbo-xxx.xml)

1)通过spring配置引用远程服务

<dubbo:application name=".._consumer"/>//消费方应用名

<dubbo:register address="zookeeper://ip:2181"/>//使用zookeeper注册中心暴露服务地址

<dubbo:reference id="demoService" interface="..demoService"/>//生成远程服务代理,可以像使用本地bean一样使用demoService

5、使用dubbo应注意的地方(http://blog.csdn.net/hzzhoushaoyu/article/details/43273099)

这个主要是在整个学习及使用过程中记录的,以及一些同事在初识过程问过我的,这边做了整理然后直接列举在下面:
1)服务版本号
  • 引用只会找相应版本的服务

<dubbo:serviceinterface=“com.xxx.XxxService” ref=“xxxService” version=“1.0” /><dubbo:referenceid=“xxxService” interface=“com.xxx.XxxService” version=“1.0”/>

  • 为了今后更换接口定义发布在线时,可不停机发布,使用版本号
  • 2)暴露一个内网一个外网IP问题

为了在测试环境提供一个内网访问的地址和一个办公区访问的地址。

•增加一个指定IP为内网地址的服务协议
•增加一个不指定IP的服务协议,但是在/etc/hosts中hostname对应的IP要为外网IP
上面这种方案是一开始使用的方案,后面发现dubbo在启动过程无论是否配路由还是会一个个去连接,虽然不影响启动,但是由于存在超时所以会影响启动时间,而且每台机器还得特别配置指定IP,后面使用另外一套方案:

  1. 服务不配置ip,绑定到0.0.0.0,自动获取保证获取到是内网IP注册到注册中心即可,如果不是想要的IP,可以在/etc/hosts中通过绑定Hostname指定IP
  2. 内网访问方式通过注册中心或者直连指定内网IP和端口
  3. 外网访问方式通过直连指定外网IP和端口
使用这种方式需要注意做好防火墙控制等,比如在线默认也是不指定IP,会绑定在0.0.0.0,如果非法人员知道调用的外网IP和端口,而且可以直接访问就麻烦了(如果在应用中做IP拦截也成,需要注意有防范措施)。
3)dubbo reference注解问题
前文介绍使用时已经提到过,@Reference只能在spring bean实例对应的当前类中使用,暂时无法在父类使用;如果确实要在父类声明一个引用,可通过配置文件配置dubbo:reference,然后在需要引用的地方跟引用spring bean一样就行
4)服务超时问题
目前如果存在超时,情况基本都在如下几点:
  • 客户端耗时大,也就是超时异常时的client elapsed xxx,这个是从创建Future对象开始到使用channel发出请求的这段时间,中间没有复杂操作,只要CPU没问题基本不会出现大耗时,顶多1ms属于正常
  • IOThread繁忙,默认情况下,dubbo协议一个客户端与一个服务提供者会建立一个共享长连接,如果某个客户端处于特别繁忙而且一直往一个服务提供者塞请求,可能造成IOThread阻塞,一般非常特殊的情况才会出现
  • 服务端工作线程池中线程全部繁忙,接收消息后塞入队列等待,如果等待时间比预想长会引起超时
  • 网络抖动,如果上述情况都排除了,还出现在请求发出后,服务接收请求前超过预想时间,只能归类到网络抖动了,需要SA一起查看问题
  • 服务自身耗时大,这个需要应用自身做好耗时统计,当出现这种情况的时候需要用数据来说明问题及规划优化方案,建议采用缓存埋点的方式统计服务中各个执行阶段的耗时情况,最终如果超过预想时间则把缓存统计的耗时情况打日志,减少日志量,且能够得到更明确的信息
现在我们应用使用过程中发现两种类型的耗时,一种我们目前只能归类到网络抖动,后续需要找运维一起关注这个问题,另外一种是由于一些历史原因,数据库查询容易发生抖动,总有一个时间点会突然多出很多超时。
5)服务保护
服务保护的原则上是避免发生类似雪崩效应,尽量将异常控制在服务周围,不要扩散开。
说到雪崩效应,还得提下dubbo自身的重试机制,默认3次,当失败时会进行重试,这样在某个时间点出现性能问题,然后调用方再连续重复调用,很容易引起雪崩,建议的话还是很据业务情况规划好如何进行异常处理,何时进行重试。
服务保护的话,目前我们主要从以下几个方面来实施,也不成熟,还在摸索:
  • 考虑服务的dubbo线程池类型(fix线程池的话考虑线程池大小)、数据库连接池、dubbo连接数限制是否都合适
  • 考虑服务超时时间和重试的关系,设置合适的值
  • 一定时间内服务异常数较大,则可考虑使用failfast让客户端请求直接返回或者让客户端不再请求
经领导推荐,还在学习Release it,后续有其他想法,再回头来编辑。
6)zkclient问题
前文已经提到过zkclient有两个问题,修改服务器时间会导致容器挂掉;dubbo使用zkclient没有传超时时间导致zookeeper无法连接的时候,直接阻塞Integer.MAX_VALUE。
正在调研curator,目前只能说curator不会在无法连接的时候直接阻塞。
另外zkclient和curator的jar包应该都是jdk1.6编译的,所以系统还在jdk1.5以下的话无法使用。
7)注册中心的分组group和服务的不同实现group
这两个东西完全不同的概念,使用的时候不要弄混了。
registry上可以配置group,用于区分不同分组的注册中心,比如在同一个注册中心下,有一部分注册信息是要给开发环境用的,有一部分注册信息时要给测试环境用的,可以分别用不同的group区分开,目前对这个理解还不透彻,大致就是用于区分不同环境。
service和reference上也可以配置group,这个用于区分同一个接口的不同实现,只有在reference上指定与service相同的group才会被发现,还有前文提到的分组合并结果也是用的这个。

6、dubbo的容错方案

当我们的系统中用到Dubbo的集群环境,因为各种原因在集群调用失败时,Dubbo提供了多种容错方案,缺省为failover重试。

       Dubbo的集群容错在这里想说说他是因为我们实际的项目中出现了此类的问题,因为依赖的第三方项目出现异常,导致dubbo调用超时,此时使用的是默认的集群容错方式,而配置的reties='3',这样前段系统连续掉用了三次服务,结果可想而知.

     先说一下各节点关系:

       这里的Invoker是Provider的一个可调用Service的抽象,Invoker封装了Provider地址及Service接口信息。

        Directory代表多个Invoker,可以把它看成List<Invoker>,但与List不同的是,它的值可能是动态变化的,比如注册中心推送变更。

         Cluster将Directory中的多个Invoker伪装成一个Invoker,对上层透明,伪装过程包含了容错逻辑,调用失败后,重试另一个。

         Router负责从多个Invoker中按路由规则选出子集,比如读写分离,应用隔离等。

         LoadBalance负责从多个Invoker中选出具体的一个用于本次调用,选的过程包含了负载均衡算法,调用失败后,需要重选。

  集群容错模式:

  Failover Cluster

失败自动切换,当出现失败,重试其它服务器。(缺省)

通常用于读操作,但重试会带来更长延迟。

可通过retries="2"来设置重试次数(不含第一次)。正是文章刚开始说的那种情况.

  Failfast Cluster

快速失败,只发起一次调用,失败立即报错。

通常用于非幂等性的写操作,比如新增记录。

  Failsafe Cluster

失败安全,出现异常时,直接忽略。

通常用于写入审计日志等操作。

  Failback Cluster

失败自动恢复,后台记录失败请求,定时重发。

通常用于消息通知操作。

  Forking Cluster

并行调用多个服务器,只要一个成功即返回。

通常用于实时性要求较高的读操作,但需要浪费更多服务资源。

可通过forks="2"来设置最大并行数。

  Broadcast Cluster

广播调用所有提供者,逐个调用,任意一台报错则报错。(2.1.0开始支持)

通常用于通知所有提供者更新缓存或日志等本地资源信息。

重试次数配置如:(failover集群模式生效)

<dubbo:serviceretries="2"/>

或:<dubbo:referenceretries="2"/>

或:<dubbo:reference>

            <dubbo:methodname="findFoo"retries="2"/>

    </dubbo:reference>

集群模式配置如:

<dubbo:servicecluster="failsafe"/>

或:<dubbo:referencecluster="failsafe"/>

7、dubbo负载均衡策略:

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

    RandomLoadBalance

随机,按权重设置随机概率。

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

    RoundRobinLoadBalance

轮循,按公约后的权重设置轮循比率。

存在慢的提供者累积请求问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。

    LeastActiveLoadBalance

最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。

使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。

   ConsistentHashLoadBalance

一致性Hash,相同参数的请求总是发到同一提供者。

当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。

 

       Dubbo的集群容错和负载均衡同样也是Dubbo本身的高级特性.正如我们在说自定义扩展的时候一样,这两个特征同样也可以进行自定义扩展,用户可以根据自己实际的需求来扩展他们从而满足项目的实际需求.

 

8、服务之间如何实现通信?

1)同步调用

REST(JAX-RS,Spring Boot)

RPC(Thrift,Dubbo,HSF)

2)异步消息调用(kafka,Notify,NetaQ)

RESTFUL和RPC比较:

restful基于HTTP,更易实现,服务端技术更灵活,各语言都支持,跨客户端,对客户端无特殊要求,只需封装HTTP的SDK就能调用

RPC:传输协议更高效,安全更可控

特别在一个公司内部,如果有统一的开发规范和统一的服务框架时,开发效率优势更明显

同步:调用问题,性能差(特别是层次多时)

异步:减低调用服务之间的耦合,调用之间的缓冲,确保消息挤压不会冲垮被调用方,同时保证调用方的服务体验,继续干自己该干的活,不至于被后台性能拖慢,付出的代价是一致性的减弱,需要接受数据最终一致性,后台服务一般要实现幂等性,因为消息发送处于性能的考虑一般会有重复;必须引入一个独立的broker

9、这么多服务,怎么找?

微服务架构中,一般每个服务都是有多个拷贝,做负载均衡

一个服务随时可能下线,也可能应对临时访问压力增加新的服务节点

10、那么,服务之间如何相互感知?服务如何管理?

服务发现,有两类做法:

 通过zookeeper等类似技术做服务注册信息的分布式管理

当服务上线时,服务提供者将自己的服务信息注册到ZK(或类似框架),并通过心跳维持长连接,实时更新链接信息

服务调用者通过ZK寻址,根据可定制算法,找到一个服务,还可以将服务信息缓存在本地以提高性能,当服务下线时,ZK会发通知给服务客户端

1)客户端做:

优点是架构简单,扩展灵活,只对服务注册器依赖

2)缺点是客户端要维护所有调用服务的地址,有技术难度,一般大公司都有成熟的内部框架支持,如HSF

服务端做:优点简单,所有服务对前台调用方透明,一般在小公司在云服务上部署的应用蚕蛹的比较多


3 0