且谈软件架构

简介: 且谈软件架构

什么是架构呢?我想这是首要回答的问题? 架构(Architecture)一词最早源于建筑学术语,后来才被计算机科学领域借用。以下是其在维基百科的定义:

架构是规划、设计和构建建筑及其物理结构的过程与产物。在计算机工程中,架构是描述功能、组织和计算机系统实现的一组规则与方法。

京东智能通讯部资深架构师胡峰在《程序员进阶心法:快速突破成长瓶颈》中这样写道:

好在经过多年的实践,行业里逐渐形成了关于软件架构的共同认知:软件系统的结构与行为设计。而实现就是围绕这种已定义的宏观结构取开发程序的过程

   根据以上的描述我们已经对架构有了一个大致的认知,即软件系统的结构与行为设计。Architecture也有结构的意思,在《柯林斯英语词典》中,有这样一种释义: Thearchitectureofsomethingisitsstructure ,即架构就是结构。事实上architecture 和structure很像,是一个architect [ˈɑːkɪtekt] 和structure [ˈstrʌktʃə(r)] 的合成词architect的首要语义即为: 建筑师。在某种意义上,我们可以将架构等同于结构,架构设计可以等同于结构设计,属于宏观层面。

   但是如果在百度上搜索建筑架构,第一页出来的就是建筑结构相关的词条,这似乎说明一点,建筑领域内结构一词比架构用的更为广泛,我个人倒是觉得是因为结构一词比架构更为直观,所以国内建筑领域更喜欢用结构一词。在百度百科中,建筑结构的定义如下:

建筑结构是指在房屋建筑中,由各种构件(屋架、梁、板、柱等)组成的能够承受各种作用的体系。所谓作用是指能够引起体系产生内力和变形的各种因素,如荷载、地震、温度变化以及基础沉降等因素

这是一个清晰的定义,我们可以参照建筑结构的定义,用类比的方式给软件架构下一个定义:

软件架构是指在软件中,由各种构件(框架、库)组成的能够承受各种作用的体系。所谓作用是指能够引起体系产生bug和响应速度变慢的各种因素,如数据量过多,程序员删库跑路,用户访问量突然飞增等因素。

关于框架和库的区别,请参看我的这篇文章: Vue 学习笔记(一)初遇

事实上架构的定义还可以接着向前扩充,胡峰老师在《程序员进阶心法:快速突破成长瓶颈》如下描述:

从定义上,你已经知道架构师一种结构设计,但它同时可能存在于不同的维度和层次上:

  • 高维度: 指系统、子系统或服务之间的切分与交互结构
  • 中维度: 指系统、服务内部模块的切分与交互结构
  • 低维度: 指系统组成的代码结构、数据结构、库表结构

 在javaEE中,MVC(事实上我觉得MVC更像是一种设计模式),单体架构, 面向服务的架构 (SOA),微服务是环绕在开发者周围的名词,可能每个java开发者都听说过以上几个名词。目前上来看微服务上是市面上比较流行的架构。微服务流行的原因在我看来有以下几个:

  • 互联网的进一步普及,导致各大网站的访问量进一步的上升
  • 培训班的鼓吹

不管怎么样,如Spring官网所说: 微服务正成为一种新常态。至于为什么架构会走到这里,我们需要回顾一下软件架构发展的历史,历史会给我们答案。在介绍软件架构发展简史之前,我们需要介绍一下集群和分布式。集群也并非软件开发领域独有的概念,践行这一概念比较悠久的应该是军事领域中的军事单位,如师旅团某种意义上可以算是人的集群。军事单位的集群可能不那么容易理解。我们这里说一下火炮集群,通俗的讲就是一尊炮对敌人的杀伤是有限的,那我就来两尊,两尊还不够我就来三尊......。类比到软件开发领域就是,单个软件服务在一台计算机上已经无法在应付请求了,那我就在搞一台计算机,同样的软件服务在部署一遍。那么在访问这个软件服务的时候,应当由集群中的哪个服务来处理请求呢? 最好能够让此时此刻负载较小的节点来处理,这样使得每个节点的压力都比较平均。要实现这个功能,就需要再所有节点之前增加一个"调度者"的角色,用户所有的请求都先交给它,让它来根据当前所有节点的负载情况,决定将这个请求交给哪个节点处理,这个调度者我们称之为负载均衡服务器。分布式是什么呢?维基百科的定义是:

分布式系统是一组计算机,通过网络相互连接传递消息与通信后并协调它们的行为而形成的系统。组件之间彼此进行交互以实现一个共同的目标。

我们可以认为是一种部署形式,将一个软件拆分成不同的服务,这些服务放在不同的计算机上。这些服务之间通过协作来完成某些请求。分布式和集群看起来是有些类似的,主要区别是集群是相似的服务,分布式是不同的服务。

