软件的可扩展性是一个很有趣的话题。实现软件可伸缩性涉及的因素很多,在本文中我们将讨论一些与开发和运营相关的因素。我们将深入讨论如何编写软件(软件开发)和如何运行软件(运营)以实现软件的可扩展性。对于初学者来说,成本和可扩展性通常是成正比的。1.什么是软件可扩展性Full-scaleblog将软件可扩展性定义为:软件可扩展性是一种工具或系统能够根据用户需要增加其容量和功能的属性。可扩展软件在适应变化、??升级、检修和资源扩展的同时保持稳定。因此,如果软件能够弹性地处理负载,在请求量增加时分配更多的资源(通常是动态的),那么我们可以说这个软件是可扩展的。要在现实中实现这一点,我们还需要注意代码部分。2.从开发的角度看软件的可扩展性软件工程师应该知道如何编写可扩展的软件。您应该首先专注于编写使软件易于扩展的代码。编写边缘可用的软件很容易,但很难编写易于测试、维护和扩展的代码。以下是一些使软件更具可扩展性的编程技术。可扩展软件的高性能代码编写软件应用程序时可以只考虑可用性,也可以考虑软件的可扩展性、可维护性和灵活性。选择合适的算法是基于时间和空间的复杂度,针对场景选择合适的算法可以产生很好的效果。了解大O符号和流处理以对抗空间复杂性对于编写可伸缩软件非常有帮助。例如,您可以使用二分搜索代替线性搜索来加快算法执行速度。在对空间复杂度要求高的场景下,可以用少量内存实现基于流处理的大文件小内存复制。观看这个6分钟的视频,了解排序算法的可视化。https://www.youtube.com/watch?v=kPRA0W1kECg更好的内存管理作为软件工程师,您应该关心内存管理、垃圾收集等事情,不要让它们成为可扩展性的障碍。预测和编写资源争用代码对于可扩展软件也很重要。选择高性能库还有其他方法可以帮助软件扩展,包括比较和使用更多高性能解决方案。例如,您可以使用javascript代替lodash以获得更快和更高的性能。另外,不要仅仅因为某个库或包很流行就使用它,还要检查你的软件的性能和可扩展性影响。例如,您可以使用Day.js而不是Moment.js来执行简单的日期操作。如果需要,本机方法也可用于使软件更具可扩展性。异步处理想象一下,当客户下单成功,需要发送订单确认邮件,你会怎么做?我总是建议异步执行它,因为它是流程的非关键部分。使用队列和消费者您可以轻松地设置队列和消费者来完成下订单后发送电子邮件的任务。就算邮件晚发1分钟也没关系。如果您的订单量很大,您可以通过扩展消费者数量来减少延迟。任何非关键或非阻塞任务都可以推送到后台异步完成,这有助于优化可用资源而不会出现故障。异步处理的另一个例子是使用异步代码。根据您的编程语言的具体情况,您都应该能够将某些任务推送到后台执行。当一个任务被执行时,它可以发送一个响应表明它已经被安排好了。您可以在Node.js中查看异步响应示例。当然,这取决于你选择的语言,有些语言(比如PHP)可能不提供开箱即用的异步代码。https://geshan.com.np/blog/2020/11/nodejs-for-php-developers/#node.js-code-execution-is-async-and-non-sequentialWritingstatelessforscalablesoftware程序无状态是高度可扩展软件的先决条件。正如Redhat在比较无状态和有状态时提到的那样,“将无状态事务视为自动售货机:一个请求对应一个响应”,而将有状态程序描述为“您可以将有状态事务视为同一个人之间正在进行的多轮对话”。无状态软件在请求之间不共享任何东西,也不依赖于本地文件系统之类的东西。不要使用本地文件系统。如果您需要保存文件,您应该使用可靠的远程系统,例如具有访问控制的存储,例如AmazonS3存储桶。这使得通过CDN保存文件和提供可扩展的服务变得容易,这通过静态和动态的分离提高了软件的可扩展性。与网站相比,使用CDN可以更有效地提供图像和其他此类PDF文件等静态内容。使用Apache或Nginx提供动态内容优于提供静态内容。使用客户端会话而不是服务器端会话的另一个经典示例是在Web应用程序中使用客户端cookie而不是服务器端会话。您可以使用类似JsonWebToken(JWT)的身份验证和授权方案轻松替换服务器端会话。JWT可以很容易地从客户端传递到服务器,每个请求都作为标头或cookie的一部分。因为服务器像牲畜而不是宠物一样工作,所以很容易扩展软件。如果一定要用session,那就用redis之类的数据库,而不是存储在本地文件系统上,这样就可以方便的更换服务器。http://cloudscaling.com/blog/cloud-computing/the-history-of-pets-vs-cattle/这里的关键点是,不要挂在你的服务器上,它们应该一次性配置并基于弹性负载。这样,我们就可以通过编写无状态软件来实现轻松的可扩展性和高可用性。3.从操作的角度来看软件的可扩展性术语操作和平台,我指的是软件部署和运行的位置和方式,还包括这些系统的架构以及它们如何交互。软件部署的位置至关重要。如果你的用户在悉尼,而软件部署在欧洲,那么网络延迟会很严重。同样,布局不当或组件选择不当也会产生负面影响。让我们看看在操作级别对软件可扩展性有关键影响的因素。垂直扩展与水平扩展这是关于服务器类比是牲畜还是宠物的扩展讨论。想象一下,您正在管理一个相当受欢迎的电子商务网站,每天有大约500个订单和50,000个独立访问者。您有一个规格接近AmazonEC2m5.4xlarge的大型Web服务器,它有16核CPU和64GB的大内存。我们假设在其上运行WooCommerce商店,包括网站服务和MySQL数据库都在同一台服务器上运行。现在,距离黑色星期五只有3个月的时间,该公司将开展大规模的电视广告活动,预计节日期间的流量将增加5-7倍。管理层会花很多钱做广告,而且网站4-5天都不能宕机。预计在这3-4天内,该网站每天的独立用户访问量将超过30万次,订单量将超过3000个。您现在有两个选项来扩展应用程序,垂直(向上扩展)或水平(向外扩展)。垂直扩展(Scale-Up)如果选择垂直扩展,需要增加更多的硬件资源来解决这个问题。您可以改用EC2m5.24xlarge机器,它具有96核CPU和384GB内存。CPU和内存是旧机的6倍,理论上应该够支撑了。但是有3个重要问题,首先你需要一点停机时间来升级硬件,其次也是最重要的原因是这台机器会出现单点故障。考虑到网站负载,数据库很可能由于某些问题而崩溃。后面如果流量没有想象的那么大,你也会做一个shrink操作,避免过度的资源浪费。横向扩展另一种选择是横向扩展,您将尝试获得许多较小的EC2实例,比如8-50个t3.mediums实例。每个实例将有2个CPU内核和4GB内存。因此,一个包含50个t3.mediums实例的集群将为您提供总共100个CPU内核和200GB内存。要在这些新的EC2实例集群之间平均分配负载,您可以使用AmazonApplicationLoadBalancer。为了使您的应用程序更具可扩展性,您可以使用具有32个CPU内核和128GB内存的AmazonRDSdb.m5.8xlarge实例。您还可以根据需要配置备份。此时,您有50台服务器可以使用。如果其中3个损坏,您可以立即更换3个新的。如果负载低,只有3个实例在运行,当流量激增时,可以分分钟加20个。打折季结束后,可以扩容到db.m5.large,每天500个订单就够用了。考虑到这一点,让我们在下面直观地解释一下。这是Docker和Kubernetes的优点之一,你可以将你的工作任务打包到轻量级的容器中,Kubernetes可以管理这些容器的水平扩展和滚动部署。多年来,Docker改变了我们工程师的工作方式。https://geshan.com.np/blog/2018/11/4-ways-docker-changed-the-way-software-engineers-work-in-past-half-decade/这里要提到的一点是扩展关系数据库非常困难。即使使用分片之类的东西,如果您不知道自己在做什么,垂直扩展关系数据库也比水平扩展更容易。亚马逊就是一个例子,同样的概念可以应用于任何其他主要的云提供商,例如谷歌云或Azure。这就引出了我的下一点,NoSQL数据库的使用。使用NoSQL提高软件可扩展性在上面的示例中,如果您的在线商店网站上有20个人,您可以使用关系数据库来为其提供服务。对于来自每个用户的每个请求,应用程序都会访问关系数据库,这很慢但不是灾难性的。现在假设有120个用户同时在线,性能可能明显下降严重,我们可以看到一些基于预分配数据库的数据库连接问题。https://sysadminxpert.com/aws-rds-max-connections-limit/用于可扩展软件的NoSQL数据库NoSQL数据库非常灵活,因为我们可以使用像Redis这样的NoSQL内存键值存储。使用像Redis这样的内存数据库来提供所有产品详细信息将大大减少响应时间。另一种用途是使用Solr或ElasticSearch进行快速的多条件搜索,例如查询“mediumredadidasbrandt-shirt”,而不是运行复杂的SQL查询。Solr是支持事务的NoSQL数据库,有助于提高软件的可扩展性和弹性。Redis和Solr/ElasticSearch都需要提前准备一些数据才能正常工作,但这肯定比每个客户端请求都去查询关系数据库要好得多。对于每个写入请求,都需要写入关系数据库。例如,客户的每次购买都必须存储在关系数据库中,而在所有浏览场景中至少有80-90%的流量我们可以使用NoSQL数据库,这可以使软件更具扩展性。最终一致性和CAP理论NoSQL数据库速度很快,因为它们对最终一致性进行了权衡。为了更好地理解数据存储,我强烈建议更新您的CAP理论知识-一致性、可用性和分区容错性。https://twitter.com/mykola/status/1101337299525267457您可以在这篇关于高可扩展性的文章中了解有关如何将用户从100万扩展到1100万的更多信息。由于NoSQL数据库也可以用作高效缓存,这引出了我的下一个要点,即使用高效缓存来实现软件可扩展性。http://highscalability.com/blog/2016/1/11/a-beginners-guide-to-scaling-to-11-million-users-on-amazons.html缓存支持软件扩展正如PhilCarlton所说只有计算机科学中的两个难题:缓存失效和命名。缓存失效也是一个有趣的问题。您将需要大量缓存,因为没有缓存的常规方式不再具有可扩展性。不同级别的缓存对于良好的软件可伸缩性起着至关重要的作用。以下是您可以利用缓存来实现更具可扩展性的软件的一些方法。Memoization第一级缓存可以在代码级别进行,其中一种基本方法是Memoization。记忆化是其他缓存函数的高阶函数。它可以优化一些慢功能。它缓存函数第一次调用后的结果,后续调用只要参数相同就可以直接在缓存中找到结果。您可以看到一个Node.jsMemoization示例,其中服务器可以将响应缓存1分钟。所以1分钟之内,即使数据发生了变化,客户端得到的还是老数据。https://geshan.com.np/blog/2020/11/nodejs-for-php-developers/#memoization-exampleHTTP缓存另一层缓存可以在HTTP层完成。通过良好地使用HTTP标头,可以根据需要缓存响应。还可以使用Cloudflare等应用程序实现HTTP缓存,并设置规则使响应缓存几分钟甚至几小时,以减少服务器负载。这种类型的缓存机制帮助我们实现高水平的软件可扩展性。如果您有能力管理完整的HTTP缓存和HTTP加速器,那么Varnish是一个不错的选择。Varnish声称:根据您的架构,它通常提供300-1000倍的交付速度。目前VarnishDocker镜像的下载量已超过一百万,我认为人们可能已经大量使用Kubernetes,因为它具有无与伦比的软件可扩展性和巨大的HTTP缓存。https://hub.docker.com/_/varnish我不确定只读副本是否是一种纯数据库缓存机制。但我很确定从只读副本运行查询可以大大减轻主数据库的压力并有助于提高软件的可扩展性。当然还有许多其他方法可以在多层应用程序上实现缓存。在了解了软件可扩展性之后,您很可能希望添加缓存以提高系统速度,具体取决于您的情况。4.结语软件的可扩展性是一个难题,运行环境使其更加复杂。一个中型公司眼中的大型概念在FAANG公司眼中可能根本排不上号。究竟什么级别才算大规模取决于您的软件系统每天处理的RPM/RPS。我没有真正每秒处理数十万或数百万请求的系统,我只听说过这样的规模。我实际看到和工作过的系统每秒有100到1000个请求,即使在这种规模下满足软件可伸缩性也是一个非常有趣和具有挑战性的问题。
