凤凰架构-软件风格架构


1. 架构的演进

软件架构风格演进

软件架构风格从大型机原始分布式大型单体面向服务微服务服务网格无服务,技术架构上确实呈现出从大到小的发展趋势。

那么就产生一个问题了,在目前所接触到的web应用开发中,为什么要做这些事情?为什么要把一个单体的项目划分为多个进程的微服务项目?

单体项目架构下的服务检修

举个例子,以企业中的单体架构Java系统,其更新和升级都必须要有固定的停机机会,必须在特定的时间窗口内开始和结束

但是假设,如果在这个窗口中产生了意外,导致服务上线时间延长,这就是严重的生产事故,并且由于是单体架构,也就意味着服务100%不可用

微服务架构下的服务检修

在这个视角下,这不过是一次在线服务更新而已,假设我们有9台后端服务器向外提供服务,当需要更新的时候,那么方案是这样的:

  • 首先停止1/3的后端服务器容器,然后重新以最新的镜像构建新的容器,并且发布,然后再进行测试、导流、金丝雀发布
  • 剩余的2/3的后端服务器容器基于此进行操作

这样做的优点,就实现了服务平滑升级,线上服务不停服,实现最新代码的平滑替换,并且在这个过程中,可以实时监控服务发布时的相关指标,配合监控报警系统,可以对服务的稳定性以及正确性进行认证,一旦产生问题,立即回滚,减少对生产的影响

在微服务的架构下,即使有某一个服务的容器产生了问题,也不会导致整体服务的不可用

无论是从测试发布、服务故障止损这些来说,微服务都是当前软件复杂化背景下的标准解决方案

知识补充:金丝雀发布

金丝雀发布,又叫做灰度发布,它的原理是ABTest,简单来说,就是对于同一个服务路由,可以提供有多种的服务方案,通常使用场景是新功能的发布,然后让一部分用户可以先用到这个功能,并且可以做出反馈。

在之前的工作中有用到一个Apollo的组件,来进行灰度发布,通过控制哪些用户可以命中灰度发布策略的形式

// 其中hit的判断,交给apollo这个组件来进行判断
// 在滴滴的技术方案中,就是通过配置Apollo中的用户灰度命中策略,来实现这个功能的
// 其实现原理大致是在需要进行灰度的服务代码块上进行拦截,然后用这些if-else进行包裹

// 1. 如果命中了灰度逻辑,那么就走新服务
if (hit) {
    service.newService();
} else {
    // 2. 没有命中灰度逻辑,走老服务
    service.oldService();
}

2. 单体系统

单体:只是表明系统中主要的过程调用都是进程内调用,并不会发生进程间的通信

  • 优点:对于小型系统而言,单台机器就可以支撑其良好运行的系统,容易开发,测试,部署,不会产生进程间的通信问题(Inter-Process Communication,IPC),因此它是运行效率最高的一种架构风格
  • 单体系统由于所有的代码都运行在同一个进程空间之内,所有的模块都不需要考虑网络分区、对象复制等这些麻烦的事情以及性能损失,获取了进程内调用的简单、高效等好处

但是当软件性能需求超过了单机,也就是说,单体系统的服务可以被划分为多个,并且这多个模块并行运行时可以取得更高的性能时,应该要进行微服务的划分

  • 缺点:单体系统,当工程庞大时,可能导致开发成本、沟通成本、产生严重的性能瓶颈等问题。
  • 任何一部分代码出现了缺陷,过度消耗了进程空间内的资源,所造成的影响也是全局性的、难以隔离的,比如说内存泄漏、线程爆炸、死锁等问题,影响的就是其他全部功能,如果消耗的是其某些更高层次的公共资源,比如说是端口号或者是数据库连接池泄漏,影响整台机器
  • 同时针对系统检修这样的需求,假设我们的系统有5个模块,我们视图更新A模块,这也就意味这其他4个模块需要同时下线,而在微服务架构中,模块之间运行在不同的进程,因此在这样的情况下,单体系统的弊端就是影响面广、隔离能力缺失,难以阻断错误的传播,技术异构的困难

软件分层架构:收到的外部请求在各层之间以不同的形式的数据结构进行流转传递,触达到最末端的数据库后按照相反的顺序回馈响应。

单体系统并不意味着只能有一个整体的程序封装形式

如果需要,它完全可以由多个JARWARDLL或者其他模块格式来构成,即使是横向扩展的角度,在负载均衡器之后同时部署若干个相同的单体系统副本,以达到分摊流量压力的效果

理想中的软件架构观念:希望系统中的每一个部件,每一处代码都尽量是可靠的,靠不出或者少出缺陷来构建可靠系统

3. 面向服务架构(Service-Oriented Arcitecure)

SOA架构是一次具体地、系统性地解决分布式服务主要问题的架构模式,其主要的目的是:让每一个子系统都能够独立地部署、运行、更新

其中比较有代表性的架构模式是:

(1)烟囱式架构(信息孤岛):它指的是一种完全不与其他相关信息系统进行相互操作或者协调工作的模式,但是实际上,这样的划分是不存在的,因为既然是系统拆分,那么就证明这两个系统原本是由一个项目工程派生出来的,如果真的没有任何协调工作,那么我们在开发这两个系统的时候,实际上应该要把这两个系统看作为两个工程而并非一个工程

(2)微内核架构:微内核架构也被称为插件式架构,这种架构是可行的,比如说在一个web应用中,我们可以将一个公共服务集群封装为一个内核系统,这个系统向外提供服务,而这些插件是作为远程调用的形式对内提供服务

(3)事件驱动架构:为了能让子系统相互通信,避免远程服务调用带来的高昂成本,来自系统外部的消息将以事件的形式发送到管道

