分布式-Dubbo01-认识分布式

分布式架构的发展历史与背景

理解分布式架构

软件架构的发展历史

参考:https://blog.csdn.net/fdxcg_x/article/details/115708922

1320926-20200722145816632-206965628

单体架构

优点

  • 小项目开发快 成本低
  • 架构简单
  • 易于测试
  • 易于部署

缺点

  • 大项目模块耦合严重,不易开发,维护,沟通成本高
  • 新增业务困难
  • 核心业务与边缘业务混合在一块,出现问题互相影响

垂直架构

image-20220628082810793

​ 根据业务把项目垂直切割成多个项目,因此这种架构称之为垂直架构。此时用于加速前端页面开发的**Web框架(MVC)**是关键

优点

1、项目架构简单、前期开发成本低,周期短,适合小型项目

2、通过垂直拆分,原来的项目不至于无线扩大

3、不同项目可以采用不同的技术

4、可针对核心业务服务增加集群节点

1、系统拆分实现了流量切割,降低单体时的流量集中

2、业务拆分,可以针对不同模块优化部署迭代,互不影响

3、方便水平扩展、负载均衡、容错率高

缺点

1、服务之间的调用方式、接口协议不统一

2、服务监控不到位

3、服务ip和端口发生变更时,不好维护

4、搭建集群之后,实现负载均衡比较复杂。比如:内网负载,在迁移得时候会影响调用方的路由,导致线上故障

分布式架构(SOA(Service Oriented Architecture))

​ “面向服务的架构,是一个架构模型或者一种设计方法,而并不是服务解决方案。其中包含多个服务, 服务之间通过相互依赖或者通过通信机制,来完成相互通信的,最终提供一系列的功能。一个服务通常以独立的形式存在与操作系统进程中。各个服务之间通过网络调用 。

​ 跟 SOA 相提并论的还有一个 ESB(企业服务总线),简单来说ESB就是一根管道,用来连接各个服务节点。为了集成不同系统,不同协议的服务,ESB 可以简单理解为:它做了消息的转化解释和路由工作,让不同的服务互联互通;

img

SOA 所解决的核心问题

  1. 系统集成:站在系统的角度,解决企业系统间的通信问题,把原先散乱、无规划的系统间的网状结构,梳理成规整、可治理的系统间星形结构,这一步往往需要引入 一些产品,比如 ESB、以及技术规范、服务管理规范;这一步解决的核心问题是【有序】
  2. 系统的服务化:站在功能的角度,把业务逻辑抽象成可复用、可组装的服务,通过服务的编排实现业务的快速再生。目的:把原先固有的业务功能转变为通用的业务服务,实现业务逻辑的快速复用;这一步解决的核心问题是【复用】
  3. 业务的服务化:站在企业的角度,把企业职能抽象成可复用、可组装的服务;把原先职能化的企业架构转变为服务化的企业架构,进一步提升企业的对外服务能力;前面两步都是从技术层面来解决系统调用、系统功能复用的问题。第三步,则是以业务驱动把一个 业务单元封装成一项服务。这一步解决的核心问题是 【高效】

优点

1、服务以接口为粒度,对调用者屏蔽接口底层细节

2、业务分层后架构更加清晰,业务模块化后职责单一,便于业务功能转化实现快速复用

3、服务间统一的通信协议

缺点

1、服务拆分粒度把控复杂,容易造成服务模块边界不清晰

2、服务接口剧增,调用链路长,容易超时等

微服务架构

img

SOA架构的一种拓展和变种,强调去中心化、服务拆分粒度更小。

优点

1、能够被一个团队单独进行敏捷开发(开发、测试、部署、运维)实现DevOps开发运维一体化

2、各服务功能单一、粒度小

3、服务间的依赖性弱、独立性强、各个服务可以独立部署

缺点

1、难以管理、服务边界问题

2、调用链路长、链路跟踪、问题定位难度较大

3、分布式事务问题

分布式架构带来的问题

问题

1、分布式事务

2、分布式任务

3、服务依赖关系复杂,调用链路长,需考虑服务降级、熔断、限流等

4、部署运维成本增加

5、不允许服务有状态

​ 无状态服务是指对单次请求的处理,不依赖其他请求,也就是说,处理一次请求所需的全部信息,要么都包含在这个请求里,要么可以从外部获取到(比如说数据库),服务器本身不存储任何信息。

6、分布式会话

​ 此仅针对应用层服务,不能将Session 存储在一个服务器上。

改造面临的风险

1、新功能与旧bug需要2套代码同时维护

2、业务的拆分、完整性问题

3、团队协作方式的转变

4、开发人员的技能提升

5、系统交付部署方式的改变

​ 些问题解决涉及业务部门及整个技术部门(开发、测试、运维)协商与工作标准的制定。业务相关问题暂不做讨论,技术架构上应该要清楚自己的职责是,如何通过技术手段把业务波动降至最低、开发成本最低、实施风险最低?

