关于并发的一些设计
Posted wei57960
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于并发的一些设计相关的知识,希望对你有一定的参考价值。
前言
为何需要并发知识?并发是指在客户端请求大,
并发的架构及落地需与业务相结合,业务下是读多还是写多。写多的情况下,是个人写多(锁的粒度小),还是公共写多(锁的粒度大)。淘宝的双11、春运时的抢票、微博大V的热点新闻,秒杀业务等。
那么并发大是指多大并发呢?百万千万的数据、并发对应什么样的架构呢。我们使用的中间件,服务器 Web容器 可以接受多少的请求,多少的并发?你对数据化的指标有没有概念。
高并发设计同样要秉承架构设计的3个原则:简单、合适和演进。“过早的优化是万恶之源”,不能脱离业务的实际情况,更不要过度设计,合适的方案就是最完美的。
1. 高并发设计目标
1.1 宏观目标
并发不仅仅是性能的优化,能够抗住并发量就完事了。高并发系统设计的目标有三个:高性能、高可用,以及高可扩展。
- 高性能:性能体现了系统的并行处理能力,在有限的硬件投入下,提高性能意味着节省成本。同时,性能也反映了用户体验,响应时间分别是100毫秒和1秒,给用户的感受是完全不同的。
- 表示系统可以正常服务的时间。一个全年不停机、无故障;另一个隔三差五出线上事故、宕机,用户肯定选择前者。另外,如果系统只能做到90%可用,也会大大拖累业务。
- 表示系统的扩展能力,流量高峰时能否在短时间内完成扩容,更平稳地承接峰值流量,比如双11活动、明星离婚等热点事件。
这3个目标是需要通盘考虑的,因为它们互相关联、甚至也会相互影响。比如将服务设计成无状态的,这样集群设计保证了高拓展性,那么间接也提升了高可用和高性能。
再比如说:为了保证可用性,通常会对服务接口进行超时设置,以防大量线程阻塞在慢请求上造成系统雪崩,那超时时间设置成多少合理呢?一般,我们会参考依赖服务的性能表现进行设置。
1.2 微观目标
宏观目标种的评判依据有哪些?具体有哪些?怎样评判和测试系统达到这三项标准。
1.2.1 高性能
- 平均响应时间
取一段时间内系统接口的响应时间的平均值
缺点: 对于慢请求不是很敏感
比如1万次请求,其中9900次是1ms,100次是100ms,则平均响应时间为1.99ms,虽然平均耗时仅增加了0.99ms,但是1%请求的响应时间已经增加了100倍。
- TP90,TP99等分位置
将响应时间按时间从小到大排序,TP90为第90%位置的响应时间。数值越大对慢请求越敏感比如第99%位置。
- 吞吐量
和响应时间成反比,响应时间越低吞吐量越高。如果响应时间为1ms则吞吐量为每秒1000次。
通常设定吞吐量目标时会兼顾平均响应时间和TP。比如每秒一万次请求下,AVG在50ms一下,TP99控制在100ms以下。对于高并发系统,AVG和TP分位值必须同时要考虑。
从用户体验的角度来看,200ms被认为是一个分界点,以下的话用户感受不到卡顿。1秒是第二个分界点,用户能感受到延迟但是能接受。
对于一个健康的高并发系统,TP99应该控制在200毫秒以内,TP999控制在1秒以内。
1.2.2 高可用
高可用性指系统具有较高的无故障运行能力,可用性 = 平均故障时间/系统总运行时间。一般用几个9来描述系统的可用性。
对于高可用这块,基本要求是保障3个9 或4个9。原因很简单,如果你只能做到2个9,意味着有1%的故障时间,像一些大公司每年动辄千亿以上的GMV或者收入,1%就是10亿级别的业务影响。
1.2.3 高拓展
高拓展指面对突发情况,不能临时改造项目架构,最快的方式是通过加机器的方式来线性提高系统的处理能力。
对于业务集群或者基础组件来说,扩展性 = 性能提升比例 / 机器增加比例,理想的扩展能力是:资源增加几倍,性能提升几倍。通常来说,扩展能力要维持在70%以上。
但是从高并发系统的整体架构角度来看,扩展的目标不仅仅是把服务设计成无状态就行了,因为当流量增加10倍,业务服务可以快速扩容10倍,但是数据库可能就成为了新的瓶颈。
像mysql这种有状态的存储服务通常是扩展的技术难点,如果架构上没提前做好规划(垂直和水平拆分),就会涉及到大量数据的迁移。
因此高拓展不仅需要考虑应用的拓展(服务集群),还有考虑数据库,缓存,消息队列,第三方服务,负载均衡,带宽。当并发达到一个量的时候,上面每个因素都可能成为系统拓展的瓶颈。
2. 高并发的实现方案有哪些
关于宏观目标和微观目标我们如何实现,以及在不同的业务场景下是怎么取舍的。主要分为通用的方法以及从高性能,高可用,高拓展上分别的说一下。
2.1 通用的拓展方法
通用的方法主要从横向和纵向两个维度。
2.1.1 纵向拓展
提供单机的的处理能力,包括提升单个软件的运行效率,比如jvm调优,mysql调优。让单个软件发挥更大的性能。还有就是提升硬件的运行效率,加硬件的配置,比如cpu 缓存 带宽等。
- 提升单机的硬件性能:通过增加内存、CPU核数、存储容量、或者将磁盘升级成SSD等堆硬件的方式来提升。
- 提升单机的软件性能:使用缓存减少IO次数,使用并发或者异步的方式增加吞吐量。
2.1.2 横向拓展
纵向的拓展总是有瓶颈的,单个服务 数据库,机器处理能力都是有上限的,不管你怎么优化。横向上我们通过集群的方式来增加服务的并发处理能力。横向包括以下两个方面。
- 做好分层架构:这是横向扩展的提前,因为高并发系统往往业务复杂,通过分层处理可以简化复杂问题,更容易做到横向扩展。
上面这种图是互联网最常见的分层架构,当然真实的高并发系统架构会在此基础上进一步完善。比如会做动静分离并引入CDN,反向代理层可以是LVS+nginx,Web层可以是统一的API网关,业务服务层可进一步按垂直业务做微服务化,存储层可以是各种异构数据库。
- 各层进行水平扩展:无状态水平扩容,有状态做分片路由。业务集群通常能设计成无状态的,而数据库和缓存往往是有状态的,因此需要设计分区键做好存储分片,当然也可以通过主从同步、读写分离的方案提升读性能。
2.2 具体的解决方案
2.2.1 高性能
1、集群部署,通过负载均衡减轻单机压力。
2、多级缓存,包括静态数据使用CDN、本地缓存、分布式缓存等,以及对缓存场景中的热点key、缓存穿透、缓存并发、数据一致性等问题的处理。
3、分库分表和索引优化,以及借助搜索引擎解决复杂查询问题。
4、考虑NoSQL数据库的使用,比如HBase、TiDB(NewSql 结合了rdbms和nosql的优点)等,但是团队必须熟悉这些组件,且有较强的运维能力。
5、异步化,将次要流程通过多线程、MQ、甚至延时任务进行异步处理。
6、限流,需要先考虑业务是否允许限流(比如秒杀场景是允许的),包括前端限流、Nginx接入层的限流、服务端的限流。
7、对流量进行削峰填谷,通过MQ承接流量。
8、并发处理,通过多线程将串行逻辑并行化。
9、预计算,比如抢红包场景,可以提前计算好红包金额缓存起来,发红包时直接使用即可。
10、缓存预热,通过异步任务提前预热数据到本地缓存或者分布式缓存中。
11、减少IO次数,比如数据库和缓存的批量读写、RPC的批量接口支持、或者通过冗余数据的方式干掉RPC调用。
12、减少IO时的数据包大小,包括采用轻量级的通信协议、合适的数据结构、去掉接口中的多余字段、减少缓存key的大小、压缩缓存value等。
13、程序逻辑优化,比如将大概率阻断执行流程的判断逻辑前置、For循环的计算逻辑优化,或者采用更高效的算法。
14、各种池化技术的使用和池大小的设置,包括HTTP请求池、线程池(考虑CPU密集型还是IO密集型设置核心参数)、数据库和Redis连接池等。
15、JVM优化,包括新生代和老年代的大小、GC算法的选择等,尽可能减少GC频率和耗时。
16、锁选择,读多写少的场景用乐观锁,或者考虑通过分段锁的方式减少锁冲突。
上述方案无外乎从计算和 IO 两个维度考虑所有可能的优化点,需要有配套的监控系统实时了解当前的性能表现,并支撑你进行性能瓶颈分析,然后再遵循二八原则,抓主要矛盾进行优化。
2.2.2 高可用
1、对等节点的故障转移,Nginx和服务治理框架均支持一个节点失败后访问另一个节点。
2、非对等节点的故障转移,通过心跳检测并实施主备切换(比如redis的哨兵模式或者集群模式、MySQL的主从切换等)。
3、接口层面的超时设置、重试策略和幂等设计。
4、降级处理:保证核心服务,牺牲非核心服务,必要时进行熔断;或者核心链路出问题时,有备选链路。
5、限流处理:对超过系统处理能力的请求直接拒绝或者返回错误码。
6、MQ场景的消息可靠性保证,包括producer端的重试机制、broker侧的持久化、consumer端的ack机制等。
7、灰度发布,能支持按机器维度进行小流量部署,观察系统日志和业务指标,等运行平稳后再推全量。
8、监控报警:全方位的监控体系,包括最基础的CPU、内存、磁盘、网络的监控,以及Web服务器、JVM、数据库、各类中间件的监控和业务指标的监控。
9、灾备演练:类似当前的“混沌工程”,对系统进行一些破坏性手段,观察局部故障是否会引起可用性问题。
高可用的方案主要从冗余、取舍、系统运维3个方向考虑,同时需要有配套的值班机制和故障处理流程,当出现线上问题时,可及时跟进处理。
2.2.3 高拓展
1、合理的分层架构:比如上面谈到的互联网最常见的分层架构,另外还能进一步按照数据访问层、业务逻辑层对微服务做更细粒度的分层(但是需要评估性能,会存在网络多一跳的情况)。
2、存储层的拆分:按照业务维度做垂直拆分、按照数据特征维度进一步做水平拆分(分库分表)。
3、业务层的拆分:最常见的是按照业务维度拆(比如电商场景的商品服务、订单服务等),也可以按照核心接口和非核心接口拆,还可以按照请求源拆(比如To C和To B,APP和H5)。
references
以上是关于关于并发的一些设计的主要内容,如果未能解决你的问题,请参考以下文章