分布式系统的常见问题

不好意思最近实在是有点太忙了,将近一个月没更新博客,其实前几天我是有发表一篇关于HSF框架的源码解析,后来由于一些原因不得不删除。其实HSF也跟Dubbo类似,解决了分布式系统中的一系列问题。


分布式带来的优势就是能够将复杂业务拆分成多个服务的组合,就如同controller-service-dao中的service接口部署在不同的机器上,service不就是服务的抽象么。让不同的团队维护不同的service,这样便于精细化开发和维护。当然,服务的独立部署也给动态扩容带来的了可能性,因为不同服务的负载是不同的,所以我们能够给每个服务配置不同的资源,达到最大限度的利用资源,例如计算密集型的机器配置强大的CPU,IO密集的配置强大的网卡等等。我们可以根据不同的服务,做很多个性化的处理,例如在交易模块后面构建大数据平台进行数据挖掘,训练模型能够个性化的推荐给不同的用户可能购买的商品提升购买率。


当然分布式也会带来很多问题

  1. 分布式事务:
    这是一个老生常谈的问题,我们都知道事务就是一些列操作的原子性保证,在单机的情况下,我们能够依靠本机的数据库连接和组件轻易做到事务的控制,但是分布式情况下,业务原子性操作很可能是跨服务的,这样就导致了分布式事务,例如A和B操作分别是不同服务下的同一个事务操作内的操作,A调用B,A如果可以清楚的知道B是否成功提交从而控制自身的提交还是回滚操作,但是在分布式系统中调用会出现一个新状态就是超时,就是A无法知道B是成功还是失败,这个时候A是提交本地事务还是回滚呢?其实这是一个很难的问题,如果强行保证事务一致性,可以采取分布式锁,但是那样会增加系统复杂度而且会增大系统的开销,而且事务跨越的服务越多,消耗的资源越大,性能越低,所以最好的解决方案就是避免分布式事务。
    还有一种解决方案就是重试机制,但是重试如果不是查询接口,必然涉及到数据库的变更,如果第一次调用成功但是没返回成功结果,那调用方第二次调用对调用方来说依然是重试,但是对于被调用方来说是重复调用,例如A向B转账,A-100,B + 100,这样会导致A扣了100,而B增加200。这样的结果不是我们期望的,因此需在要写入的接口做幂等设计。多次调用和单次调用是一样的效果。通常可以设置一个唯一键,在写入的时候查询是否已经存在,避免重复写入。但是幂等设计的一个前提就是服务是高可用,否则无论怎么重试都不能调用返回一个明确的结果调用方会一直等待,虽然可以限制重试的次数,但是这已经进入了异常状态了,甚至到了极端情况还是需要人肉补偿处理。其实根据CAP和BASE理论,不可能在高可用分布式情况下做到一致性,一般都是最终一致性保证。
  2. 负载均衡
    每个服务单独部署,为了达到高可用,每个服务至少是两台机器,因为互联网公司一般使用可靠性不是特别高的普通机器,长期运行宕机概率很高,所以两台机器能够大大降低服务不可用的可能性,这正大型项目会采用十几台甚至上百台来部署一个服务,这不仅是保证服务的高可用,更是提升服务的QPS,但是这样又带来一个问题,一个请求过来到底路由到哪台机器?路由算法很多,有DNS路由,如果session在本机,还会根据用户id或则cookie等信息路由到固定的机器,当然现在应用服务器为了扩展的方便都会设计为无状态的,session会保存到专有的session服务器,所以不会涉及到拿不到session问题。那路由规则是随机获取么?这是一个方法,但是据我所知,实际情况肯定比这个复杂,在一定范围内随机,但是在大的范围也会分为很多个域,例如如果为了保证异地多活的多机房,夸机房调用的开销太大,肯定会优先选择同机房的服务,这个要参考具体的机器分布来考虑。

  3. 服务发现
    服务的提供者如何被服务的使用者发现,当然很多情况一个服务既是某些服务的提供者,也是其他服务的使用者,这就需要一个中间的机制来让大家互相感知,例如Zookeeper(简称ZK),我简单介绍下ZK的作用和实现,其他类似于spring boot的ureka和taobao的HSF的配置中心大概都一样的作用,只是不同的技术实现而已。Zookeeper是一个高可用集群组件,因为它可以在集群之间保持同步和一致,维护统一的一个类似于文件目录的结构。ZK包含服务端和客户端,每个机器维护一个客户端保持与服务端的心跳,客户端可以在整个目录的任何节点设置Watcher感知,这样只要ZK维护的该目录有任何改动,客户端都能收到回调通知。如果这个目录存放的某个服务提供者的ip列表,ZK能够感知该服务的心跳,一旦该机器与ZK失去联系,目录变化,服务使用者感知到请求,这样服务使用者调用的时候就获取不到该服务提供者的ip,这样完成动态的失效转移。当服务恢复,ip又添加回原有ZK维护的节点列表。ZK采用了Paxos算法保证了ZK之间的一致性,内部的选举机制保证了集群只要有超过半数的机器正常,集群就可用。高可用的ZK成为一个服务的订阅与通知的中心,这样完成服务发现的功能。

  4. 数据库性能与高可用
    数据库是重要的部分,因为大部门时候我们需要持久化很多数据完成业务逻辑,但是数据库很难像应用服务器那样做到线性的扩容,尤其是关系数据库,所以现在会引入一些对集群支持比较好的NoSQL去支撑系统对性能的要求,但是NoSQL也有很多局限性,例如没有事务支持,这点在一些对事务敏感的情况下是难以忍受的,还有查询的不方便,很难支持join等操作,所以NoSQL的使用对于场景的判断非常重要,现在Redis和Memcache等更多应用在缓存使用上,核心数据库依然采用关系数据库,以MySQL为代表。数据库的性能主要是在查询的优化上,项目实战中DBA经常会警告一些常用的SQL并没走索引进行了全表扫描,所以合理使用索引进行高效的查询是很必要的。当然索引也不是建立的越多越好,例如某些重复率很高的字段不适合建索引,大量的索引甚至比数据本身更占用空间。对于数据量多的情况,我们可以采用分表分库的方案进行拆分,分表一般采用关键字段进行取模,尽量让多个表进行均分。负载过高可以采用主从读写分离等等。
    高可用的数据库一般采用主从机制,主库挂了之后会自动转移到备库,曾经跟DBA讨论过,线上不能引入流量到备库,如果性能不够自动扩容,因为如果线上主库挂掉,瞬间流量落到从库依然会挂,到头一台机器也不能用了。原则主从是为了高可用,而不是为了扩容,扩容操作应该在机器负载较高的时候就能收到警报之后。能够失效转移。现在高可用主要的方案也是冗余,包括异地机房等等,本质上都是冗余。

猜你喜欢

转载自blog.csdn.net/micro_hz/article/details/78632108