关于分布式系统还有一个绕不开的CAP定理,这里我简单介绍一下,有兴致的同学可以去找相关的文章去详细了解,2000年7月,加州大学伯克利分校的Eric Brewer教授在ACM PODC会议上提出CAP猜想。2年后,麻省理工学院的Seth Gilbert和Nancy Lynch从理论上证明了CAP。之后,CAP理论正式成为分布式计算领域的公认定理。 C( Consistency )代表一致性,A代表可用性( Availability ),P代表( 分区容错性 )。粗略的讲: 一个分布式系统最多只能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三项中的两项。

接下来我们接着讨论软件架构的发展历史,我个人认为,就web后端架构的发展而言,并没有一个共识可言。有的人认为微服务是SOA向前再走一步,有人认为SOA和微服务并没有多大的区别,关于这方面的讨论请参看知乎上的相关讨论:SOA和微服务架构的区别?为什么关于软件架构的没有形成一个广泛的共识呢? 我想这大概是软件架构是依附于业务和用户量的,而不是业务依附于架构。如果一个互联网公司的业务和用户量飞快增长,当前的架构已经不再满足于当前的业务了,公司就会去考虑调整软件架构来适应当前的业务。就好像是城市发展一样,城市发展到了一定地步开始建地铁,建新城。而不是这个城市在目前交通系统完全满足需求的时候,去建地铁。这跟国家建设的思路是不同的,国家建设要着眼于未来。在这方面胡峰老师在《程序员进阶心法:快速突破成长瓶颈》一书中着有非常精彩的描述:

相对于建筑架构,软件架构规程更像是城市的规划与演变的过程。有一定历史的城市,慢慢都会演变出所谓的旧城和新城,而新城相对于旧城就是一次架构升级的过程。城市规划师会对城市的分区、功能划分进行重新定位于规划。

在当前的软件开发领域的有一些不好的习气,尤其是在新手中,表现为一上来就是高并发、分库分表、主从同步等当时为了找一些资料,加入了一些QQ群,其中有些人提出的两个问题是令我印象深刻的, 第一个是有位仁兄贴了一段代码上来,问这段代码能扛得住多少并发? 我当时是这么想的,这不是由你的项目架构来决定的吗? 要从宏观上来看,假如你就一个Tomcat,其他什么的都没有了。那这是由你的Tomcat的最大连接数来决定的,假如访问量在很短的时间就远远超过了最大连接数,你的Tomcat就崩掉了,跟你这段代码有什么关系呢? 然后我诚实的表达了我的疑问,就是你的Tomcat的最大连接数是多少,是单体架构吗? 然后这位仁兄并不理解是什么是网络中的建立连接,他只想知道他贴出来的代码最大能抗多少并发量。最后的讨论结果就是他给我扣了个杠精的帽子,整场讨论不欢而散。互联网中给和自己意见不一致的人扣帽子,我个人认为也是一种不好的习气。 第二个是在群里闲聊的时候,好像是有位仁兄讲架构师是不是在项目的设计阶段就能考虑到分库、分表。我想这位仁兄是不是对架构师是不是有什么误解,架构师又不是算命的。 只能是数据量到达那个级别,再去分库分表,而不是在数据量没到这个级别的时候就考虑分库分表。没有人能看到未来是什么样子,在未来到达之前,所有对未来的描述都是预测。还有就是觉得MyBatis天下第一的,Hibernate现在没人用的,觉得现在的公司都是前后端分离的,微服务,分库分表。不考虑当前的业务场景,觉得公司不上最新的技术就是跟不上潮流的。事实上我们国家并没有那么多BAT,如果业务和访问量走到了那里,公司会主动调整架构的。 不会有公司在当前架构满足业务需求的情况下,盲目的去追求最新最潮流的技术,企业是逐利的。这方面也许是培训班带的,也许是被大厂的面试题带出来的。

 对于我而言,web后端的架构发展也就是从单体架构突然就跑到了微服务,这里解释一下为什么是从单体架构突然跑到了微服务:我在大学中就搞的是单体,然后在B站学习的时候,看到一个up主简单的介绍了一下微服务,然后等我毕业实习的时候,我就发现微服务已经成为新常态了,并没有经历过单体和微服务之间的中间架构。实在抱歉,我对SOA并不了解,我查了好多资料,看了一些视频,又跟一些资深的开发者进行交流,询问他们软件架构发展是怎么样的,最终都没有一个我要的完美答案,反而让我更有种云里雾里的感觉。有资深开发者给我答案是: 单体(All in one 所有的代码写在一个类中)到MVC(切分职能,Controller:控制层  View: 视图层  Model:模型层),再到RPC,再到SOA,再到微服务。也许会有人指出MVC并不能算一种架构,但是请注意架构等同于结构,MVC描述的是软件内部采用的结构,说MVC是架构也没问题。所以当我们在说到架构的时候,就有两种语义: 第一种是内部的架构,对于java程序员来说就是包那一层。第二种就是整体对外表现的形式,这听起来有些抽象,粗略的说就是假如你所有的业务都在一个工程下,在交付部署的时候,是一个jar包或者war包,那这就是单体架构。假如你根据业务进行了切分为一个又一个的模块,最后部署的时候,体现为多个jar包,分别在不同的web服务器中,业务之间的相互协作通过RPC(Remote Procedure Call)来完成,那这个架构你就可以姑且称之为微服务,当然你也可以称之为SOA。这两种架构的区别并不是那么大。

