分布式中间件之Dubbo详解

发布时间:2025-12-10 11:25:17 浏览次数:7

文章内容输出来源:拉勾教育Java高薪训练营

心得体会: 在拉勾教育高薪训练营里经过三个月多的学习,很辛苦但也收获很多,主要体现在:1.以前是工作遇到了哪方面的问题才去学习并解决,很难形成知识体系,在拉勾高薪训练营里课程内容很丰富,基础回顾加进阶讲解,完美的打造了个人的知识体系,为成为一名合格的架构师奠定了基础;2.值得一说的是“闯关式”的学习模式,若上一个模块的作业未完成就无法学习下一个模块,但有班主任和导师对个人的学习进行督促,可以让我们学员必须牢靠的掌握技术知识点;3.在知识点技术落地方面,以前只是听说过或停留在概念层面,在拉勾教育的课程中,不同的老师会从不同的角度对知识点进行讲解,有些比较难理解的知识点(例如Dubbo的SPI机制),老师一般会从基础简单的案例入手,让你轻松理解并掌握,然后在扩展延伸其重点。特别是源码分析方面,彻底发散了个人思维,理解过后可以写出优雅的代码,比如之前是知道有反射这个概念,也知道有责任链这种设计模式,但不会用也不知道在什么场景下用,经过这次对Dubbo源码的分析理解,成功的使用反射和责任链模式解决了工作上一个不好处理的业务逻辑问题。当然在这里还有一群爱学习的小伙伴,可以一起讨论问题,一起分享技术。是的,如果你现在的能力支撑不了你的梦想,那就让我们站在拉勾教育这个巨人的肩膀上去实现自己的梦想吧!

本文概述: 本文将主要讲解分布式系统中Dubbo框架,主要内容包括Dubbo相关概念、架构设计的演变、注册中心Zookeeper、Dubbo的高级特性、Dubbo源码分析、Dubbo中的设计模式和Dubbo项目实战。
Dubbo知识点思维导图:

一、Dubbo相关概念

1. 分布式

分布式是指把一个系统拆分为多个子系统,每个子系统负责各自的那部分功能,独立部署,各司其职。简单说,分布式就是多个系统在一起作不同的事。

2. 集群

集群是指多个实例共同工作,最简单或最常见的集群是把一个应用复制多份部署。简单说,集群就是多个系统在一起作同样的事 。
常见的集群模式有:
主从集群:一主多从,主从各司其职,主节点负责资源分配和任务调度,从节点负责具体的执行;
主备集群:一主一备,主要是为了解决单点故障问题,便于备份恢复;
注意:分布式的核心体现是拆分;集群的核心体现是复制;所以分布式一定是集群,但集群不一定是分布式。

3. RPC

RPC(remote procedure call)即远程过程调用,比如发送了一个查询用户明细的请求GET/readUser?userId=1234,实际上是调用了服务端的一个方法 readUser(int userId)。即在本地电脑上可以调用一个远程服务端的方法,所以叫远程过程调用。这里的"远"也不一定是跨越网络的,也可以同一台主机的两个进程之间相互交流。RPC并不是一个具体的技术,它指定了一种通信方式,是一种服务间通信的理念。在java中RPC的落地框架比较多,常见的有Hessian、gRPC、Thrift、HSF (High-Speed Service Framework)、Dubbo等。
【Hessian】
Hessian是一个轻量级的remoting onhttp工具,使用简单的方法提供了RMI的功能。基于HTTP协议,采用二进制编解码。
【gRPC】
gRPC是Google 公布的开源软件,基于最新的 HTTP 2.0 协议,并支持常见的众多编程语言。
【Thrift】
Thrift是Facebook的开源 RPC 框架,主要是一个跨语言的服务开发框架。它拥有功能强大的代码生成引擎,无缝地支持C + +,C#,Java,Python和PHP和Ruby。thrift允许你定义一个描述文件,描述数据类型和服务接口。依据该文件,编译器方便地生成RPC客户端和服务器通信代码。
最初由Facebook开发用做系统内个语言之间的RPC通信,2007年由Facebook贡献到apache基金 ,现在是apache下的opensource之一 。支持多种语言之间的RPC方式的通信,比如php语言client可以构造一个对象,调用相应的服务方法来调用java语言的服务,跨越语言的C/S RPC调用。底层通讯基于SOCKET。
【HSF】
HSF全称为High-Speed Service Framework,是一个分布式的服务框架,HSF从分布式应用层面以及统一的发布/调用方式层面提供服务支持,从而可以很容易的开发分布式的应用以及提供或使用公用功能模块,而不用考虑分布式领域中的各种细节技术,例如远程通讯、性能损耗、调用的透明化、同步/异步调用方式的实现等等问题。
【Dubbo】
Dubbo框架是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成。

4. Restful API

RESTful是一种架构风格,其最大的几个特点为:资源、统一接口、URI 和无状态。
资源: 所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,就是一个具体的实在。
统一接口: RESTful 架构风格规定,数据的元操作,即 CRUD(Create,Read,Update 和 Delete,即数据的增删查改)操作,分别对应于HTTP方法:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源,这样就统一了数据操作的接口,仅通过 HTTP 方法,就可以完成对数据的所有增删查改工作。
URL: 可以用一个 URI(统一资源定位符)指向资源,即每个 URI 都对应一个特定的资源,要获取这个资源,访问它的 URI 就可以,因此 URI 就成了每一个资源的地址或识别符。
无状态: 所谓无状态的,即所有的资源,都可以通过URI定位,而且这个定位与其他资源无关,也不会因为其他资源的变化而改变。有状态和无状态的区别,举例如下:
比如查询员工的工资,如果查询工资是需要登录系统,进入查询工资的页面,执行相关操作后,获取工资的多少,则这种情况是有状态的。因为查询工资的每一步操作都依赖于前一步操作,只要前置操作不成功,后续操作就无法执行。如果输入一个 URI 即可得到指定员工的工资,则这种情况是无状态的,因为获取工资不依赖于其他资源或状态。且这种情况下,员工工资是一个资源,由一个 URI 与之对应,可以通过 HTTP 中的 GET 方法得到资源,这是典型的 RESTful 风格。
注意:RPC侧重于动作,即RPC可以通过动作去操作资源;RESTful侧重于资源,是面向资源的设计架构;在传输效率方面,RPC 效率更高,RPC使用自定义的TCP协议,可以让请求报文体积更小,或者使用HTTP2协议,也可以很好的减少报文的体积,提高传输效率;但RPC 实现复杂,流程繁琐;REST调用及测试都很方便。

5. 长连接和短连接