如何选型分布式架构

RPC远程调用技术

协议 描述 优点 缺点
RMI JAVA 远程方法调用、使用原生二进制方式进行序列化 简单易用、SDK支持,提高开发效率 不支持跨语言,不支持负载均衡
Web Service 比较早系统调用解决方案 ,跨语言, 其基于WSDL 生成 SOAP 进行消息的传递。 SDK支持、跨语言 实现较重,发布繁琐
Http 采用http +json 实现 简单、轻量、跨语言 不支持SDK
Hessian 采用http +hessian 序列化实现 简单,轻量、sdk支持 不能跨语言

RPC调用需要解决的问题

微服务调用中中,如何实现A->B的负载均衡、服务发现、健康监测、容错的功能呢?

负载均衡:这么多个机器调用哪一台?

服务发现:样发现新的服务地址呢?

健康检测:服务关宕机或恢复后怎么办?

容错:如果调用其中一台调用出错了怎么办?

3种代理架构比较

参考文档:https://blog.csdn.net/adparking/article/details/114577835

1、集中式负载均衡(4层代理(F5硬件代理)+7层代理(Nginx软件代理))

image-20220727085907484

2、嵌入应用内部的去中心化架构(服务发现与注册 consul nacos ribbon)

image-20220727085941776

3、基于独立代理进程的Service Mesh(服务网格)架构

image-20220727090005751

模式 优点 缺点 适应场景 案例
集中式负载架构 简单集中式治理与语言无关 配置维护成本高多了一层IO单点问题(硬件F5+软件nginx两层负载,F5以主从HA部署,nginx则以集群多实例部署) 大部分公司都适用,对运维有要求 亿贝、携程、早期互联网公司
客户端嵌入式架构 无单点性能更好 客户端复杂语言栈要求 中大规模公司、语言栈统一 Dubbo 、Twitter finagle、Spring Cloud Ribbon
独立进程代理架构 无单点性能更好与语言无关 运维部署复杂开发联调复杂 中大规模公司对运维有要求 Smart StackService Mesh

RMI 远程调用架构

​ Java RMI,即 远程方法调用(Remote Method Invocation),一种用于实现远程过程调用(RPC)(Remote procedure call)的Java API, 能直接传输序列化后的Java对象和分布式垃圾收集。它的实现依赖于Java虚拟机(JVM),因此它仅支持从一个JVM到另一个JVM的调用。

image-20220727084716917

1、注册中心(可以是服务提供者本身):

1
LocateRegistry.createRegistry(8080);

2、注册远程服务

1
2
UserService hello = new UserServiceImpl();
Naming.bind("rmi://localhost:8080/UserService3", hello);

3、引用远程服务,并发起调用

1
2
UserService userService = (UserService) Naming.lookup("rmi://localhost:8080/UserService");
userService.getName(11);

Dubbo架构与设计说明

架构图

image-20220727085813804

流程说明

  1. Provider(提供者)绑定指定端口并启动服务
  2. 指供者连接注册中心,并发本机IP、端口、应用信息和提供服务信息发送至注册中心存储
  3. Consumer(消费者),连接注册中心 ,并发送应用信息、所求服务信息至注册中心
  4. 注册中心根据 消费 者所求服务信息匹配对应的提供者列表发送至Consumer 应用缓存。
  5. Consumer 在发起远程调用时基于缓存的消费者列表择其一发起调用。
  6. Provider 状态变更会实时通知注册中心、在由注册中心实时推送至Consumer

这么设计的意义:

  1. Consumer 与Provider 解偶,双方都可以横向增减节点数。
  2. 注册中心对本身可做对等集群,可动态增减节点,并且任意一台宕掉后,将自动切换到另一台
  3. 去中心化,双方不直接依懒注册中心,即使注册中心全部宕机短时间内也不会影响服务的调用
  4. 服务提供者无状态,任意一台宕掉后,不影响使用

Dubbo 整体设计

image-20220727090423881

  • config 配置层:对外配置接口,以 ServiceConfig, ReferenceConfig 为中心,可以直接初始化配置类,也可以通过 spring 解析配置生成配置类
  • proxy 服务代理层:服务接口透明代理,生成动态代理 扩展接口为 ProxyFactory
  • registry 注册中心层:封装服务地址的注册与发现,以服务 URL 为中心,扩展接口为 RegistryFactory, Registry, RegistryService
  • cluster 路由层:封装多个提供者的路由及负载均衡,并桥接注册中心,以 Invoker 为中心,扩展接口为 Cluster, Directory, Router, LoadBalance
  • monitor 监控层:RPC 调用次数和调用时间监控,以 Statistics 为中心,扩展接口为 MonitorFactory, Monitor, MonitorService
  • protocol 远程调用层:封装 RPC 调用,以 Invocation, Result 为中心,扩展接口为 Protocol, Invoker, Exporter
  • exchange 信息交换层:封装请求响应模式,同步转异步,以 Request, Response 为中心,扩展接口为 Exchanger, ExchangeChannel, ExchangeClient, ExchangeServer
  • transport 网络传输层:抽象 mina 和 netty 为统一接口,以 Message 为中心,扩展接口为 Channel, Transporter, Client, Server, Codec
  • serialize 数据序列化层:可复用的一些工具,扩展接口为 Serialization, ObjectInput, ObjectOutput, ThreadPool