管道的实现可以是一些持久化的设施亦或者是消息队列之类,通常是消息队列之类

在这种系统下,存在一个称为是企业服务总线的消息管道来实现各个子系统之间的通信交互

令各服务在企业服务总线的作用下,无需相互依赖却能相互通信,既带来了服务松耦合的好处,也可以为进一步实施业务流程编排提供了基础

4. 微服务架构

分布式架构需要考虑的问题:

  • 服务拆分的粒度是怎么样的?(单一职责,避免重复开发,自治(团队独立、技术独立、数据独立、部署独立))
  • 服务集群地址如何进行维护?
  • 服务之间如何实现远程调用?(面向服务,微服务对外暴露业务接口)
  • 服务健康状态如何感知?

微服务架构:微服务是一种通过多个小型应用组合构建成一个单个应用,专注于单一职责的、语言无关的、细粒度的web服务,具备有如下的特点

  • 应用之间不必使用单一的编程语言,而是可以基于不同的编程语言以及程序库来实现
  • 应用之间不必运行在单一的进程空间内,而是使用轻量IPC技术来实现应用间的交互,形成服务调度链路
  • 应用的部署可以基于自动化、智能化、可伸缩化的部署

九个核心的业务与技术特征

(1)围绕能力构建:一个服务的开发应该要匹配团队开发能力以及规模

(2)分散治理:服务在开发、部署时,应该由专业团队进行开发维护,该业务能力对应的技术栈进行实现

服务调用要做好隔离、容错、降级、避免出现级联的问题

(3)通过服务来实现独立自治的组件:

(4)产品化思维:要把软件开发看作是一个持续改进、提升的过程。

(5)数据去中心化:微服务明确地提倡数据应该按领域分散管理、更新、维护、存储,在单体服务中,一个系统的各个功能模块通常会使用同一个数据库

诚然中心化的存储天生就更容易避免一致性问题,但是,同一个数据实体在不同服务的视角里,它的抽象形态往往也是不同的

(6)强终端弱管道

(7)容错性设计:要求在微服务的设计中,有自动的机制对其依赖的服务进行快速故障检测,在持续出错的时候进行隔离,在服务恢复的时候重新联通,如果没有容错性的设计,系统就很容易就会被一两个服务崩溃所带来的雪崩效应淹没,可靠系统完全可能由会出错的服务组成

(8)演进式设计:承认服务会被报废淘汰,一个设计良好的服务,应该是能够报废的,假如系统中出现不可更改、无可替代的服务,这反而说明这是系统设计上脆弱的表现,微服务锁追求的独立、自治,反对这种脆弱性

(9)基础设施自动化:基础设施自动化,比如说CICD(持续集成持续部署),显著减少了构建、发布、运维工作的复杂性

微服务与SOA

微服务从定义上来讲,它不是SOA的或者是衍生品,一个很重要的特点是:它摒弃了SOA中可以抛弃的约束和规定,提倡以实践标准来代替规范标准

从我个人理解的层面上来讲,微服务就是不再遵循SOA中的那些规范,对于系统中各个服务的实现,可以由架构者自由发挥,比如说注册发现,跟踪治理,负载均衡,故障隔离,认证授权等不再有同一的解决方案。

典型的微服务结构

服务网关:用于对外提供一个访问的接口,当得到一个请求之后,会自动路由到对应的服务实例上

服务集群:业务能力运行时实例

注册中心:能够将服务实例的访问地址注册在此中间件上,由此中间件完成服务实例地址的维护

配置中心:可以基于此中间件实现配置的下发

服务监控和保护:系统数据埋点,可以基于此组件进行服务的监控以及QPS相关数据埋点

5. 后微服务架构

后微服务时代:从软件层面独立应对微服务架构问题,发展到软、硬一体,合力应对架构问题的时代,也就是后微服务时代

一些例子:

服务扩容

  • 采用软件方案:在服务集群中增加进程实例,进一步压榨机器性能
  • 采用硬件方案:购买新的服务器,在新的服务器上增加进程实例

服务发现

  • 采用软件方案:部署注册中心,但是注册中心本质上也是服务实例,可能存在一定的问题
  • 采用硬件方案:部署DNS服务器,让服务访问依赖的是稳定的记录名而不是容易变化的IP地址

以上,我们会思考一个问题,纯软件方案,怎么实现快速部署?上下线?

这正是容器虚拟化技术被提出的一个背景,不需要购买新的机器,只需要敲敲键盘,通过Docker引擎提供的能力,就可以实现服务快速部署

Kubernetes Spring Cloud
弹性伸缩 Autoscaling N/A
服务发现 KubeDNS / CoreDNS Spring Cloud Eureka
配置中心 ConfigMap / Secret Spring Cloud Config
服务网关 Ingress Controller Spring Cloud Zuul
负载均衡 Load Balancer Spring Cloud Ribbon
服务安全 RBAC API Spring Cloud Security
跟踪监控 Metrics API / Dashboard Spring Cloud Turbine
降级熔断 N/A Spring Cloud Hystrix

服务网格

这里书上讲的很玄乎,其实完全可以看图

以ServiceA为例,当我们的中央API服务器需要调用ServiceA的能力的时候,这时候并不是直接发请求到ServiceA运行的实例上,而是先转发到处于同一个Pod的代理服务器上,然后代理服务器解析请求,再去转调ServiceA的能力,代理服务器再将结果返回

这样的一种模式就是服务网格模式(边车代理模式),这个代理服务器一般来说,会有以下的功能:

  • 实现正常的服务间通信
  • 接收来自控制器的指令
  • 根据控制平面中的配置,对数据平面通信的内容进行分析、处理
  • 实现熔断、认证、度量、监控、负载均衡等各种附加功能

文章作者: 穿山甲
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 穿山甲 !
  目录