大家好!前几天,作为OTUS教育平台的一部分,推出了新课程:“建筑与设计模式”。在开始时,我们举办了传统的公开课。它研究了单片应用程序,多层和无服务器体系结构的功能。我们详细研究了事件驱动系统,面向服务的系统和微服务体系结构。
老师是Matvey Kalinin,他是一位具有20多年编程经验的专家,并且是“建筑与设计模式”课程的作者。一点背景
最初,程序确实为自己解决了任务集,并且非常孤立。但是随着时间的流逝,程序不断发展,人们开始了解到,功能的复杂性开始影响改进的速度,可靠性和对各种复杂性的抵抗力。确实,当我们拥有一个或两个相同且不变的程序时,编写这些程序并确保它们之间的交互并不困难。但是,当它们越来越多时,就无法避免问题,并且所涉及的软件包也不重要。今天,大多数情况下都是分发应用程序。它们由几个模块组成,并通过系统消息互连。即,获得了彼此交互的程序的相当大的集团。为了使他们成功进行互动,我们需要考虑:- 快速反应;
- 带宽
- 就一个资源单位而言的绩效;
- 扩展能力;
- 整合能力;
- 所使用平台的功能;
- 现有业务流程的特征;
- 以及更多…
同时,人们不禁会想起下面的话:“正确的程序代码创建和维护不需要大量的人工成本。快速,轻松地进行更改。错误很少。人工成本最小,而功能和灵活性最大。”
罗伯特·塞西尔·马丁
也就是说,理想情况下只编写一次程序(如果编写得当),改进将很小。如何实现这一目标并相互链接?为了回答这个问题,我们转向历史。?
因此,我们意识到我们需要适当地构建软件产品的结构。但是软件架构的目的是什么?无论如何,为什么我们在本文标题中使用“腿长”一词?问题是,当您开始编程时,您已经知道要构建的内容。最初,一切都从业务流程开始。有一个订单和一个客户(至少以文字形式)描述了他的系统应该如何工作。首先,该系统仅以其描述的形式存在。然后,对这一过程进行形式化和概述,但这仍然是成功的一半,因为进一步的开发开始了。并且开发人员需要将此过程转换为软件产品,该软件产品应该...然后是时候回顾另一句话:“软件体系结构的目标是减少创建和维护系统所需的人力。”
罗伯特·塞西尔·马丁
目标是由这样的通用短语构成的,这并不奇怪。事实是架构生活在抽象的思想中。为什么?因为从事软件体系结构的人员将业务客户的愿景转换为开发人员的愿景。而且,如果我们谈论的是开发团队的架构师和企业架构师,那么他们每个人都有不同的目标,但是他们两个都在努力一件事-减少人工成本。在这种情况下,有趣的是看一下软件的价值:问题就在这里:为什么它更重要,系统的操作还是更改它的简便性?为了回答这个问题,让我们从实用性的角度看一下该程序:如果有一个正常工作的程序不允许更改,那么当需求改变时,这样的程序最终将变得无关紧要。如果程序无法正常运行,但是可以对其进行更改,则可以在不断变化的需求框架内使其正常运行。该程序将始终保持有用。编程范例(模型)
您认为最著名的(最先)编程模型是什么?当然是一个巨石。现在可以适当地暂时回到1968年,并回顾一下Edsger Dijkstra,他证明了对转换的无节制使用(goto指令)对程序的结构有害。他建议用更易于理解的if / then / else和do / while /直到构造替换过渡。现在,goto指令的显示频率降低了。但是以前,goto指令很常见。通常,这是一种邪恶的形式,因为当您看到其中包含goto指令的代码时,您会感觉到您可能找不到它的终点。越是跳转,就越复杂,即得到了意大利面条代码。现在,我们将该代码称为“意大利面条”,例如,其中20个嵌套的ifs,可能还有1个goto。这也不是很清楚的代码。想象一下,您进入10-15,并且您正在尝试了解循环的工作原理-这就是Dijkstra的想法。Spaghetti代码是结构不良,令人困惑且难以理解的程序,其中包含许多goto语句(尤其是跳转),异常以及降低结构的其他构造。通常,这是一种众所周知且相当普遍的编程反模式。这样的程序需要大量的时间来理解,支持和测试。结构化程序设计
有编程语言,他们实现了一些目标。在一定程度上,程序的结构并没有对实施产生很大影响。但是程序不断发展,它们之间形成了无数联系。有一次,一个人觉得将算法打包到一个易于阅读,测试的结构中很重要。在程序结构级别已开始进行更改。即,不仅程序本身必须满足结果,而且程序结构也必须满足某些标准。因此,我们顺利地切换到结构化编程。据他介绍,该程序是在不使用goto运算符的情况下构建的,它包含三个基本控制结构:使用子程序,开发本身是从上到下逐步进行的。再说一次,回到1966年……今年,Ole-Johan Dahl和Kristen Nyugor注意到,在ALGOL语言中,可以将函数调用堆栈的框架移到动态内存(堆)中,以便在函数内部声明的局部变量可以在以后保存。从它退出。结果,函数变成了类的构造函数,局部变量变成了实例变量,而嵌套函数变成了方法。这导致通过严格使用函数指针来发现多态。面向对象编程
众所周知,在OOP中,程序被表示为对象的集合,每个对象都是特定类的实例,并且类形成继承层次结构。结构化的基本原则:您可以从另一个角度看待所有这些原则。罗伯特·马丁(Robert Martin)开发了SOLID的原理,一方面确定了程序员如何处理抽象,另一方面又形成了多态,继承的过程。命令式编程
命令式程序类似于计算机必须执行的命令。这些程序的特点是:- 指令写在程序的源代码中;
- 必须按顺序执行说明;
- 执行先前指令期间获得的数据可以由后续指令从存储器中读取;
- 通过执行指令获得的数据可以写入存储器。
也是很古老的设计。命令式语言的主要特征:- 使用命名变量;
- 使用赋值运算符;
- 复合表达式的使用;
- 使用例程。
但是,我们继续“时间旅行”。这次我们将在1936年再回来(!)。有趣的是,今年的Alonzo Church发明了lambda演算(或λ演算),后来在1958年构成了John McCarthy发明的LISP语言的基础。λ演算的基本概念是不变性-即不可能更改符号的值。功能编程
函数式编程涉及根据源数据和其他函数的结果来计算函数的结果,并不意味着程序状态的显式存储。实际上,这意味着功能语言没有赋值语句。让我们用一个例子来看看命令式和功能式之间的区别:#
target = [] #
for item in source_list: #
trans1 = G(item) # G()
trans2 = F(trans1) # F()
target.append(trans2) #
#
compose2 = lambda A, B: lambda x: A(B(x))
target = map(compose2(F, G), source_list)
那么什么是软件架构?
软件体系结构是有关软件系统组织的一组决策。这包括:- 选择结构元件及其界面;
- 所选元素和界面的行为,它们之间的相互作用;
- 将选定的结构和行为要素组合成更大的系统;
- 指导整个组织的建筑风格。
请注意:首先我们得出的结论是goto不适合我们,然后我们发现存在某些规则(封装,继承,多态),然后我们意识到这些规则不仅有效,而且符合某些原则。第四点是建筑风格,我们将在后面讨论。该体系结构的主要目的是支持系统的生命周期。良好的体系结构使系统易于学习,易于开发,维护和部署。最终目标是在系统的整个生命周期内最小化成本,并最大程度提高程序员(更准确地说,开发团队)的生产率。首先,我们讨论了编写程序所需的规则。但是,除了编写程序外,还提供支持,开发和部署。也就是说,架构没有捕获编程的特定区域,而是整个开发周期。一个好的架构应该提供:- 各种用例和有效的系统操作。
- 系统维护简单。
- 易于系统设计。
- 易于系统部署。
建筑风格
整体式
首先,让我们谈谈著名的巨石。但是,在小型系统中仍然可以找到这种样式。整体架构意味着您的应用程序是一个大型的,相互连接的模块。所有组件均设计为可协同工作,共享内存和资源。所有功能或其主要部分都集中在一个过程或容器中,该过程或容器分为内部层或库。
优点:- 易于实现。无需浪费时间考虑进程间通信。
- 开发端到端测试很容易。
- 易于部署。
- 使用Loadbalancer在您的应用程序的多个实例之间轻松扩展。
- 易于操作。
但是现在他还有更多缺点:- 强大的凝聚力会导致应用程序的发展陷入困境。
- 组件的独立缩放会导致复杂性和功能的完全重新测试。
- 比较难理解。
- 随着复杂性的增加,开发时间也会增加。
- 缺少组件隔离。
一方面,整体是好的,但是一旦您开始开发它,就会遇到困难。什么是服务?
现在每个人都知道什么是服务。它可以定义为执行重复任务的可见资源,并由外部指令描述。现代服务具有以下功能:- 服务不是基于IT能力,而是针对业务需求;
- 服务是自给自足的,并根据服务的接口,操作,语义,动态特性,策略和属性进行描述;
- 服务的重用由其模块化计划提供;
- 服务协议是在称为供应商和用户的实体之间达成的,并不影响服务本身的实施;
- 在其生命周期中,通过服务元数据,注册中心和存储库托管服务并使它们可见。
- 聚合:将一个或多个企业的业务流程和复杂的应用程序结合在一起,以松散耦合的服务为基础。
由于上述特征,出现了面向服务的体系结构(SOA)的概念。面向服务的体系结构(SOA)
SOA是一种用于创建企业IT体系结构的体系结构样式,它使用面向服务的原理来实现业务及其支持信息系统之间的紧密联系。
SOA具有以下特征:- 改善企业架构与业务之间的关系。
- 允许您从集成服务集创建复杂的应用程序。
- 创建灵活的业务流程。
- 它不依赖于一组技术。
- 在独立发展和部署的意义上是自治的。
SOA 部署模型包括业务智能和开发以及IT智能和开发。该程序集包括编程服务和构建复杂的应用程序。托管包括托管应用程序和运行时工具,例如企业服务总线(ESB)。至于手册,它包括支持操作环境,监视服务的性能以及监视对服务策略的遵守情况。
微服务架构
现在该讨论微服务架构了。在其中,应用程序由小型独立服务应用程序组成,每个应用程序都有自己的资源。服务彼此交互以执行与其商机有关的任务。有几个部署单元。每个服务都是独立部署的。
优点:- 支持整个系统的模块化。
- 不相关的服务更易于修改以服务于不同的应用程序。
- 不同的服务可能属于不同的团队。
- 服务服务可以在整个公司内重复使用。
- 易于理解和测试。
- 与其他服务中使用的技术无关。
- 服务的隔离提高了所有功能的整体可靠性。
缺点:- 整体功能(日志记录,访问权限等)的实施困难。
- 进行端到端的系统测试很困难。
- 更加艰苦的操作和支持。
- 比整体而言,需要更多的设备。
- 几个团队的支持导致他们之间互动的协调。
值得注意的是,在这种架构中,没有DevOps很难做任何事情。分层架构
分层架构是最常见的架构模式。它也称为n层体系结构,其中n是级别数。该系统分为多个级别,每个级别仅与两个相邻的级别交互。体系结构并不意味着任何强制性的级别-可以有三个,四个,五个或更多。大多数情况下,使用三层系统:表示层(客户端),逻辑层和数据层。最常见的层是:- 表示层(用于与用户一起工作);
- 应用层(服务-安全,访问);
- 业务逻辑层(域实现);
- 数据访问层(数据库接口的表示)。
封闭层
级别隔离的概念严格地将一个级别与另一个级别分开:您只能从一个级别转到另一个级别,并且不能一次跳过多个级别。
开放层
该系统允许您跳过打开的级别并落在下面的级别上。
MVC
MVC的概念在1978年被描述。MVC概念的最终版本仅在1988年发表在Technology Object杂志上。主要目标是将业务逻辑(模型)与其可视化(表示,视图)分开。它有什么作用:- 代码重用的可能性增加了。
- 用户可以在不同的上下文中同时看到相同的数据。
该模型提供数据并响应控制器命令,从而更改其状态。该视图负责向用户显示模型数据,以响应模型更改。控制器解释用户的操作,通知模型需要更改。
事件驱动架构
另一个有趣的架构。它用于开发和实现在松耦合的软件元素之间传输事件的系统。由不同的单一目的事件处理组件组成,这些组件异步接收和处理事件。该模板由两个主要拓扑组成-中介和代理。转销商拓扑
在某些过程中,需要控制步骤的顺序。在这里,我们是有用的中介。体系结构组件的主要类型:- 事件队列;
- 事件的调解员;
- 事件的渠道;
- 事件处理程序。
事件事件可以定义为“状态的重大变化”。一个事件可以包括两个部分:- 标头(事件名称,事件时间戳和事件类型);
- 正文(描述实际发生的情况)。
中介器拓扑客户端将事件发送到事件队列,该事件队列用于将事件发送到中介器。调解器接收初始事件,并为过程的每个步骤将其他异步事件发送到事件通道。侦听事件通道的事件处理器从中介程序接收事件,并通过处理事件来执行某些业务逻辑。
经纪人拓扑代理的拓扑与中间拓扑的不同之处在于,没有中央事件中介器。消息流通过轻量级消息代理(例如ActiveMQ,HornetQ等)在链中的事件处理器组件之间分配。当事件处理流程相对简单并且不需要集中的事件编排时,此拓扑很有用。事件处理器的每个组件负责处理一个事件并发布一个新事件,以指示该操作刚刚完成。
如果第一种情况在“某处下方”是异步的,那么第二种情况可能是完全异步的。一个事件生成多个事件,并且它们可以增加和增加。优点 事件驱动架构:- 这些组件是隔离的,并允许每个组件完成而不影响系统的其余部分;
- 易于部署;
- 高性能。允许并行异步操作;
- 扩展良好。
缺点:无服务器架构
这是无需基础架构管理即可创建和运行应用程序和服务的方法。该应用程序仍在服务器上运行,但是平台完全控制了这些服务器。整个基础结构受第三方提供商的支持,并且以负责身份验证过程,消息传递等的服务的形式提供了必要的功能。区分以下术语:- (Function-as-a-Service) — , .
- (Backend as a Service) — , - API, . , , , .
如果我们考虑“客户端-服务器”的体系结构,则系统中的大多数逻辑(身份验证,页面导航,搜索,事务)都由服务器应用程序实现。
在无服务器架构中,一切都有些不同。身份验证被第三方BaaS服务(现成的云服务)替代,对数据库的访问也被另一BaaS服务替代。应用程序逻辑部分已经在客户端内部-例如,跟踪用户的会话。客户端已经成为单页应用程序。可以通过搜索服务(FaaS-作为服务)进行搜索。购买功能也作为单独的服务(FaaS)与客户隔离。
好了,仅此而已,如果您对这些细节感兴趣,请观看整个视频。至于新课程,您可以在这里熟悉其课程。