扩展您的 Web 应用程序:基本步骤
已发表: 2022-09-13为您的业务创建应用程序是不够的,您需要优化它们。 一个有效的方法是规模化。 在本文中,您将了解代码优化、架构优化以及如何构建可扩展的 Web 应用程序。
优化
Gearheart 建议问自己以下问题:
- 数据库查询是否最优(解释分析、索引的使用)?
- 数据存储是否正确(SQL 与 NoSQL)?
- 是否使用缓存?
- 对 FS 或数据库没有不必要的请求?
- 数据处理算法是最优的吗?
- 环境设置是否最佳:Apache/Nginx、MySQL/PostgreSQL、PHP/Python?
这些问题中的每一个都可以在单独的文章中讨论,因此在本文的框架内详细考虑它们显然是多余的。 重要的是要了解,在开始扩展应用程序之前,非常希望尽可能优化其工作 - 事实上,有可能根本不需要扩展。
缩放
假设您已经优化了您的应用程序,但它仍然无法处理负载。 在这种情况下,显而易见的解决方案是将应用程序分布在多个主机上,以便通过增加可用资源来提高应用程序的整体性能。 这种方法被正式称为“缩放”应用程序。 更准确地说,可伸缩性是系统通过增加可用资源量来提高其性能的能力。
有两种类型的可扩展性:垂直和水平。 垂直可扩展性意味着通过在一个节点(主机)中添加资源(CPU、内存、磁盘)来提高应用程序性能。 水平扩展是分布式应用程序的典型特征,意味着通过添加另一个节点来提高应用程序性能。
很明显,最简单的方法是简单的硬件升级(处理器、内存、磁盘)——即垂直扩展。 此外,这种方法不需要对应用程序进行任何修改。 然而,垂直缩放很快达到了极限,之后开发人员和管理员别无选择,只能切换到应用程序的水平缩放。
应用架构
大多数 Web 应用程序都是先验分布式的,因为它们的架构至少可以分为三层:Web 服务器、业务逻辑(应用程序)、数据(数据库、静态)。
这些层中的每一层都可以缩放。 因此,如果您的系统有一个应用程序和一个数据库驻留在同一主机上,那么第一步绝对应该将它们分开在不同的主机上。
瓶颈
在进行系统扩展时,首先要做的是确定哪一层是“瓶颈”,即比系统的其余部分慢。 首先,您可以使用诸如 top (htop) 之类的普通实用程序来评估 CPU/内存消耗,并使用 df、iostat 来评估磁盘消耗。 但是,最好为单独的主机提供战斗负载仿真(使用 AB 或 JMeter),您可以在其上使用 xdebug、oprofile 等实用程序分析应用程序。 您可以使用 pgFouine 之类的实用程序来识别狭窄的数据库查询(当然,最好根据来自战斗服务器的日志来执行此操作)。
通常它取决于应用程序的体系结构,但通常最有可能成为瓶颈的候选者是数据库和代码。 如果您的应用程序处理大量用户数据,则瓶颈很可能是静态存储。
数据库扩展
如上所述,现代应用程序的瓶颈通常是数据库。 它的问题通常分为两类:性能和需要存储大量数据。
您可以通过将数据库划分为多个主机来减少数据库的负载。 它们之间存在同步的严重困难,可以通过实现同步或异步复制的主/从方案来解决。 对于 PostgreSQL,您可以使用 Slony-I 进行同步复制,使用 PgPool-II 或 WAL (9.0) 进行异步复制。 为了解决读写请求拆分的问题,以及平衡slave之间的负载,可以配置一个特殊的数据库访问层(PgPool-II)。
在关系数据库的情况下存储大量数据的问题可以通过分区(PostgreSQL 中的“分区”)或通过将数据库部署在分布式数据库(如 Hadoop DFS)上来解决。
您可以在关于配置 PostgreSQL 的优秀书籍中阅读这两种解决方案。
1.但是,对于存储大量数据,最好的解决方案是分片,这是大多数NoSQL数据库(例如MongoDB)的固有优势。
2.此外,由于缺乏解析/优化查询、检查数据结构完整性等方面的开销,NoSQL 数据库通常比它们的 SQL 兄弟运行得更快。比较关系数据库和 NoSQL 数据库的主题也相当广泛,值得单独的文章。
3.另外值得注意的是Facebook的经验,它使用MySQL没有JOIN选择。 这种策略允许他们更轻松地扩展数据库,同时将负载从数据库转移到代码,如下所述,代码比数据库更容易扩展。
代码缩放
- 扩展代码的复杂性取决于您的主机需要多少共享资源来运行您的应用程序。 它只是会话,还是需要共享缓存和文件? 无论哪种方式,首先要做的是在具有相同环境的多个主机上运行应用程序的副本。
- 接下来,您需要在这些主机之间设置负载/请求平衡。 您可以在 TCP (HAProxy)、HTTP (nginx) 或 DNS 上执行此操作。
- Gearheart 提到,下一步是使静态文件、缓存和 Web 应用程序会话在每个主机上都可用。 对于会话,您可以使用通过网络工作的服务器(例如,Memcached)。 作为缓存服务器,当然可以在不同的主机上使用相同的 Memcached。
- 静态文件可以通过 NFS/CIFS 或使用分布式 FS(HDFS、GlusterFS、Ceph)从一些共享文件存储中挂载。
还可以将文件存储在数据库中(例如,Mongo GridFS),从而解决可用性和可伸缩性问题(考虑到 NoSQL 数据库的可伸缩性问题是通过分片解决的)。
另外值得注意的是,部署到多个主机的问题。 如何确保用户通过单击“更新”不会看到不同版本的应用程序? 在我看来,最简单的解决方案是从配置负载均衡器(网络服务器)中排除未更新的主机,并在进行更新时按顺序打开它们。 您还可以通过 cookie 或 IP 将用户绑定到特定主机。 如果更新需要对数据库进行重大更改,最简单的方法是暂时关闭项目。