分布式优缺点
分分布式系统的核心是可扩展性,通过对服务、存储的扩展,来提高系统的处理能力,通过对多台服务器协同工作,来完成单台服务器无法处理的任务,尤其是高并发或者大数据量的任务。
我们不仅仅要看到分布式所带来的巨大优势,还应看到分布式所带来的挑战,并在实际应用中尽量去避免去克服这些问题。
1. 分布式优点
1.1 增大系统容量。我们的业务量越来越大,而要能应对越来越大的业务量,一台机器的性能已经无法满足了,我们需要多台机器才能应对大规模的应用场景。所以,我们需要垂直或是水平拆分业务系统,让其变成一个分布式的架构。
1.2 加强系统可用。我们的业务越来越关键,需要提高整个系统架构的可用性,这就意味着架构中不能存在单点故障。这样,整个系统不会因为一台机器出故障而导致整体不可用。所以,需要通过分布式架构来冗余系统以消除单点故障,从而提高系统的可用性。
1.3 因为模块化,所以系统模块重用度更高。
1.4 因为软件服务模块被拆分,开发和发布速度可以并行而变得更快。
1.5 系统扩展性更高。
1.6 团队协作流程也会得到改善。
1.7 然后来对比一下单体应用和分布式架构的优缺点:
2. 分布式缺点
2.1 架构设计变得复杂(尤其是其中的分布式事务)。
2.2 部署单个服务会比较快,但是如果一次部署需要多个服务,部署会变得复杂。
2.3 系统的吞吐量会变大,但是响应时间会变长。
2.4 运维复杂度会因为服务变多而变得很复杂。
2.5 架构复杂导致学习曲线变大。
2.6 测试和查错的复杂度增大。
2.7 技术可以很多样,这会带来维护和运维的复杂度。
2.8 管理分布式系统中的服务和调度变得困难和复杂。
3. 分布式面临的问题
(1) 当服务越来越多时,服务URL配置管理变得非常困难,F5硬件负载均衡器的单点压力也越来越大。
(2) 当进一步发展,服务间依赖关系变得错踪复杂,甚至分不清哪个应用要在哪个应用之前启动,架构师都不能完整的描述应用的架构关系。
(3) 接着,服务的调用量越来越大,服务的容量问题就暴露出来,这个服务需要多少机器支撑?什么时候该加机器?
(4) 服务多了,沟通成本也开始上升,调某个服务失败该找谁?服务的参数都有什么约定?
(5) 一个服务有多个业务消费者,如何确保服务质量?
(6) 随着服务的不停升级,总有些意想不到的事发生,比如cache写错了导致内存溢出,故障不可避免,每次核心服务一挂,影响一大片,人心慌慌,如何控制故障的影响面?服务是否可以功能降级?或者资源劣化?
4. 分布式带来问题
4.1 分布式事务
这是一个老生常谈的问题,我们都知道事务就是一些列操作的原子性保证,在单机的情况下,我们能够依靠本机的数据库连接和组件轻易做到事务的控制,但是分布式情况下,业务原子性操作很可能是跨服务的,这样就导致了分布式事务,例如A和B操作分别是不同服务下的同一个事务操作内的操作,A调用B,A如果可以清楚的知道B是否成功提交从而控制自身的提交还是回滚操作,但是在分布式系统中调用会出现一个新状态就是超时,就是A无法知道B是成功还是失败,这个时候A是提交本地事务还是回滚呢?其实这是一个很难的问题,如果强行保证事务一致性,可以采取分布式锁,但是那样会增加系统复杂度而且会增大系统的开销,而且事务跨越的服务越多,消耗的资源越大,性能越低,所以最好的解决方案就是避免分布式事务。
还有一种解决方案就是重试机制,但是重试如果不是查询接口,必然涉及到数据库的变更,如果第一次调用成功但是没返回成功结果,那调用方第二次调用对调用方来说依然是重试,但是对于被调用方来说是重复调用,例如A向B转账,A-100,B + 100,这样会导致A扣了100,而B增加200。这样的结果不是我们期望的,因此需在要写入的接口做幂等设计。多次调用和单次调用是一样的效果。
通常可以设置一个唯一键,在写入的时候查询是否已经存在,避免重复写入。但是幂等设计的一个前提就是服务是高可用,否则无论怎么重试都不能调用返回一个明确的结果调用方会一直等待,虽然可以限制重试的次数,但是这已经进入了异常状态了,甚至到了极端情况还是需要人肉补偿处理。其实根据CAP和BASE理论,不可能在高可用分布式情况下做到一致性,一般都是最终一致性保证。
4.2 负载均衡
每个服务单独部署,为了达到高可用,每个服务至少是两台机器,因为互联网公司一般使用可靠性不是特别高的普通机器,长期运行宕机概率很高,所以两台机器能够大大降低服务不可用的可能性,这正大型项目会采用十几台甚至上百台来部署一个服务,这不仅是保证服务的高可用,更是提升服务的QPS,但是这样又带来一个问题,一个请求过来到底路由到哪台机器?路由算法很多,有DNS路由,如果session在本机,还会根据用户id或则cookie等信息路由到固定的机器,当然现在应用服务器为了扩展的方便都会设计为无状态的,session会保存到专有的session服务器,所以不会涉及到拿不到session问题。
那路由规则是随机获取么?这是一个方法,但是据我所知,实际情况肯定比这个复杂,在一定范围内随机,但是在大的范围也会分为很多个域,例如如果为了保证异地多活的多机房,夸机房调用的开销太大,肯定会优先选择同机房的服务,这个要参考具体的机器分布来考虑。
4.3 服务发现
服务的提供者如何被服务的使用者发现,当然很多情况一个服务既是某些服务的提供者,也是其他服务的使用者,这就需要一个中间的机制来让大家互相感知,例如Zookeeper(简称ZK),我简单介绍下ZK的作用和实现,其他类似于spring boot的ureka和taobao的HSF的配置中心大概都一样的作用,只是不同的技术实现而已。
Zookeeper是一个高可用集群组件,因为它可以在集群之间保持同步和一致,维护统一的一个类似于文件目录的结构。ZK包含服务端和客户端,每个机器维护一个客户端保持与服务端的心跳,客户端可以在整个目录的任何节点设置Watcher感知,这样只要ZK维护的该目录有任何改动,客户端都能收到回调通知。如果这个目录存放的某个服务提供者的ip列表,ZK能够感知该服务的心跳,一旦该机器与ZK失去联系,目录变化,服务使用者感知到请求,这样服务使用者调用的时候就获取不到该服务提供者的ip,这样完成动态的失效转移。当服务恢复,ip又添加回原有ZK维护的节点列表。
ZK采用了Paxos算法保证了ZK之间的一致性,内部的选举机制保证了集群只要有超过半数的机器正常,集群就可用。高可用的ZK成为一个服务的订阅与通知的中心,这样完成服务发现的功能。
4.4 数据库性能与高可用
数据库是重要的部分,因为大部门时候我们需要持久化很多数据完成业务逻辑,但是数据库很难像应用服务器那样做到线性的扩容,尤其是关系数据库,所以现在会引入一些对集群支持比较好的NoSQL去支撑系统对性能的要求,但是NoSQL也有很多局限性,例如没有事务支持,这点在一些对事务敏感的情况下是难以忍受的,还有查询的不方便,很难支持join等操作,所以NoSQL的使用对于场景的判断非常重要,现在Redis和Memcache等更多应用在缓存使用上,核心数据库依然采用关系数据库,以MySQL为代表。
数据库的性能主要是在查询的优化上,项目实战中DBA经常会警告一些常用的SQL并没走索引进行了全表扫描,所以合理使用索引进行高效的查询是很必要的。当然索引也不是建立的越多越好,例如某些重复率很高的字段不适合建索引,大量的索引甚至比数据本身更占用空间。对于数据量多的情况,我们可以采用分表分库的方案进行拆分,分表一般采用关键字段进行取模,尽量让多个表进行均分。负载过高可以采用主从读写分离等等。
高可用的数据库一般采用主从机制,主库挂了之后会自动转移到备库,曾经跟DBA讨论过,线上不能引入流量到备库,如果性能不够自动扩容,因为如果线上主库挂掉,瞬间流量落到从库依然会挂,到头一台机器也不能用了。原则主从是为了高可用,而不是为了扩容,扩容操作应该在机器负载较高的时候就能收到警报之后。能够失效转移。现在高可用主要的方案也是冗余,包括异地机房等等,本质上都是冗余。
CAP定理,指的是在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得。分布式系统的最大难点,就是各个节点的状态 ...