一、分布式架构的发展历史与背景
分布式系统(distributed system)是建立在网络之上的软件系统。正是因为软件的特性,所以分布式系统具有高度的内聚性和透明性。因此,网络和分布式系统之间的区别更多的在于高层软件(特别是操作系统),而不是硬件。简单点理解就是一个节点来干的活,先在分成多个节点来干。
为什么会发展分布式架构?
- 稳定性和可用性这两个指标很难达到。如:单点问题,一旦大型主机出现故障,那整个系统就将处于不可用的状态。而对于大型机的使用机构来说,这种不可用导致的损失是非常巨大的。
- 单机处理能力存在瓶颈
- 升级单机处理能力的性价比越来越低
架构的发展历史
分布式架构所带来的成本
分布式事物
分布式事物是指一个操作,分成几个小操作在多个服务器上执行,要么多成功,要么多失败这些分布事物要做的
不允许服务有状态(stateless service)
无状态服务是指对单次请求的处理,不依赖其他请求,也就是说,处理一次请求所需的全部信息,要么都包含在这个请求里,要么可以从外部获取到(比如说数据库),服务器本身不存储任何信息。
服务依懒关系复杂
服务 A --> B--> C 那和服务C 的修改 就可能会影响 B 和C,事实上当服务越来 越多的时候,C的变动将会越来越困难。
部署运维成本增加
不用说了,相比之前几个节点,运维成本的增加必须的。
源码管理成本增加
原本一套或几套源码现在拆分成几十个源码库,其中分支、tag都要进行相应管理。
如何保证系统的伸缩性
伸缩性是指,当前服务器硬件升级后或新增服务器处理能力就能相对应的提升。
分布式会话
此仅针对应用层服务,不能将Session 存储在一个服务器上。
分布式JOB
通常定时任务只需要在一台机器上触发执行,分布式的情况下在哪台执行呢?
二、如何选型分布式架构
如何选型分布式架构
RPC远程调用技术
协议 | 描述 | 优点 | 缺点 |
---|---|---|---|
RMI | JAVA 远程方法调用、使用原生二进制方式进行序列化 | 简单易用、SDK支持,提高开发效率 | 不支持跨语言 |
Web Service | 比较早系统调用解决方案 ,跨语言, 其基于WSDL 生成 SOAP 进行消息的传递。 | SDK支持、跨语言 | 实现较重,发布繁琐 |
Http | 采用http +json 实现 | 简单、轻量、跨语言 | 不支持SDK |
Hessian | 采用http +hessian 序列化实现 | 简单,轻量、sdk支持 | 不能跨语言 |
基于反向代理的集中式分布式架构
这是最简单和传统做法,在服务消费者和生产者之间,代理作为独立一层集中部署,由独立团队(一般是运维或框架)负责治理和运维。常用的集中式代理有硬件负载均衡器(如F5),或者软件负载均衡器(如Nginx),这种软硬结合两层代理也是业内常见做法,兼顾配置的灵活性(Nginx比F5易于配置)。
- 优点:简单快速、几乎没有学习成本
- 适用场景:轻量级分布式系统、局部分布式架构。
- 瓶颈:Nginx中心负载、Http传输、JSON序列化、开发效率、运维效率。
嵌入应用内部的去中心化架构
这是很多互联网公司比较流行的一种做法,代理(包括服务发现和负载均衡逻辑)以客户库的形式嵌入在应用程序中。这种模式一般需要独立的服务注册中心组件配合,服务启动时自动注册到注册中心并定期报心跳,客户端代理则发现服务并做负载均衡。我们所熟悉的 duboo 和spring cloud Eureka +Ribbon/'r?b?n/ 都是这种方式实现。
相比第一代架构它有以下特点几点:
- 去中心化,客户端直连服务端,减少一次网络传输。
- 动态注册和发现服务,不需要人工去发现设置服务。
- 高效稳定的网络传输
- 高效可容错的序列化
- 代码侵入性进较强
基于独立代理进程的架构
这种做法是上面两种模式的一个折中,代理既不是独立集中部署,也不嵌入在客户应用程序中,而是作为独立进程部署在每一个主机上,一个主机上的多个消费者应用可以共用这个代理,实现服务发现和负载均衡,如下图所示。这个模式一般也需要独立的服务注册中心组件配合,作用同第二代架构。
模式 | 优点 | 缺点 | 适应场景 | 案例 |
---|---|---|---|---|
集中式负载架构 | 简单 集中式治理 与语言无关 | 配置维护成本高 多了一层IO 单点问题 | 大部分公司都适用,对运维有要求 | 亿贝、携程、早期互联网公司 |
客户端嵌入式架构 | 无单点 性能更好 | 客户端复杂 语言栈要求 | 中大规模公司、语言栈统一 | Dubbo 、Twitter finagle、Spring Cloud Ribbon |
独立进程代理架构 | 无单点 性能更好 与语言无关 | 运维部署复杂 开发联调复杂 | 中大规模公司 对运维有要求 | Smart Stack、Service Mesh |
三、Dubbo 架构与设计说明
dubbo架构
流程说明
- Provider(提供者)绑定指定端口并启动服务
- 指供者连接注册中心,并发本机IP、端口、应用信息和提供服务信息发送至注册中心存储
- Consumer(消费者),连接注册中心 ,并发送应用信息、所求服务信息至注册中心
- 注册中心根据 消费 者所求服务信息匹配对应的提供者列表发送至Consumer 应用缓存。
- Consumer 在发起远程调用时基于缓存的消费者列表择其一发起调用。
- Provider 状态变更会实时通知注册中心、在由注册中心实时推送至Consumer
这么设计的意义
- Consumer 与Provider 解偶,双方都可以横向增减节点数。
- 注册中心对本身可做对等集群,可动态增减节点,并且任意一台宕掉后,将自动切换到另一台
- 去中心化,双方不直接依懒注册中心,即使注册中心全部宕机短时间内也不会影响服务的调用
- 服务提供者无状态,任意一台宕掉后,不影响使用
Dubbo 整体设计
- 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
其协作流程如下
Dubbo 中的SPI机制
java spi的具体约定为:当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。 基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码里制定。jdk提供服务实现查找的一个工具类java.util.ServiceLoader
dubbo spi 在JAVA自带的SPI基础上加入了扩展点的功能,即每个实现类都会对应至一个扩展点名称,其目的是 应用可基于此名称进行相应的装配。