短连接: HTTP1.0中每一个请求和响应都会触发一次TCP连接的建立和断开,此为短连接。
长连接: 在HTTP1.1中增加了keep-alive机制,允许在一个TCP连接上发送多个请求和响应,此为长连接。通俗点说,就是建立连接过后可以持续发送请求,无须再建立连接。HTTP请求头部中的Connection标识是否使用长连接,当为Connection: keep-alive时,表明使用长连接(通常无需设置,默认);当不希望使用长连接,则设为Connection: close。

Dubbo服务调用连接是长连接,Dubbo服务调用是小数据量的通信,针对每一次RPC通信,都会生成一个唯一的id来标识,这样就能区分出一次RPC请求对应的RPC响应了。
长连接和短连接的性能比较:
如果二者的限制条件一样(比如都是同步方式或IO复用方式、同等数据量通信、连接个数相同)情况下,长连接性能理论上要优于短连接,因为前者省去了三次握手和四次挥手过程。
如果二者限制条件不同,绝大多数场景的限制条件都是不同的,如果通信较频繁,使用长连接性能较好;如果通信次数较少,使用短连接更合适,毕竟不用维护长连接。

6. Dubbo框架

Dubbo官网:http://dubbo.apache.org/
官网上说,Apache Dubbo是一款高性能Java RPC框架。所以这里必须指出,我们经常所说的Dubbo不是指Dubbo协议,而是指RPC框架即远程服务调用的分布式框架,具体的说Dubbo框架是RPC理念的落地产品。而本文着重介绍的是Dubbo框架。
在分布式应用场景有高并发,高可扩展和高性能的要求。还涉及到,序列化/反序列化,网络,多线程以及设计模式的问题,而Dubbo 框架对这些进行了封装。
Dubbo底层是使用Netty这样的NIO框架,是基于TCP协议传输的,配合以Hession序列化完成RPC通信。

7. Dubbo协议

Dubbo协议是阿里巴巴自己实现的一种网络应用层协议,传输层还是TCP,Dubbo协议与HTTP协议、FTP协议,SMTP协议等这些应用层协议是并列的概念。

8. Dubbox框架

Dubbox 是一个分布式服务框架,其前身是阿里巴巴开源项目Dubbo ,被国内电商及互联网项目中使用,后期阿里巴巴停止了该项目的维护,当当网便在Dubbo基础上进行优化,并继续维护,为了与原有的Dubbo区分,故将其命名为Dubbox。Dubbox弥补了Dubbo中没有REST框架的不足,即Dubbo服务端服务可以通过dubbox快速包装为REST风格的服务。

9. Zookeeper

ZooKeeper 是一个开源的分布式协调服务,它是服务集群的管理者,监视着集群中各个服务节点的状态,并根据节点提交的反馈进行合理操作。最终,将简单易用的接口和性能高效、功能稳定的系统提供给用户。
分布式应用程序基于 Zookeeper 可以实现数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、分布式锁和分布式队列等功能。
ZooKeeper将作为注册中心与Dubbo结合使用。

10. Dubbo和Zookeeper的关系

Dubbo为服务间提供了调用,主要用于管理分布式系统,从后面的Dubbo架构中我们可以看到Dubbo的工作需要一个非常重要的组件——注册中心(Register),而Zookeeper是分布式协调的治理工具,它能很好的提供服务注册和发现的功能,这样Dubbo和Zookeeper就完美的相遇了,当然Dubbo官网也推荐注册中心使用Zookeeper。简单来说,打个比方:每个dubbo服务就是动物园的动物,zookeeper是动物园。如果游客想看动物的话那么就去动物园看。比如你要看老虎,那么动物园有老虎你才能看到。换句话说我们把很多不同的dubbo服务(动物)放到zookeeper(动物园中)提供给我们游客进行观赏。这个过程中三个关键:场所、提供者、消费者。
再说一个分布式的项目,提供者与消费者被拆分了开来,部署在不同的tomcat中,在消费者中需要调用提供者的接口,但是两个运行在不同tomcat下的服务无法直接互调接口,那么就可以通过zookeeper和dubbo实现。就好比把动物放到动物园,我们要看了直接去动物园就行。而不能直接去动物生活的地方去看,会有性命安全之忧(比如你去看老虎)。

我们通过Dubbo建立提供者服务,并且到Zookeeper上面注册,在Zookeeper上填写对应的Dubbo服务的IP及端口号。即Zokeeper (注册中心)主要功能是服务的注册与发现,Dubbo(远程服务调用的分布式框架)主要实现应用服务与Zokeeper注册中心的链接调用(类似JDBC工具类)。

二、架构设计的演变

1. 单体架构

单体架构所有模块和功能都集中在一个项目中 ,部署时也是将项目所有功能整体部署到服务器中。

优点:
(1)小项目开发快,成本低
(2)架构简单
(3)易于测试
(4)易于部署
缺点:
(1)大项目模块耦合严重,不易开发维护,沟通成本高
(2)新增业务困难
(3)核心业务与边缘业务混合在一块,出现问题互相影响

2. 垂直架构

根据业务把项目垂直切割成多个项目,因此这种架构称之为垂直架构。

优点:
(1)系统拆分实现了流量分担,解决了并发问题
(2)可以针对不同系统进行优化
(3)方便水平扩展,负载均衡,容错率提高
(4)系统间相互独立,互不影响,新的业务迭代时更加高效
缺点:
(1)服务系统之间接口调用硬编码
(2)搭建集群之后,实现负载均衡比较复杂
(3)服务系统接口调用监控不到位,调用方式不统一
(4)数据库资源浪费,充斥慢查询,主从同步延迟大

3. 分布式架构(SOA )

SOA全称为Service Oriented Architecture,即面向服务的架构 。它是在垂直划分的基础上,将每个项目拆分出多个具备松耦合的服务,一个服务通常以独立的形式存在于操作系统进程中。各个服务之间通过网络调用,这使得构建在各种各样的系统中的服务可以 以一种统一和通用的方式进行交互。

优点:
(1)服务以接口为粒度,为开发者屏蔽远程调用底层细节,使用Dubbo面向接口远程方法调用屏蔽了底层调用细节
(2)业务分层以后架构更加清晰,并且每个业务模块职责单一 扩展性更强
(3)数据隔离,权限回收,数据访问都通过接口,让系统更加稳定安全
(4)服务应用本身无状态化,这里的无状态化指的是应用本身不做内存级缓存,而是把数据存入数据库
(5)服务责任易确定,每个服务可以确定责任人,这样更容易保证服务质量和稳定
缺点:
(1)粒度控制复杂,如果没有控制好服务的粒度,服务的模块就会越来越多,就会引发超时、分布式事务等问题
(2)服务接口数量不宜控制,容易引发接**炸,所以服务接口建议以业务场景进行单位划分,并对相近的业务做抽象,防止接**炸
(3)版本升级兼容困难,尽量不要删除方法、字段,枚举类型的新增字段也可能不兼容
(4)调用链路长,服务质量不可监控,调用链路变长,下游抖动可能会影响到上游业务,最终形成连锁反应,服务质量不稳定,同时链路的变长使得服务质量的监控变得困难

