1.1 应用架构

本课程专为初学者设计,因为您不会长时间设计严肃应用程序的架构。但别担心,好的架构是例外而不是规则。在构建应用程序之前选择正确的应用程序架构是非常困难的。

大型服务器应用程序的流行架构示例:

  • 分层架构(Layered Architecture)。
  • 分层架构。
  • 面向服务的体系结构 (SOA)。
  • 微服务架构(Microservice Architecture)。

他们每个人都有其优点和缺点。但是研究它们不会给你任何东西。架构是对“如何组织系统内数千个对象的交互”这个问题的答案。在您体验到问题的全部复杂性之前,您将无法理解解决方案的全部多功能性。

所有应用程序都使用某种架构,或者至少假装使用某种架构。因此,了解流行的应用程序设计方法将使您能够快速更好地理解应用程序的工作原理。这意味着在您需要的地方进行更改。

“必要时进行更改”是什么意思?有没有不需要修改的地方?确切地。

具体来说,假设您正在从事一个中型后端项目。它由一个 20 人的团队编写了 5 年。该项目耗时 100 人年,包含约 10 万行代码。它总共包含两千个类,分为 10 个不同大小的模块。

并添加一个严酷的现实。一些任务的逻辑分布在几个模块中。此外,业务逻辑可以在前端(用 JavaScript 编写)和/或直接在数据库中作为存储过程编写。您仍然确定可以立即确定要进行更改的确切位置吗?

这不是我为了吓唬你而编造的噩梦。这是一个典型的项目。情况更糟。为什么会这样?可能有多种原因,但几乎总是这样:

  • 很多人都在从事这个项目——他们每个人的看法都有所不同。
  • 5年,项目换了10个人,新人不太懂。
  • 创建软件是不断做出改变,不断改变一切。
  • 五年前,当我们决定架构时,项目的想法有些不同。

但最主要的是,无论项目的架构如何,从事该项目的所有程序员都对这个项目的工作方式有着相同的理解。让我们从最简单的概念开始——客户端-服务器架构。

1.2 客户端-服务器交互的概念

现在我们将理解作为客户端-服务器体系结构基础的概念,并将让您更好地理解 Internet 上数百万程序的交互是如何组织的。

顾名思义,这个概念涉及两方:客户端服务器。这里的一切就像生活一样:客户端是这个或那个服务的客户,服务器是服务提供者。客户端和服务器实际上是程序,例如典型的客户端是浏览器

可以给出以下示例作为服务器:

  • Web 服务器,例如 Tomcat。
  • 数据库服务器,例如 MySQL。
  • 像 Stripe 这样的支付网关。

客户端和服务器通常通过 Internet 进行通信(尽管它们可以在同一局域网中工作,并且通常可以在任何其他类型的网络中工作)。通信通过标准协议(如 HTTP、FTP)或较低级别的协议(如 TCP 或 UDP)进行。

通常根据服务器提供的服务类型来选择协议。例如,如果是视频通话,那么通常使用UDP。

还记得 Tomcat 及其 servlet 是如何工作的吗?服务器接收 HTTP 消息,将其解包,从那里提取所有必要的信息并将其传递给 servlet 进行处理。然后将处理结果打包回 HTTP 响应并发送给客户端。

这是典型的客户端-服务器交互。浏览器是 Web 客户端,Tomcat 是 Web 服务器。Tomcat 甚至被称为网络服务器。

但仔细想想,重要的不是名字,而是本质——程序之间的角色分配。您在 HTML 页面中运行的 JS 脚本可以称为客户端,而您的 servlet可以称为服务器。毕竟,它们在客户端-服务器概念的框架内成对工作。

1.3 一个重要的细微差别

还值得注意的是,客户端-服务器交互基于这样的交互由客户端发起的原则:服务器只回答客户端并报告它是否可以向客户端提供服务,如果可以,在什么条件下.

客户端位于何处以及服务器位于何处并不重要。客户端软件和服务器软件通常安装在不同的机器上,但也可以在同一台计算机上运行。

这个概念是作为简化复杂系统的第一步而开发的。她有这些优点:

  • 逻辑简化:服务器不知道客户端的任何信息以及将来如何使用其数据。
  • 可能存在弱客户端:所有资源密集型任务都可以转移到服务器。
  • 独立开发客户端代码和服务端代码。
  • 许多不同的客户端,例如 Tomcat 和不同的浏览器。

最基本的客户端与服务端交互的版本如图所示:

客户端服务器

重要的是要注意这里的两个细节。一、图片显示多个客户端可以访问一台服务器。其次,他们可以同时访问它。这也是服务器的重要组成部分。

一个客户端通常与一个用户交互,因此通常甚至不需要授权。但是,服务器处理来自数千个客户端的请求,在为其开发代码时,您需要能够区分授权和身份验证。

服务器并行处理数千个请求也很重要。这意味着在开发后端代码时,您将需要不断考虑并发访问资源的任务。此外,服务器代码极有可能出现竞争条件(线程竞争)、死锁(线程相互阻塞)。

必须监控重要对象的生命周期:

您不能通过new Thread().start(). 相反,您需要有一个将在所有服务线程之间共享的线程池。

此外,您不能只启动一个异步任务,因为它们也是在单独的线程中执行的。创建这样的任务时,您应该始终知道哪个线程池正在执行它,以及如果这样的池溢出会发生什么。

所有对文件和目录的操作都必须通过 try-with-resources 完成。如果在正常的应用程序中您忘记关闭流或文件,这是一个问题吗?当您退出程序时它会自行关闭。但是如果你忘记在代码中某处关闭服务器上的文件,而你的服务器已经运行了几个月......很快,成千上万个这样未关闭的文件将累积起来,操作系统将停止打开新文件进行读取(work with files由操作系统控制)。Teamlead 不会拍你的头...

1.4 客户端-服务器架构

另一个重点。客户端-服务器体系结构只定义了计算机之间交互的一般原则,交互的细节由各种协议决定。

这个概念(客户端-服务器)告诉我们,我们需要将网络上的机器分为客户端机器和服务器机器,客户端机器总是需要一些东西,而服务器机器则提供他们需要的东西。在这种情况下,客户端始终启动交互,交互发生的规则由协议描述。

客户端-服务器交互架构有两种类型:第一种称为双层客户端-服务器架构,第二种是多层客户端-服务器架构(有时称为三层架构或三层架构,但这是一个特例)。

客户端-服务器交互的两层体系结构的操作原理是请求的处理发生在一台服务器上,而在此处理过程中不引用其他服务器。

两层客户端-服务器交互模型可以画成一个简单的图。

两层客户端-服务器架构

这里可以看到第一层是客户端的一切,第二层是服务端的一切。