协作流程如下:

image-20220727090534527

Dubbo 中的SPI机制

java的SPI:具体约定为:当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。 基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码里制定。jdk提供服务实现查找的一个工具类java.util.ServiceLoader

image-20220727091542633

1
2
3
4
5
6
7
8
public static void main(String[] args) {
    Iterator<UserService> services = ServiceLoader.load(UserService.class).iterator();
    UserService service = null;
    while (services.hasNext()) {
        service = services.next();
    }
    System.out.println(service.getUser(111));
}

Dubbo的SPI机制:

dubbo spi 目录文件

img

dubbo spi 文件内容:

luban=tuling.dubbo.server.LubanFilter

装配自定义Filter

img

配置关系图

image-20220728220127188

Dubbo调用模块

dubbo调用模块核心功能是发起一个远程方法的调用并顺利拿到返回结果,其体系组成如下:

透明代理:通过动态代理技术,屏蔽远程调用细节以提高编程友好性。

负载均衡:当有多个提供者是,如何选择哪个进行调用的负载算法。

容错机制:当服务调用失败时采取的策略

调用方式:支持同步调用、异步调用

透明代理:

参见源码:

com.alibaba.dubbo.config.ReferenceConfig#createProxy

com.alibaba.dubbo.common.bytecode.ClassGenerator

com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory

负载均衡

Dubbo 目前官方支持以下负载均衡策略:

  1. 随机(random):按权重设置随机概率。此为默认算法.
  2. 轮循(roundrobin):按公约后的权重设置轮循比率。
  3. 最少活跃调用数(leastactive):相同活跃数的随机,活跃数指调用前后计数差。
  4. 一致性Hash(consistenthash ):相同的参数总是发到同一台机器

为避免热点问题,一致性哈希使用虚拟节点,增大节点数量,根据指定的参数的hash的结果分散在0-2 31-1 之间

​ 一致性哈希(Consistent Hashing)是一种在分布式系统中用于负载均衡的技术。它的主要目标是在动态添加或删除服务器时,最小化已经存在的键-值对与服务器之间的映射关系的变化。

在传统的哈希表中,键通过哈希函数映射到一组固定的桶中,每个桶对应一个特定的服务器或节点。但是,在动态环境中,当节点数量发生变化时,这种静态的映射可能会导致大量的数据迁移,影响系统性能。

​ 一致性哈希通过引入虚拟节点(Virtual Nodes)和一种特殊的哈希函数来解决这个问题。具体来说,一致性哈希将哈希空间映射到一个环上,每个服务器或节点在环上对应一个位置。虚拟节点是对实际节点的多次复制,每个节点对应多个虚拟节点,它们也均匀地分布在环上。

​ 当需要存储或检索数据时,首先将数据的键哈希到环上的一个位置,然后沿着环顺时针查找,直到找到第一个不小于该位置的节点,这个节点就是负责处理这个键的节点。这样做的好处是,当节点被添加或删除时,只有部分数据会受到影响,大部分数据仍然映射到原来的节点上,从而减少了数据迁移的开销。

​ 一致性哈希在构建分布式缓存、分布式数据库等系统时广泛应用,能够提高系统的可扩展性和容错性。

image-20220728222535490

image-20220728222812973

容错

Dubbo 官方目前支持以下容错策略:

  1. 失败自动切换:调用失败后基于retries=“2” 属性重试其它服务器
  2. 快速失败:快速失败,只发起一次调用,失败立即报错。
  3. 勿略失败:失败后勿略,不抛出异常给客户端。
  4. 失败重试:失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作
  5. **并行调用: **只要一个成功即返回,并行调用指定数量机器,可通过 forks=”2” 来设置最大并行数。
  6. 广播调用:广播调用所有提供者,逐个调用,任意一台报错则报错 

异步调用

异步调用是指发起远程调用之后获取结果的方式。

  1. 同步等待结果返回(默认)
  2. 异步等待结果返回
  3. 不需要返回结果

Dubbo 中关于异步等待结果返回的实现流程如下图:

img

消费者订阅原理

image-20220702122859080

image-20220702124112036

什么是Dubbo

​ Dubbo 是一款高性能、轻量级的开源 Java RPC 框架,核心功能包含服务自动发现与注册、负载均衡、高可用容错、降级、服务监控等。让开发者像调用本地方法一样去调用远程服务,无需关注底层细节,支持SPI做可插拔配置(选择用那种底层通信协议:默认dubbo协议,REST、Hession、Http、WebService、Thrift、gRpc)