• 架构与思维:互联网高性能Web架构 Hello


    1 什么是高性能Web架构


    在互联网业务中,我们经常会面临流量巨大的复杂的分布式场景。这就要求我们在设计系统的时候保证系统具有承载高并发(High Concurrency)的能力,同时能够保证系统的高可用性(High Availability)。
    所以,具备高性能Web架构通常是指,通过稳健的系统设计能力,来保证系统能够同时处理复杂的业务场景,并保证性能、稳定性、可用性的架构体系。
    高性能Web常用的一些衡量指标有响应时间(Response Time),吞吐量(Throughput),每秒请求数QPS(Query Per Second),并发用户数等。

    响应时间(RT)  响应时间是指系统对请求作出响应的时间,如一个请求从发送request到response的时间是500ms,这就是请求的响应时间。
    吞吐量(Throughput)  吞吐量是指系统在单位时间内处理请求的数量。
    并发用户数  并发数是指系统同时能处理的请求数量,这个也是反映了系统的负载能力。

    与吞吐量相比,并发用户数是一个更直观但也更笼统的性能指标。实际上,并发用户数是一个非常不准确的指标,因为用户不同的使用模式会导致不同用户在单位时间发出不同数量的请求。

    QPS每秒查询率(Query Per Second) 每秒查询率QPS是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准
    TPS每秒事务数(Transactions Per Second) 是吞吐量的常用量化指标

    2 如何提升系统的性能指标


    在互联网分布式架构设计,我们常常用两种方式来提高系统的性能,一种是横向扩展(Scale Out),一种是纵向扩展(Scale Up)。对于横向纵向扩张我们在 微服务系列MySQL系列Redis系列 中已经多次 讨论过了。
    纵向扩展主要提高单机的性能,让其算力不断提升。横向扩展就是典型的分治思维,单机的内存、CPU、连接数、吞吐量始终是有限的,多实例化的集群支撑才是解决大型分布式场景的保障。

    2.1 纵向扩展(Scale Up): 提升单机算力

    1、单机硬件水平提升,如 CPU(多线程计算服务消耗大)、内存(缓存等)、GPU(音视频、直播类)、SSD磁盘(数据存储等),升级到更高的配置;
    2、提升单机架构性能,很多时候不是服务器硬件水平低,而是架构的不合理。如:

    • 服务模块化分层不合理,导致调用互相影响,请求链路过长。
    • 未合理地使用缓存,导致系统计算时间过长,IO操作频率过高。
    • 存储服务,如数据库可优化空间大,如 索引使用不当,单表容量过大,单表字段过多,查询未优化等。
      这些都是在架构设计的时候需要谨慎对待的。
      但是正如上面说的,不论是单机硬件性能,还是单机架构性能,优化都有一个极限,互联网分布式架构设计高并发终极解决方案还是横向扩展。

    2.2 横向扩展(Scale Out):

    典型的分治思维,单机的内存、SSD、CPU、GPU、连接、吞吐量始终是有限的,总不能无限制的扩展。所以我们需要追求横向扩展,在架构的各个层面做水平扩容,增加实例数量,比如Services Cluster、Redis Cluster、MySQL Cluster,并在长期的业务实践中不断优化。
    相对于Scale Up,他有哪些优势:

    • 理论上的性能无限扩充,单机的扩容总是有限制的,但是集群是可以持续扩张。bat大厂核心服务的成千上万的服务集群就是很好的业内范例。
    • 提高可用性,水平扩容最终会把计算能力和数据能力分摊在不同的实例、甚至不同的服务、甚至不同机房。某个分区出问题不会导致整个系统崩塌,这个单体系统是不能保障的,哪怕他硬件再牛。

    3 业内常见的系统分层架构如下:


    image

    • Client: 包括Web浏览器、App应用、还有第三方请求。
    • Reverse Proxy:流量的入口,提供 反向代理、负载均衡 等能力的支持
    • Web Services: Web服务层,处理业务逻辑,直接面向用户视觉,返回HTML页面、文档、Json信息等
    • Computing Services:提供计算能力,所有繁琐的业务计算、数据存取、缓存存取。
    • Cache:缓存层,主要消耗内存,提供高效率数据存取。
    • Data:数据层,将数据持久化到硬盘。

    如果要保证整个系统的高性能,那不是单纯对缓存层或数据层进行扩容的事情,就要求各层都可以水平扩展,下面我们一一来拆解下。

    4 分层水平扩展架构实践


    4.1 Reverse Proxy 的Scale Out


    image
    一般情况下,我们使用 Nginx 来实现 Load Balance(Random、Round Robin、Hash等),但是单台Nginx照样存在宕机的风险。这时候我们就使用DNS 轮询
    在 Nginx 上层做反向代理,一般会在一个域名上配置多个IP解析。当我们访问 DNS服务的时候,他会轮询的返回IP。而我们增加Nginx实例的时候,同时也在DNS 服务上增配解析IP,这样就达到理论上的无限扩容了。
    如图中,当我们有多个Nginx实例的时候,我们还可以配置keepalive做探活,以保证可用性。

    4.2 Web Services 的 Scale Out


    image
    Web Services的上层是Nginx,他本身就是出色的负载均衡利器。当我们有多个服务实例的时候,只要在Nginx上conf文件上进行配置,就可以实现强大而稳定的负载了。而且对服务实例的新增和缩减,可以达到高效配置和快速扩缩的能力。Nginx可以调度足够多的Web Services实例,实现互联网系统高并发运行。

    4.3 Computing Services 的 Scale Out


    image
    计算服务这一层的横向扩展,可以使用 命名服务(Name Server)模式进行处理。比如我们建立一个高可用的服务注册与发现中心。
    Alive的计算服务实例都注册到服务中心去,Web服务层则订阅服务中心,获取可用的计算服务,并执行调用。无论是使用Rest API调用,还是使用RPC调用,当你明显发现计算服务无法支撑逐渐上升的流量的时候(比如用户量暴涨、请求暴增),可以部署新的计算服务实例,并自动地注册到服务中心。实现平滑的动态的计算服务扩容相当优雅。
    另外,目前的服务间调用一般是去中心化的,会把订阅到的注册服务实例信息保存在本地(比如 Web 服务 把 计算服务地址 列表保存在本地),哪怕服务中心挂了,短时间内,也不影响请求的正常运转。

    4.4 数据层 的 Scale Out


    这个算老生长谈了,我们在 MySQL系列Redis系列 中中已经不厌其烦的讨论过 缓存层 和 数据库层水平扩容的方式了。
    在互联网高速发展的今天,巨量的数据已经覆盖我们生活的每个角落,我们的衣食住行都是海量的数据进行支撑和管理。当我们的数据过于庞大的时候,一般会通过分库分表,来将数据库拆分到不同的实例,甚至服务器上,来达到存储层系统性能扩展的目的。
    MySQL分库分表》中我们有详细说拆分的原理和方式,以MySQL为例,
    这边我们再做一下整理。一般有以下三种拆分方式:

    4.4.1 按照Range水平拆分


    image
    上面的示例,使用了范围RANGE函数对岗位编号进行分区,共分为3个分区,
    岗位编号为1 ~ 25w 的对应在分区P0中,25w+1 ~ 50w 编号在分区P1中,依次类推即可,类别编号大于 50w的数据统一存放在分区P3中即可。
    这种模式的优势是:

    • 判断简单,只要对值进行判断一下,就能找到对应的存储分区;
    • 区间自定义的情况下,可以比较均匀的分布数据。
    • 扩展简单,当超过50w的数据比较多的时候,可以再加一个 id(50w,75w) 的分区。
    • 可能存在的问题就是,各Range之间的请求压力可能不均衡,比如访问频率最高的用户数据可能都落在第2区间。

    4.4.2 按照HASH水平拆分


    image
    上面的例子,使用HASH函数对createtime日期进行HASH运算,并根据这个日期来分区数据,这里按照 年 为单位共分为3个分区。
    建表语句上添加一个“PARTITION BY HASH (expr)”子句,其中“expr”是一个返回整数的表达式,它可以是字段类型为MySQL 整型的一列的名字,也可以是返回非负数的表达式。
    这种模式的优势是:

    • 同样的,判断简单,只要对相应字段进行hash计算一下就可以了。
    • 大部分情况下,数据分布也比较均匀,请求分布都可以比较均匀。比如你使用日期、性别等来hash,得到的结果一般是比较均衡的。
    • 缺点是:扩展性不佳,扩增一个数据实例,或者hash属性对应改变的时候,意味着你的数据所属区要重新划定,数据迁移是个不可缺少的成本。

    4.4.3 按照LIST(预定义列表)拆分


    image
    上面的例子,使用了列表匹配LIST函数对位置编号进行分区,共分为2个分区,编号为46,77,89的对应在分区P0中,106,125,177类别在分区P1中。
    不同于RANGE的是,LIST分区的数据必须匹配列表中的岗位编号才能进行分区,所以这种方式只是适合比较区间值确定并少量的情况。

    不管是哪种模式,我们通过水平扩展,将数据分摊到不同的实例甚至服务器上,以此提升了数据库单实例的性能和稳定性。

    5 总结


    在大型互联网分布式系统的架构设计中,High Concurrency (高性能)、High Availability(高可用) 是构成的关键因素,我们通过精妙的设计,来保证系统能够同时并行处理足够多的请求、能够提高稳定持久服务的时间(n个9)、能够大幅降低事务处理时间。
    提高系统并发能力的方式,方法论上主要有两种:垂直扩展(Scale Up)与水平扩展(Scale Out)。
    Scale Up 是指单机硬件水平提升,如 CPU(多线程计算服务消耗大)、内存(缓存等)、GPU(音视频、直播类)、SSD磁盘(数据存储等),升级到更高的配置,来提高并发性和可用性,但单机性能终归是有限的,不可能无限扩展。
    在我们上面的讨论中,我们通过对各层逐层的水平扩容来实践互联网系统保持高性能高可用性的可能性。

    • Reverse Proxy 通过 DNS轮询 的方式实现IP解析的增配,以保证快速的扩容。
    • nginx 通过配置conf 实现对上游 Web Services 的扩容和负载均衡。
    • Computing Services 通过服务注册中心实现新增服务实例的自动发现,保证水平扩容的高效和稳定。
    • 数据层可以使用 数据库分区 来实现水平扩展。
  • 相关阅读:
    【报错】引入jar包import org.apache.commons.codec.digest.DigestUtils 报错,jar不存在
    【spring data jpa】使用jpa的@Query,自己写的语句,报错:org.springframework.expression.spel.SpelEvaluationException: EL1007E: Property or field 'status' cannot be found on null
    【java】在分页查询结果中对最后的结果集List进行操作add()或remove()操作,报错:java.lang.UnsupportedOperationException
    【java】java中直接根据Date 获取明天的时间
    【java】处理时间字段 在数据库查询的时候只想要年月日,不想要时分秒 ,java中设置时间类型为年月日,java中设置Date中的时分秒为00.00.000
    【spring data jpa】 spring data jpa 中 时间格式设置between and 查询
    【mybatis】mybatis 中select 查询 select * 查询出来的数据,字段值带不出来 数据不全
    【spring mvc】spring mvc POST方式接收单个字符串参数,不加注解,接收到的值为null,加上@RequestBody,接收到{"uid":"品牌分类大”},加上@RequestParam报错 ---- GET方式接收单个参数的方法
    Centos7安装配置tomcat 9并设置自动启动
    centos7 yum安装配置redis 并设置密码
  • 原文地址:https://www.cnblogs.com/wzh2010/p/15730040.html
Copyright © 2020-2023  润新知