4. 微服务架构

微服务架构是SOA架构的一种扩展,这种架构模式下它拆分粒度更小、服务更独立。把应用拆分成为一个个微小的服务,不同的服务可以使用不同的开发语言和存储,服务之间往往通过Restful等轻量级通信。微服务架构关键在于微小、独立、轻量级通信。
微服务是在SOA上做的升华 , 粒度更加细致,微服务架构强调的一个重点是“业务需要彻底的组件化和服务化”。

优点:
(1)微服务很小,便于特定业务功能的聚焦
(2)微服务很小,每个微服务都可以被一个小团队单独实施(开发、测试、部署上线、运维),团队合作一定程度解耦,便于实施敏捷开发
(3)微服务很小,便于重用和模块之间的组装
(4)微服务很独立,不同的微服务可以使用不同的语言开发,松耦合
(5)微服务架构下,我们更容易引入新技术
(6)微服务架构下,我们可以更好的实现DevOps开发运维一体化

缺点:
(1)微服务架构下,分布式复杂难以管理,当服务数量增加,管理将越加复杂
(2)微服务架构下,分布式链路跟踪难等

三、Dubbo架构概述

1. Dubbo基本架构

节点角色名称
Provider暴露服务的服务提供方
Consumer调用远程服务的服务消费方
Registry服务注册与发现的注册中心
Monitor统计服务的调用次数和调用时间的监控中心
Container服务运行容器,负责启动、加载、运行服务提供者

调用关系说明:
虚线:代表异步调用
实线:代表同步访问
蓝色虚线:代表启动时完成的功能
蓝绿色虚线:代表程序运行中执行的功能
调用流程:
0:服务运行容器运行容器初始化,启动、加载、运行服务提供者
1:服务提供者在服务容器启动时,向注册中心注册自己提供的服务
2:服务消费者在启动时,向注册中心订阅自己所需的服务
3:注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心会基于长连接推送变更数据给消费者
4:服务消费者从提供者地址列表中基于软负载均衡算法选一台提供者进行调用,如果调用失败,则重新选择一台
5:服务提供者和消费者在内存中的调用次数和调用时间会定时每分钟发送给监控中心

2. Dubbo的服务治理

5. Dubbo的负载均衡

Dubbo 的负载均衡策略有四种(缺省为Random随机调用):
Random, 随机,按照权重设置随机概率做负载均衡。
RoundRobin, 轮询,按照公约后的权重设置轮询比例。
LeastActive, 按照活跃数调用,活跃度差的被调用的次数多。活跃度相同的 Invoker 进行随机调用。
ConsistentHash, 一致性 Hash,相同参数的请求总是发到同一个提供者。

6. Dubbo的过滤器

6.1 Dubbo的Filter介绍

在服务的调用过程中,在服务消费者调用服务提供者的前后,都会调用 Filter(过滤器)。可以针对消费者和提供者配置对应的过滤器,由于过滤器在RPC执行过程中都会被调用,所以为了提高性能需要根据具体情况配置。Dubbo框架中有自带的系统过滤器,服务提供者有11个,服务消费者有5个。

6.2 Dubbo的Filter使用

Dubbo中过滤器的使用可以通过@Activate的注释,或者配置文件实现。
举例:配置文件实现过滤器

<!-- 消费者过滤器配置 --><dubbo:reference filter="filter01,filter02"/><!-- 消费者默认过滤器配置,拦截reference过滤器 --><dubbo:consumer filter="filter03,filter04"/><!-- 提供者过滤器配置 --><dubbo:service filter="filter05"/><!-- 提供者默认过滤器配置,拦截service过滤器 --><dubbo:provider filter="filter06,filter07"/>

过滤器的使用遵循以下几个规则:
(1)过滤器顺序,过滤器执行是有顺序的。例如,用户定义的过滤器的过滤顺序默认会在系统过滤器之后。又例如,上图中 filter=“filter01, filter02”,filter01 过滤器执行就在 filter02 之前。
(2)过滤器失效,如果针对某些服务或者方法不希望使用某些过滤器,可以通过“-”(减号)的方式使该过滤器失效。例如,filter=“-filter01”。
(3)过滤器叠加,如果服务提供者和服务消费者都配置了过滤器,那么两个过滤器会被叠加生效。
由于,每个服务都支持多个过滤器,而且过滤器之间有先后顺序。因此在设计上Dubbo采用了装饰器模式,将 Invoker 进行层层包装,每包装一层就加入一层过滤条件。在执行过滤器的时候就好像拆开一个一个包装一样。

7. Dubbo的协议

7.1 Dubbo 支持的通信协议

(1)dubbo协议
Dubbo默认的通信协议就是dubbo 协议,单一长连接,进行的是 NIO 异步通信,基于 hessian 作为序列化协议。适用场景:传输数据量小(每次请求在 100kb 以内),但是并发量很高。

(2)rmi协议
支持java二进制序列化,多个短连接。适用场景:消费者和提供者数量差不多的情况,适用于文件的传输,一般较少用。

(3)hessian协议
支持hessian 序列化协议,多个短连接。适用场景:提供者数量比消费者数量多的情况,适用于文件的传输,跨语言传输,一般较少用。

(4)http协议
支持 json序列化协议,多个短连接,采用同步传输。适用场景:提供者数量比消费者数量多的情况,数据包混合。

(5)webservice协议
支持SOAP文本序列化协议,多个短连接,采用同步传输。适用场景:系统集成和跨语言调用。

7.2 Dubbo支持的序列化协议

Dubbo 支持hession、java 二进制序列化、json序列化、SOAP 文本序列化多种序列化协议。但是 hessian 是其默认的序列化协议。

7.3 Dubbo协议详解

Dubbo的缺省协议,使用基于 mina 1.1.7 和 hessian 3.2.1 的 tbremoting交互。

  • 连接个数:单连接
  • 连接方式:长连接
  • 传输协议:TCP协议
  • 传输方式:NIO 异步传输
  • 序列化协议:Hessian二进制序列化协议
  • 适用范围:传入传出参数数据包较小(建议小于100K),消费者比提供者个数多,单一消费者无法压满提供者,尽量不要用 dubbo 协议传输大文件或超大字符串。
  • 适用场景:常规远程服务方法调用
    Dubbo协议采用固定长度的消息头(16字节)和不定长度的消息体来进行数据传输,消息头定义了底层框架(netty)在IO线程处理时需要的信息,协议的报文格式如下:

    协议体包含了传输的主要内容,它是由 16 字节长的报文组成,每个字节包括8个二进制位。协议详情如下:
Magic - Magic High & Magic Low (16 bits)标识协议版本号,Dubbo 协议:0xdabbSerialization ID (5 bit)标识序列化类型:比如 fastjson 的值为6。Event (1 bit)标识是否是事件消息,例如,心跳事件。如果这是一个事件,则设置为1。Two Way (1 bit)仅在 Req/Res 为1(请求)时才有用,标记是否期望从服务器返回值。如果需要来自服务器的返回值,则设置为1。Req/Res (1 bit)标识是请求或响应。请求: 1; 响应: 0。Status (8 bits)仅在 Req/Res 为0(响应)时有用,用于标识响应的状态。20 - OK30 - CLIENT_TIMEOUT31 - SERVER_TIMEOUT40 - BAD_REQUEST50 - BAD_RESPONSE60 - SERVICE_NOT_FOUND70 - SERVICE_ERROR80 - SERVER_ERROR90 - CLIENT_ERROR100 - SERVER_THREADPOOL_EXHAUSTED_ERRORRequest ID (64 bits)标识唯一请求。类型为long。Data Length (32 bits)序列化后的内容长度(可变部分),按字节计数。int类型。Variable Part被特定的序列化类型(由序列化 ID 标识)序列化后,每个部分都是一个 byte [] 或者 byte如果是请求包 ( Req/Res = 1),则每个部分依次为:Dubbo versionService nameService versionMethod nameMethod parameter typesMethod argumentsAttachments如果是响应包(Req/Res = 0),则每个部分依次为:返回值类型(byte),标识从服务器端返回的值类型:返回空值:RESPONSE_NULL_VALUE 2正常响应值: RESPONSE_VALUE 1异常:RESPONSE_WITH_EXCEPTION 0返回值:从服务端返回的响应bytes

注意:对于(Variable Part)变长部分,当前版本的Dubbo框架使用json序列化时,在每部分内容间额外增加了换行符作为分隔,请在Variable Part的每个part后额外增加换行符, 如:

Dubbo version bytes (换行符)Service name bytes (换行符)...

优点:
协议设计上很紧凑,能用 1 个 bit 表示的,不会用一个 byte 来表示,比如 boolean 类型的标识。请求、响应的 header 一致,通过序列化器对 content 组装特定的内容,代码实现起来简单。
可以改进的点:
(1)类似于 http 请求,通过 header 就可以确定要访问的资源,而 Dubbo 需要涉及到用特定序列化协议才可以将服务名、方法、方法签名解析出来,并且这些资源定位符是 string 类型或者 string数组,很容易转成 bytes,因此可以组装到 header 中。类似于 http2 的 header 压缩,对于RPC调用的资源也可以协商出来一个int来标识,从而提升性能,如果在 header 上组装资源定位符的话,该功能则更易实现。
(2)通过 req/res 是否是请求后,可以精细定制协议,去掉一些不需要的标识和添加一些特定的标识。比如 status , twoWay 标识可以严格定制,去掉冗余标识。还有超时时间是作为 Dubbo 的attachment 进行传输的,理论上应该放到请求协议的header中,因为超时是网络请求中必不可少的。提到 attachment ,通过实现可以看到 attachment 中有一些是跟协议 content 中已有的字段是重复的,比如 path 和 version 等字段,这些会增大协尺寸。
(3)Dubbo 会将服务名com.alibaba.middleware.hsf.guide.api.param.ModifyOrderPriceParam ,转换为com/alibaba/middleware/hsf/guide/api/param/ModifyOrderPriceParam; ,理论上是不必要的,最后追加一个 ; 即可。
(4)Dubbo 协议没有预留扩展字段,没法新增标识,扩展性不太好,比如新增 响应上下文 的功能,只有改协议版本号的方式,但是这样要求客户端和服务端的版本都进行升级,对于分布式场景很不友好。

8. Dubbo的线程池

8.1 Dubbo线程池的使用

DUBBO提供多种线程池策略,选择线程池策略并在配置文件指定threadpool属性,如下:

<dubbo:protocol name="dubbo" threadpool="fixed" threads="100" /><dubbo:protocol name="dubbo" threadpool="cached" threads="100" /><dubbo:protocol name="dubbo" threadpool="limited" threads="100" /><dubbo:protocol name="dubbo" threadpool="eager" threads="100" />

8.2 Dubbo提供的4种线程池策略

这里的线程池ThreadPool也是一个扩展接口SPI,Dubbo提供了该扩展接口的一些实现,具体实现如下:CachedThreadPool:创建一个自适应线程池,当线程处于空闲1分钟时候,线程会被回收,当有新请求到来时候会创建新线程