我们简单的介绍一下微服务的思想,讲一个系统拆分为若干个小服务,不同的服务的对应库是不同的,服务之间通过网络来实现相互调用。忘了说微服务的思想是由Martin Fowler所提出来的,我原本打算这里贴原文的,但是我发现原文还是比较博大精深的,其实就是我看不大懂的,也许需要慢慢体会吧!所以这里我就写上了自己的理解,这里不过多介绍Matin Fowler对微服务的阐释,这里我来介绍来它的践行者,Spring Cloud各大核心组件,于实践中去体会理论是极好的学习的方式。我们使用Spring Boot 来构建服务,使用Spring Cloud来治理服务。这里的"治理"是什么意思呢? 我们借用Spring的一张图篇来解释:

image.png

图中Spring Boot Apps可以理解为被切割成的一个又一个模块,每一个模块可以算作一个微服务。这样做的优点是什么呢? 用Spring官网的话来说就是弹性(resilience)更强和灵活性更好(flexibility )。那该怎么理解这个灵活性和弹性呢? 假设我们的系统采用的是微服务架构,其中的某个服务访问量明显超过其他服务,那我们就可以将这个服务放置多个,采用负载均衡调度器来均衡的调度这个服务。那么假如某个服务因为一些原因崩掉了呢? 不用担心,各个服务之间都是独立的,一个不影响另一个。那我该怎么知道这些服务的运行状态和如何进行服务调用呢? 我们不大可能采取A服务直接调用服务B的那种做法,这会让服务之间的关系变得混乱,降低整个项目的可维护性。这也就是图中Service Registry(注册中心的用处),服务的注册。那这个Api Gateway是做什么的呢? 这就像小区门口的保安,比较严格的那种,进小区的时候会对你进行询问,看你是不是这个系统的用户,看你是不是这个小区的住户。图中的组件就介绍到这里吧,我们注重体会思想。我们来回答一下这个“治理”是什么意思? 微服务告诉你要对系统进行切割,治理就是处理项目微服务化之后的所带来的的问题,或者说让微服务中的各个服务配合的更好。

相关文章
|
存储 监控 Java
Spring6入门 + Log4j2
Spring6入门 + Log4j2
|
4月前
|
数据采集 存储 API
Scrapy框架实战:大规模爬取华为应用市场应用详情数据
Scrapy框架实战:大规模爬取华为应用市场应用详情数据
|
存储 数据可视化 数据挖掘
图书馆图书可视化分析+大屏
在数字化时代背景下,图书馆已经成为知识获取和共享的重要场所。然而,随着馆藏书籍数量的增加,如何高效管理和利用这些资源成为了图书馆管理者和用户面临的挑战。数据分析和可视化技术的引入为解决这一问题提供了新的途径。本文致力于通过数据分析技术和可视化手段,对图书馆书籍数据进行综合挖掘,希望通过图书分类、书籍价格及读者偏好等多维度信息,进而优化图书馆管理策略、指导书籍采购决策并提升读者服务质量。本文在数字化和信息化快速发展的背景下,图书馆如何利用数据分析与可视化方法来挖掘和优化书籍借阅数据。主要内容包括。
1297 2
|
5月前
|
算法 数据处理 定位技术
基于TDOA算法的三维定位
基于TDOA算法的三维定位
724 0
|
存储 关系型数据库 MySQL
MySQL性能优化实践指南
【10月更文挑战第16天】MySQL性能优化实践指南
812 0
|
SQL 安全 Unix
缓冲区溢出攻击
【8月更文挑战第17天】
633 2
|
网络协议 算法 网络性能优化
计算机网络常见面试题(一):TCP/IP五层模型、TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议
计算机网络常见面试题(一):TCP/IP五层模型、应用层常见的协议、TCP与UDP的区别,TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议、ARP协议
|
程序员 C++ 容器
在 C++中,realloc 函数返回 NULL 时,需要手动释放原来的内存吗?
在 C++ 中,当 realloc 函数返回 NULL 时,表示内存重新分配失败,但原内存块仍然有效,因此需要手动释放原来的内存,以避免内存泄漏。
|
存储 数据管理 应用服务中间件
存储方式
【6月更文挑战第29天】存储方式
1127 2
|
监控 NoSQL Redis
Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis.
Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis.