高并发,大流量分布式系统要点

时间:2020-12-04 17:59:42

现在很多高谈阔论,高并发,大流量,分布式,SOA,名词一大堆往往抓不住要点,对于熟悉的人来说,言之无味,而对不熟悉的人来说,更类似大师讲法,除了增加神秘感外,让人越发无从了解。

其实这些问题,本质是成本收益的平衡,严格说,这其实就是所谓技术最关注的问题,
正因为没有银弹,没有统一的解决方案,所以任何系统都要结合业务自身特点,与软件,硬件平衡一起考虑完整的解决方案。

可以从一个典型的电商平台来分析,

假如,从无到有,有足够人力时间,而且目标也非常明确,一个支持海量商家,海量用户的,海量商品的平台,应该怎么设计?

1.前端接入
只要高并发,即便全部是静态页面,哪怕只有一个文件,海量的并发连接也是必须首先解决的。
这个方案相对比较成熟,可以通过负载均衡,简单增加web服务器,承载浏览器连接。单台服务器nginx能承载的并发连接大致5000到数万,
根据优化情况,可以简单算出到底需要多少台机器。在用户使用浏览器的情况下,这里几乎没有取巧的地方。
如果不能接受则只能考虑专用客户端,通过长链接,甚至UDP这类协议,来自己实现。

2.一旦请求全部接入了,就需要考虑处理问题
如果应用很单一,那么应用服务器就可以简单扩展,类似接入的web服务器,单台时间能处理的请求数,和单位时间总请求数,即可算出需要多少应用服务器,当然一般场景往往与用户相关,这里最重要的就是要解决,应用服务器无状态的问题,这样才能无缝扩展,任何web接入的请求,可以随便找一个空闲的应用服务器丢过去。

典型的,可以把用户相关的信息保存到专门的状态服务器中,请求时仅仅通过cookie提交一个令牌,用令牌从状态服务器获得对应用户的信息。

状态服务器,有一个天生的优势,就是各个用户数据之间往往隔离的,当单台服务器抗不住时,简单增加状态服务器,而在应用服务器上简单根据用户id或令牌,计算出对应的状态服务器即可。

状态服务器可以充分利用各种NoSQL服务,比如典型的redis,这里系统设计与业务需求就要综合考虑了。

A 如果偶尔丢失状态,可以接受,redis即可配置成不持久化。此时效率最高。
万一状态服务器挂了,应用端,简单该向请求到新状态服务器,用户重新登录即可,不会中断太长服务。

B 如果希望尽量少的丢失状态,那么redis就应该定期持久化,并做复制。
这样某台状态服务器挂了,可以根据情况,选择恢复主服务器,中断一小段时间,数据最完整,或备用状态服务器接管,快速恢复服务,数据可能多丢失一点点。
大部分用户完全感知不到,个别用户重新登录即可。业务中断也很小。

如果有可能,做成自动切换,会更高效率,但是一定要注意,不是什么都自动的好,如果没有积累,你写的程序,往往无法考虑大量意外,在“正常”的异常情况下可能切换很好,但是更多的异常情况下,有可能更糟糕。

C 如果要求绝对不能丢失状态,允许中断服务一段时间,redis可以配置成每次都持久化。持久化的硬盘也做冗余甚至做成共享存储。
如果出问题,因为数据都没有丢失,系统重启或更换其他硬件,所有状态即可恢复。但是显然此时redis处理能力将大大减弱。

D 如果要求绝对不能丢失状态,尽量减少中断服务,
redis可以适当自己改造,将数据成功推送到备份服务器上,且备份服务器持久化之后,再返回成功。那么理论上,当主服务器挂掉,可以立刻切换到备份服务器上,而不丢失数据,用户也基本感知不到变化。当然如何判断主的挂掉,这里还是有大量陷阱,处理不好,反而更多的中断服务。

如果去问一个不太了解技术的产品,这个用户状态到底什么要求,他很可能认为D是理所当然的,因为他完全没有成本意识。
实际上对于大部分系统,A,B都是更佳的选择,无论对于开发者,运维者,还是最终用户。

A,B简单,因此可靠,不容易出其他问题,开发成本低,运维成本低,最终更稳定,导致最终用户更满意。

只有用户量足够多,技术团队,运维团队足够强,有能力实现更好的D,这时候D才是对产品来说更好的方案。

3.应用的拆分
到2,实际上解决了绝大多数系统的并发问题,因为可以简单的通过增加硬件来扩容。
但是当应用原来越复杂之后,让一个应用服务器包含所有的服务,是很困难的,从运维成本上考虑,必然有很大浪费,因为每个服务使用频率不一样。
一些,不太常用的服务,可能全部集中到两台服务器上就足够了,这样从其他大量服务器上将这些服务去掉,相当于整个系统节省大量内存。

而从开发角度,也必然是10个小项目每个10人维护,比100人维护一个大项目更容易,因此从开发角度也要求拆分应用。

拆分应用,虽然有些技术辅助手段,但实际主要还是依赖业务本身的分析,技术仅仅是辅助,比如解决各个应用系统之间数据同步问题,跨系统RPC问题等等。

而流行的SOA,则会努力将服务拆成更小而独立的服务,一方面提高功能复用,另一方面便于开发维护,也方便运维管理。

但是这里有一个误区,认为服务拆分得越小越好,其实,从理论上,服务怎么拆分,传统的面向对象已经指明了方向,低耦合,高内聚,
说直白些,就是要善于封装,面向对象从来不是类越多,方法越多越好。

所谓SOA治理,关键还是业务梳理!

4.持久化问题
如果说上面各种问题,目前技术都可以给出比较理想的解决方案,那么持久化瓶颈问题,却始终是最大的问题所在。
前端接入服务器,应用服务器,都可以很好的横向扩展,而状态服务器,也因为其特殊性(持久化可以不太严格,数据之间无关联),也可以比较好的横向扩展。

而典型的联机交易系统,总是要求数据一致性的,没有产品会说偶尔用户账户上少1分钱,是可以接受的。

传统的银行系统数据库系统,会选择更快的专有共享存储,多机互备,将数据实实在在的写到可靠存储里,存储通过raid方式来保证冗余防止单硬盘故障,
当主机故障时,无论软硬件,简单将共享存储挂到备机上,来完成切换。

从安全角度,这种方案是非常高的,但是有两个问题,一是价格昂贵,二是系统容量还是有限。

这种方案,其实就是目前大多数云平台提供给中小系统的解决方案,只是用云存储代替了传统昂贵的共享存储。
如果你的系统可以用云平台的数据库系统或者强劲单机来提供,其实这就是一个合理的好方案。无论你是租用云主机,还是自己弄个物理机。

但是如果系统规模足够大,云平台提供的单机无法满足,或者其云数据库也无法满足(或者仅仅是因为价格太高),那么还是要想办法自己来实现。

首先,存在这样一个大数据库类似传统单机数据库那样,可以无限横向扩展吗?
在我看来是不存在的,因为传统单机数据库,重要的就是提供严格的事务支持,多机分布式事务,要想简单横向扩展,至少目前看来是很困难的。

CAP原则指出在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。
而三者如何权衡,还是要依赖业务!而不是技术。

当然目前因为技术问题P是无法避免的,所以问题往往简化成CA的选择。

对于电商系统,从业务角度,A往往不能舍弃,而C则可以根据业务设计成最终一致性。

比如,购买业务,如果用户账户钱一扣,他的订单就应该是支付成功,单机上这点通过事务很容易实现,但是对于分布式系统,如果做这样的要求,用户未必高兴,因为假设订单系统出了些问题,订单无法更新状态,那么此时只能将用户成功的支付也取消掉!

对于一个庞大的分布式系统,要保证每个系统都正常是不可能的,所以即便放弃A,允许任何一系统故障,整个系统暂停以保证数据一致性,从实际角度看也是不现实的。更不要说,参与系统越多分布协调成本越高,最终可能是即便所有系统都正常,其处理速度也完全无法接受了。

反之,如果支付成功了,允许订单状态暂时没变,就可以等待订单系统恢复后,再将其状态修改为成功。通过业务设计,避免分布式事务,不光得到最大限度的A,也提高了整个系统的处理能力。 具体细节可参看《多数据源之间不使用分布式事务实现异步最终一致性

用户会不会容忍这个短暂的数据不一致呢?钱付了,订单状态却没立刻改。

会的,其实现实中他们经常这样,以前去汇款时,自己的钱立刻交了出去,但是对方却没立刻收到,过几天后,对方才收到,或者对方不在了,汇款再退回。

当然另外一些最终一致却不会被接纳,比如,如果存在一段时间,订单先支付成功,用户余额却没扣。

此时用户即可用没扣的余额去购买其他商品,这是系统巨大的漏洞。

所以一定是要和业务结合才能解决现实问题,也因此,在持久层这里,不要期望一个完整的分布式强数据一致性的系统,而应该学习现实世界的实现方式,分析业务,找到可以接受的最终数据一致性方案。

只要能接受最终数据一致性方案,那么就可以将存储系统拆分成多个相对独立的子系统,每个子系统内都是传统的强一致性严格事务实现,而各个系统之间则通过相对松散的消息机制来互动。任何一个子系统都可以故障,操作可以在其他系统被cache,要么随后恢复,要么超时取消。

其实很典型的例子就是第三方支付系统,每个系统都会对接,显然这是两个完全独立的系统,甚至所有者也不同,但是对于支付业务的确很好的实现了,是一个真正意义上的分布式系统。

总结

对于一个电商平台,可以大致描述一下其基础架构,
1.一个接入的web集群,用来接入链接
2.一个状态保存系统
3.拆分成多个相对独立的应用系统,每个系统都无状态,可横向扩展。
4.根据应用和数据规模,将数据拆分成多个相对对立的存储系统
可以是类似:用户系统,订单系统,支付系统这样按功能拆分,
也可以再拆分成:用户1系统,用户2系统…这些系统功能完全一致,只是保存了不同用户群的数据

其中每个存储系统自己负责容错,不同存储系统通过应用系统通过内部消息来衔接,总是假定其他系统可能故障,也就必须考虑恢复或冲正。

这一切,尤其是3和4显然更多依赖于业务的分析与设计,那么从技术角度,最终的系统就是一个完全分布式的系统,可以支持几乎无限的扩展。

而某些技术,热门中间件,框架,其实仅仅是一个技术手段,并没有想象的那么重要。