public class CachedThreadPool implements ThreadPool {@Overridepublic Executor getExecutor(URL url) {//线程名,默认为DubboString name = url.getParameter(THREAD_NAME_KEY, DEFAULT_THREAD_NAME);//核心线程数,默认为0int cores = url.getParameter(CORE_THREADS_KEY, DEFAULT_CORE_THREADS);//可使用的最大线程数,默认为Integer.MAX_VALUEint threads = url.getParameter(THREADS_KEY, Integer.MAX_VALUE);//队列数,默认为0int queues = url.getParameter(QUEUES_KEY, DEFAULT_QUEUES);//线程存活时间,默认60sint alive = url.getParameter(ALIVE_KEY, DEFAULT_ALIVE);//根据queue决定是SynchronousQueue还是LinkedBlockingQueue,默认queue=0,所以是SynchronousQueuereturn new ThreadPoolExecutor(cores, threads, alive, TimeUnit.MILLISECONDS,queues == 0 ? new SynchronousQueue<Runnable>() :(queues < 0 ? new LinkedBlockingQueue<Runnable>(): new LinkedBlockingQueue<Runnable>(queues)),//NamedInternalThreadFactory主要用于修改线程名,方便我们排查问题。// AbortPolicyWithReport对拒绝的任务打印日志,也是方便排查问题。new NamedInternalThreadFactory(name, true), new AbortPolicyWithReport(name, url));}}

LimitedThreadPool:创建一个线程池,这个线程池中线程个数随着需要量动态增加,但是数量不超过配置的阈值的个数,另外空闲线程不会被回收,会一直存在

public class LimitedThreadPool implements ThreadPool {@Overridepublic Executor getExecutor(URL url) {//线程名,默认为DubboString name = url.getParameter(THREAD_NAME_KEY, DEFAULT_THREAD_NAME);//核心线程数,默认为0int cores = url.getParameter(CORE_THREADS_KEY, DEFAULT_CORE_THREADS);//可使用的最大线程数,默认为200int threads = url.getParameter(THREADS_KEY, DEFAULT_THREADS);//队列数,默认为0int queues = url.getParameter(QUEUES_KEY, DEFAULT_QUEUES);//线程存活时间,默认Long.MAX_VALUE,说明线程数只会增加不会减少return new ThreadPoolExecutor(cores, threads, Long.MAX_VALUE, TimeUnit.MILLISECONDS,//根据queue决定是SynchronousQueue还是LinkedBlockingQueue,默认queue=0,所以是SynchronousQueuequeues == 0 ? new SynchronousQueue<Runnable>() :(queues < 0 ? new LinkedBlockingQueue<Runnable>(): new LinkedBlockingQueue<Runnable>(queues)),//NamedInternalThreadFactory主要用于修改线程名,方便我们排查问题。// AbortPolicyWithReport对拒绝的任务打印日志,也是方便排查问题。new NamedInternalThreadFactory(name, true), new AbortPolicyWithReport(name, url));}}

FixedThreadPool:创建一个复用固定个数线程的线程池

public class FixedThreadPool implements ThreadPool {@Overridepublic Executor getExecutor(URL url) {//线程名,默认为DubboString name = url.getParameter(THREAD_NAME_KEY, DEFAULT_THREAD_NAME);//可使用的最大线程数,默认为200int threads = url.getParameter(THREADS_KEY, DEFAULT_THREADS);//队列数,默认为0int queues = url.getParameter(QUEUES_KEY, DEFAULT_QUEUES);//线程存活时间,默认0sreturn new ThreadPoolExecutor(threads, threads, 0, TimeUnit.MILLISECONDS,//根据queue决定是SynchronousQueue还是LinkedBlockingQueue,默认queue=0,所以是SynchronousQueuequeues == 0 ? new SynchronousQueue<Runnable>() :(queues < 0 ? new LinkedBlockingQueue<Runnable>(): new LinkedBlockingQueue<Runnable>(queues)),//NamedInternalThreadFactory主要用于修改线程名,方便我们排查问题。// AbortPolicyWithReport对拒绝的任务打印日志,也是方便排查问题。new NamedInternalThreadFactory(name, true), new AbortPolicyWithReport(name, url));}}

EagerThreadPool:创建一个线程池,这个线程池当所有核心线程都处于忙碌状态时候,创建新的线程来执行新任务,而不是把任务放入线程池阻塞队列

public class EagerThreadPool implements ThreadPool {@Overridepublic Executor getExecutor(URL url) {//线程名,默认为DubboString name = url.getParameter(THREAD_NAME_KEY, DEFAULT_THREAD_NAME);//核心线程数,默认为0int cores = url.getParameter(CORE_THREADS_KEY, DEFAULT_CORE_THREADS);//可使用的最大线程数,默认为Integer.MAX_VALUEint threads = url.getParameter(THREADS_KEY, Integer.MAX_VALUE);//队列数,默认为0int queues = url.getParameter(QUEUES_KEY, DEFAULT_QUEUES);//线程存活时间,默认60sint alive = url.getParameter(ALIVE_KEY, DEFAULT_ALIVE);// init queue and executor//自定义实现TaskQueue,默认长度为1,使用时要自己配置下;它会在提交任务时判断是否// currentPoolSize<submittedtaskcount<maxpoolsize,// 然后通过它的offer方法返回false导致增加工作线程TaskQueue<Runnable> taskQueue = new TaskQueue<Runnable>(queues <= 0 ? 1 : queues);//EagerThreadPoolExecutor继承ThreadPoolExecutor,对当前线程池提交的任务数submittedTaskCount进行记录EagerThreadPoolExecutor executor = new EagerThreadPoolExecutor(cores,threads,alive,TimeUnit.MILLISECONDS,taskQueue,new NamedInternalThreadFactory(name, true),new AbortPolicyWithReport(name, url));taskQueue.setExecutor(executor);return executor;}}

8.3 Dubbo 线程池打满异常分析及解决

问题介绍:Dubbo 默认的线程模型:Dubbo 服务端每次接收到一个 Dubbo 请求,便交给一个线程池处理,该线程池默认有200个线程,如果200个线程都不处于空闲状态,则客户端会报出如下异常:

Caused by: java.util.concurrent.ExecutionException: org.apache.dubbo.remoting.RemotingException: Server side(192.168.1.101,20880) threadpool is exhausted ...

服务端会打印 WARN 级别的日志:

[DUBBO] Thread pool is EXHAUSTED!

问题原因分析:
(1)客户端/服务端超时时间设置不合理,导致请求无限等待,耗尽了线程数
(2)客户端请求量过大,服务端无法及时处理,耗尽了线程数
(3)服务端由于 fullgc 等原因导致处理请求较慢,耗尽了线程数
(4)服务端由于数据库、Redis、网络 IO 阻塞问题,耗尽了线程数
原因可能很多,但究其根本,都是因为业务上出了问题,导致 Dubbo 线程池资源耗尽了。
问题定位分析:
可以利用阿里巴巴开源的 Java 诊断工具Arthas(阿尔萨斯)中的dashboard 命令和thread 命令进行分析。
(1)使用dashboard命令查看线程全局信息

$ dashboard

【$ dashboard】命令可以查看到线程 ID、线程名、线程组名、线程优先级、线程的状态、线程消耗的 CPU 占比、线程运行总时间、线程当前的中断位状态、是否是 daemon 线程
利用下面命令可以根据线程池名筛选出 Dubbo 服务端线程:

dashboard | grep "DubboServerHandler"

(2)使用thread 命令查看线程具体情况
查看当前最忙的前 n 个线程:

$ thread -n 3

显示所有线程信息:

$ thread

显示当前阻塞其他线程的线程:

$ thread -b

显示指定状态的线程:

$ thread --state TIMED_WAITING

线程状态一共有 [RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, NEW, TERMINATED] 6 种。
查看指定线程的运行堆栈:

$ thread 46

分析线程池打满异常的常见问题:
(1)阻塞类问题。例如数据库连接不上导致卡死,运行中的线程基本都应该处于 BLOCKED 或者 TIMED_WAITING 状态,我们可以借助 thread --state 定位到;
(2)繁忙类问题。例如 CPU 密集型运算,运行中的线程基本都处于 RUNNABLE 状态,可以借助于 thread -n 来定位出最繁忙的线程;
(3)GC 类问题。很多外部因素会导致该异常,例如 GC 就是其中一个因素,这里就不能仅仅借助于 thread 命令来排查了;
(4)定点爆破。还记得在前面我们通过 grep 筛选出了一批 Dubbo 线程,可以通过 thread ${thread_id} 定向的查看堆栈,如果统计到大量的堆栈都是一个服务时,基本可以断定是该服务出了问题,至于说是该服务请求量突然激增,还是该服务依赖的某个下游服务突然出了问题,还是该服务访问的数据库断了,那就得根据堆栈去判断了。
Dubbo 线程池打满异常问题排查及Dubbo优化:
(1)排查业务异常
(2)合理设置客户端/服务端超时时间
(3)服务端扩容,调整 Provider 端的 dubbo.provider.threads 参数大小,默认 200,可以适当提高
(4)客户端限流,调整 Consumer 端的 dubbo.consumer.actives 参数,控制消费者调用的速率

9. Dubbo的路由规则

路由是决定一次请求中需要发往目标机器的重要判断,通过对其控制可以决定请求的目标机器。我们可以通过创建这样的规则来决定一个请求会交给哪些服务器去处理。

9.1 路由规则快速入门

(1)提供两个提供者(一台本机作为提供者,一台为其他的服务器),每个提供者会在调用时可以返回不同的信息 以区分提供者。
(2)针对于消费者,我们这里通过一个死循环,每次等待用户输入,再进行调用,来模拟真实的请求
情况。通过调用的返回值,确认具体的提供者。
(3)我们通过ipconfig来查询到我们的IP地址,并且单独启动一个客户端,来进行如下配置(这里假设
我们希望隔离掉本机的请求,都发送到另外一台机器上)。
(4)通过这个程序执行后,我们就通过消费端不停的发起请求,看到真实的请求都发到了除去本机以
外的另外一台机器上。

9.2 路由规则详解

本质上是通过在Zookeeper中保存一个节点数据,来记录路由规则。消费者会通过监听这个服务的路径,来感知整个服务的路由规则配置,然后进行适配。主要路由配置的参数如下:
route:// 表示路由规则的类型,支持条件路由规则和脚本路由规则,可扩展,必填。
0.0.0.0 表示对所有IP地址生效,如果只想对某个IP的生效,请填入具体IP,必填。
com.lagou.service.HelloService 表示只对指定服务生效,必填。
category=routers 表示该数据为动态配置类型,必填。
dynamic : 是否为持久数据,当指定服务重启时是否继续生效。必填。
runtime : 是否在设置规则时自动缓存规则,如果设置为true则会影响部分性能。
rule : 是整个路由最关键的配置,用于配置路由规则。
… => … 在这里 => 前面的就是表示消费者方的匹配规则,可以不填(代表全部)。 => 后方则必须填写,表示当请求过来时,如果选择提供者的配置。官方这块儿也给出了详细的示例,可以按照那里来讲。其中使用最多的便是host参数。 必填。

10. Dubbo的管理控制台 dubbo-admin

10.1 dubbo-admin的作用

主要包含:服务管理 、 路由规则、动态配置、服务降级、访问控制、权重调整、负载均衡等管理功能
如我们在开发时,需要知道Zookeeper注册中心都注册了哪些服务,有哪些消费者来消费这些服务。我们可以通过部署一个管理中心来实现。其实管理中心就是一个web应用,原来是war(2.6版本以前)包需要部署到tomcat即可。现在是jar包可以直接通过java命令运行。

10.2 控制台安装步骤

(1)从git 上下载项目 https://github.com/apache/dubbo-admin
(2)修改项目下的dubbo.properties文件
注意:dubbo.registry.address对应的值需要对应当前使用的Zookeeper的ip地址和端口号

dubbo.registry.address=zookeeper://zk所在机器ip:zk端口dubbo.admin.root.password=rootdubbo.admin.guest.password=guest

(3)切换到项目所在的路径,使用mvn 打包
mvn clean package -Dmaven.test.skip=true
(4)java 命令运行
java -jar 对应的jar包

10.3 使用控制台

(1)访问http://IP:端口;
(2)输入用户名root,密码root;
(3)点击菜单查看服务提供者和服务消费者信息。

六、Dubbo源码分析

本次的源码分析将基于Dubbo 2.7.8的版本。

1. Dubbo源码环境构建

源码下载、编译和导入步骤如下:
(1)Dubbo的项目在github中的地址为: https://github.com/apache/dubbo
(2)进入需要进行下载的地址,执行 git clone https://github.com/apache/dubbo.git
(3)为了防止master中代码不稳定,进入Dubbo项目 cd dubbo 可以切入到最近的release分支 git
checkout 2.7.8-release
(4)进行本地编译,进入Dubbo项目 cd dubbo , 进行编译操作 mvn clean install -DskipTests
(5)使用IDEA引入项目。

2. Dubbo分层设计

Dubbo整体结构设计大致上可分为三层,分别是:
(1)业务层:即处理业务逻辑层
(2)RPC 层:即远程过程调用层
(3)Remoting 层:即远程数据传输层

组件名称描述
Service业务层,包括业务代码的实现,比如接口、实现类,直接面向开发者
Config配置层,对外提供配置,以ServiceConfig和ReferenceConfig为核心,可以直接初始化配置类,也可以解析配置文件生成
Proxy服务代理层,无论是生产者还是消费者,框架都会产生一个代理类,它用来调用远程接口,整个过程对上层透明,就是业务层对远程调用无感
Registry注册中心层,封装服务地址的注册与发现,以服务的URL为中心
Cluster路由层 (集群容错层),负责远程调用的容错策略,负载均衡策略以及路由策略,并且它桥接注册中心,以Invoker为核心
Monitor监控层,负责监控RPC调用相关的信息,比如调用次数、成功失败的情况、调用时间等
Protocol远程调用层,封装RPC调用,无论是服务的暴露还是服务的引用都是在Protocol中作为主功能入口,负责Invoker的整个生命周期,Dubbo中所有的模型都向Invoker靠拢
Exchange信息交换层,建立Request-Response模型,封装请求和响应模式, 比如把请求由同步 转换成异步
Transport网络传输层,统一网络传输的接口,比如netty和mina统一为一个网络传输接口
Serialize数据序列化层,负责管理整个框架中的数据传输的序列化和反序列化

3. Dubbo远程调用工作流程

Dubbo 框架是用来处理分布式系统中,服务发现与注册以及调用问题的,并且管理调用过程。

Dubbo远程调用流程:
0.服务提供者在启动的时候,会通过读取一些配置将服务实例化;
1.Proxy 封装服务调用接口,方便调用者调用。客户端获取 Proxy 时,可以像调用本地服务一样,调用远程服务。2.Proxy 在封装时,需要调用 Protocol 定义协议格式,例如:Dubbo Protocol;
3.将 Proxy 封装成 Invoker,它是真实服务调用的实例;
4.将 Invoker 转化成 Exporter,Exporter 只是把 Invoker 包装了一层,是为了在注册中心中暴露自己,方便消费者使用;
5.将包装好的 Exporter 注册到注册中心;
6.服务消费者建立好实例,会到服务注册中心订阅服务提供者的元数据。元数据包括服务 IP 和端口以及调用方式(Proxy);
7.消费者会通过获取的 Proxy 进行调用。通过服务提供方包装过程可以知道,Proxy 实际包装了 Invoker 实体,因此需要使用 Invoker 进行调用;
8.(9)在 Invoker 调用之前,通过 Directory 获取服务提供者的 Invoker 列表。在分布式的服务中有可能出现同一个服务,分布在不同的节点上;
9.(10)通过路由规则了解,服务需要从哪些节点获取;
10.(8)Invoker 调用过程中,通过 Cluster 进行容错,如果遇到失败策略进行重试;
11.调用中,由于多个服务可能会分布到不同的节点,就要通过 LoadBalance 来实现负载均衡;
12.Invoker调用之前还需要经过 Filter,它是一个过滤链,用来处理上下文,限流和计数的工作;
13.生成过滤以后的 Invoker;
14.用 Client 进行数据传输;
15.Codec 会根据 Protocol 定义的协议,进行协议的构造;
16.构造完成的数据,通过序列化 Serialization 传输给服务提供者;
17.Request 已经到达了服务提供者,它会被分配到线程池(ThreadPool)中进行处理;
18.Request被线程中的server执行;
19.Server 拿到请求以后查找对应的 Exporter(包含有 Invoker);
20.由于Exporter也会被 Filter 层层包裹;
21.通过 Filter 以后获得 Invoker;
22.最后,对服务提供者实体进行调用。

4. Dubbo源码框架设计

3.2 依赖检查问题概述

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

3.3 依赖检查问题的解决方案

可以通过check=“false” 关闭检查,比如,测试时,有些服务不关心,或者出现了循环依赖,必须有一方先启动,或Spring 容器是懒加载的,或者通过 API 编程延迟引用服务,需要关闭关闭 检查,否则服务临时不可用时,会抛出异常,拿到 null 引用,如果check=“false” ,总是会返回引用,当服务恢复时,能自动连上。
对于属性配置可以使用以下两种方式修改:
(1)在消费者consumer-service 中@Reference 加入check=false即可
(2)修改配置的属性文件,格式是:dubbo.reference.(报名+接口名).check=false

dubbo.reference.com.tdd.service.UserService.check=false

对于XML配置可以修改配置文件如下:

<dubbo:reference version="1.0" interface="com.tdd.service.UserService" check="false"/>

4. Dubbo的直连操作

4.1 概述

在开发及测试环境下,或注册中心不可用的情况下,经常需要绕过注册中心,只测试指定服务提供者,这时可能需要点对点直连,点对点直连将以服务接口为单位,忽略注册中心的提供者列表,A接口配置点对点,不影响 B 接口从注册中心获取列表。这种点对点的直接连接指定的服务就是Dubbo的直连模式。所以,Dubbo服务消费者Consumer不一定要从注册中心获取Dubbo服务提供者列表也可以工作,即消费端强制直连提供端。
注意:一旦使用了直连就失去负载均衡的能力。

4.2 配置使用

使用属性配置如下:

@Reference(url = "dubbo://localhost:8080")private Userservice userservice;

使用XML配置如下:

<dubbo:reference version="1.0" interface="com.tdd.service.UserService" check="false" url="dubbo://localhost:8080"/>

5. Dubbo调用结果的缓存机制

5.1 概述

当服务启动完毕后,客户端发起一次调用请求,Dubbo会先去查询缓存,如果能找到结果,则返回。否则执行完整的调用请求,最后将结果缓 存。
Dubbo提供了三种结果缓存机制:
(1)lru:基于最近最少使用原则删除多余缓存,保持最热的数据被缓存
(2)threadlocal:当前线程缓存
(3)jcache:可以桥接各种缓存实现

5.2 配置使用

可以在服务消费配置文件中进行配置:

<dubbo:reference version="1.0" interface="com.tdd.service.UserService" cache="lru"/>

或在服务消费配置文件中对方法级进行配置:

<dubbo:reference version="1.0" interface="com.tdd.service.UserService" ><dubbo:method name="sayHello" cache="lru"/></dubbo:reference>

6. Dubbo的异步调用

6.1 概述

Dubbo不只提供了堵塞式的的同步调用,同时提供了异步调用的方式。当你需要同时调用多个Dubbo服务,这些服务又没有先后顺序,就可以使用异步调用。异步调用主要应用于提供者响应耗时明显的接口方法,比如商品详情页需要调用优惠服务和库存服务就可以使用异步调用。假设每个服务调用耗时10ms,如果顺序调用则商品详情页需要耗时20ms,如果使用异步调用则只需要耗时10ms。
如何获取异步结果?
当使用异步调用Dubbo接口服务时,服务会立刻返回结果,但是结果为null。我们需要通过RpcContext获取到该服务调用的Future,然后通过调用Future.get()获取真正的结果。详见前面源码分析。

6.2 异步调用的配置使用

基于XML的配置,可以在服务消费配置文件中对方法级进行配置:

<dubbo:reference interface="com.tdd.service.UserService"><dubbo:method name="sayHello" async="true" /></dubbo:reference>

注意:在Dubbo 2.7.x的异步化实现中,无需在相关配置中进行特殊配置,显示声明异步接口即可,然后使用callback方式处理返回值。

7. Dubbo服务限流

7.1 概述

为了防止某个消费者的QPS或是所有消费者的QPS总和突然飙升而导致的重要服务的失效,系统可以对访问流量进行控制,这种对集群的保护措施称为服务限流。
Dubbo有多种限流方式,可以使用以下参数进行多维度的限流:
(1)accepts:服务端最大可接受连接数,可以理解为可以接受的最大消费者数;
(2)connections:每个Reference开启的连接数;
(3)actives:消费端控制每个接口的最大并发数;
(4)executes:服务端控制每个接口的最大并发数;

7.2 使用介绍

(1)accepts的配置:
accepts配置数表示服务端配置最大可接受连接数,这是项目级别设置,它仅可设置在服务端。
比如一个Provider设置了accepts=2,该Provider3个消费者分别为C1,C2,C3。假如这3个消费者的启动顺序为C1,C2,C3,则C3会无法启动,因为服务已经达到了最大连接数限制;
基于XML配置如下:

<!--限制当前提供者在使用dubbo协议最多接受10个消费者链接--><dubbo:protocol name="dubbo" port="20880" accepts="10"/>

(2)connections的配置:
connections配置数表示每个Reference开启的长连接数,默认是0,表示所有的Reference共享同一条连接;如果大于0,则单独为此Reference设置connections条长连接。
比如同一个项目有3个reference:
@Reference(connections=3) HelloService helloService;
@Reference TestService testService;
@Reference FooService fooService;
则该项目会生成4条连接,其中helloService有3条,testService与fooService共用一条
connections可以设置在提供者端,也可以设置在消费者端。限定连接的个数。对于短连接,该属性效果与actives相同。但对于长连接,其限制的是长连接的个数。 一般情况下,会使connectons与actives联用,让connections限制长连接个数,让actives限制一个长连接中可以处理的请求个数。联用前提:使用默认的Dubbo服务暴露协议
服务提供端限流配置:
接口级别:

<!--限制当前接口中每个方法的链接数不能超过10个--><dubbo:service interface="com.tdd.service.UserService" ref="userService" connections="10"/>

方法级别:

<!--限制当前接口中sayHello方法的链接数不能超过10个--><dubbo:service version="1.0" interface="com.tdd.service.UserService" ref="userService"><dubbo:method name="sayHello" connections="10"/></dubbo:service>

服务消费端限流配置:
接口级别:

<!--设置当前消费者对指定接口的每一个方法的链接数不能超过10个--><dubbo:reference interface="com.tdd.service.UserService" connections="10"/>

方法级别:

<!--设置当前消费者对指定接口的sayHello方法的链接数不能超过10个--><dubbo:reference interface="com.tdd.service.UserService" connections="10"><dubbo:method name="sayHello" connections="10"/></dubbo:reference>

(3)actives的配置:
actives配置数表示服务消费端每个接口的最大并发数,默认是0,如果是0则没有限制。该限流方式可以设置在服务供者端,也可以设置在服务消费端。可以设置为接口级别,也可以设置为方法级别。
服务提供端限流配置:
接口级别:

<!--设置当前服务提供端对指定接口的每一个方法的并发连接数不能超过10个--><dubbo:service interface="com.tdd.service.UserService" ref="userService" actives="10"/>

方法级别:

<!--限制当前接口中sayHello方法的并发链接数不能超过10个--><dubbo:service interface="com.tdd.service.UserService" ref="userService"><dubbo:method name="sayHello" actives="10"/></dubbo:service>

服务消费端限流配置:
接口级别:

<!--设置当前消费者对指定接口的每一个方法的并发链接数不能超过10个--><dubbo:reference interface="com.tdd.service.UserService" actives="10"/>

方法级别:

<!--设置当前消费者对指定接口的sayHello方法的并发链接数不能超过10个--><dubbo:reference interface="com.tdd.service.UserService" connections="10"><dubbo:method name="sayHello" actives="10"/></dubbo:reference>

(4)executes的配置:
executes的配置数表示服务提供端每个接口的最大并发数,默认是0,如果是0则没有限制。它仅可设置在服务端。
基于XML配置如下:
接口级别:

<!--服务器端并发执行(或占用线程池线程数)不能超过 10 个--><dubbo:service interface="com.tdd.service.UserService" ref="userService" executes="10"/>

方法级别:

<!--服务器端并发执行(或占用线程池线程数)不能超过 10 个--><dubbo:service interface="com.tdd.service.UserService" ref="userService"><dubbo:method name="sayHello" executes="10"/></dubbo:service>

8. Dubbo服务分组应用

8.1 概述

在Dubbo服务中,当一个接口有多种实现时,可以用group区分。

public interface PayService {String pay(String name);}public class WeixinPayServiceImpl implements PayService {@Overridepublic String pay(String name) {return "Weixin";}}public class ZhifubaoPayServiceImpl implements PayService {@Overridepublic String pay(String name) {return "Zhifubao";}}

配置使用:

<!-- 注册service实现类 --><bean /><bean /><!-- 暴露服务 --><dubbo:service interface="com.tdd.service.PayService" ref="weixinService" group="weixin"/><dubbo:service interface="com.tdd.service.PayService" ref="zhifubaoService" group="zhifubao"/><!-- 指定调用微信服务 --><dubbo:reference interface="com.tdd.service.PayService" group="weixin"/><!-- 指定调用支付宝服务 --><dubbo:reference interface="com.tdd.service.PayService" group="zhifubao"/>

9. Dubbo整合Hystrix服务降级

9.1 整合步骤

(1)在服务消费端引入Hystrix依赖
(2)在服务端启动类和消费端启动类上添加@EnableHystrix注解,启动Hystrix服务
(3)在服务端配置降级超时等信息,这里配置Hystrix超时时间为2s,睡眠时间为3s,如下:

@Override@HystrixCommand(commandProperties = {@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000") })public String sayHello(String message) {try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("服务被调用,方法执行了...");return "hello," + message;}

(4)在服务消费端配置降级方法等信息,如下:

@HystrixCommand(fallbackMethod = "sayHelloError")@GetMapping("/sayHello")public String sayHello(){System.out.println("调用了sayHello方法了...");return userService.sayHello("xiao xi yuan");}public String sayHelloError(){return "hystrix fallback value...";}

从上,可知当调用服务端方法时,会出现超时,然后会调用降级方法。

9.2 测试

在浏览器上输入:http://localhost:8081/sayHello 输出如下:

hystrix fallback value...

10. Dubbo升级接口灰度发布

10.1 灰度发布概述

灰度发布是实现新旧版本平滑过渡的一种发布方式,即让一部分服务更新到新版本,如果这部分服务没有什么问题,再将其它旧版本的服务更新。而实现简单的灰度发布我们可以使用版本号控制,每次发布都更新版本号,新更新的服务就不会调用旧的服务提供者。
当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。
一般处理步骤:
(1)在低压力时间段,先升级一半提供者为新版本
(2)再将所有消费者升级为新版本
(3)然后将剩下的一半提供者升级为新版本

10.2 配置使用

在不同服务提供端配置不同的版本号:

<dubbo:service version="1.0" interface="com.tdd.service.UserService" ref="userService"/><dubbo:service version="2.0" interface="com.tdd.service.UserService" ref="userService"/>

文章内容输出来源:拉勾教育Java高薪训练营
若有错误之处,欢迎留言指正~~~

需要做网站?需要网络推广?欢迎咨询客户经理 13272073477