技术选型:常见技术架构的核心原理
前面说到,在技术选型时,首先要考虑业务,其次是团队,最后才是技术。技术因素排在最后,并不是说它不重要,而是因为技术是一个必要条件,可以放到最后考虑。毕竟是在做技术选型,而不是其他。作为架构师,除了要不断地学习和尝试新技术,掌握已有的技术架构的核心原理也是一项必备技能。换句话说,目前的软件系统都面临性能、高可用、可扩展、安全等方面的条件,你已有的技术储备,能否解决这些问题?如果能,那么恭喜你;反之,则需要努力。为应对每项挑战,你至少得储备一到两种技术解决方案。
分布式缓存架构
从客户端到应用最底端的存储甚至是操作系统,影响用户请求的所有环节都可以进行性能优化,因为我们可以通过各种技术手段来提升系统性能:在客户端可以缓存、合理的布局、减少请求报文大小等方法来改善性能;也可以使用CDN将静态资源放到离用户最近的网络服务商机房;在应用机房还可以部署反向代理服务器来缓存热点请求或者文件,加快响应速度;应用端可以使用本地和分布式缓存,来加快请求处理速度,减轻数据库负载。
缓存作为最重要的性能优化手段,在整个请求链路上,不同的位置都使用缓存,它们得到的收益大致如下图所示。
加上图中虚线框的部分,表示多层反向代理,一般的中小应用会省略这部分,反向代理服务器直接代理后端的应用层服务器,它也同时承担着负载均衡和HTTP缓存服务的职责。
这里着重介绍应用层的分布式缓存架构,分布式缓存指缓存服务部署在多个服务器组成的集群中,对外提供缓存服务,其架构大致有两种形式,一种以Redis Cluster为代表,集群中的节点互相通讯的分布式缓存;一种以Memcached为代表,集群中的节点不互相通信的分布式缓存。
Memcached集群中的各个节点不互相通信,客户端通过一致性Hash等路由算法选择节点进行数据访问。而Redis官方集群方案的节点间采用gossip协议通信,可以实现去中心化,但客户端需要缓存节点的路由信息,且slave节点仅用于数据备份(可通过设置接收读请求),物理资源的利用率更低。而作为数据存储本身,两者的功能差异这里就不再作过多介绍,可根据实际业务场景来选择,下面对缓存架构设计中最重要的几个问题作简要分析。
缓存穿透
缓存穿透是指缓存并没有起作用,即业务系统去缓存中查询数据,但缓存中没有,其只能再次去存储系统查询数据。一般造成这种情况的原因有两点:
待访问的数据在主存储系统中也不存在
生成缓存数据需要消耗大量时间或资源
前面这种情况,解决方案很简单,直接设置一个带失效时间的默认值到缓存即可;后面一种情况的解决方案其实跟缓存雪崩类似。
缓存雪崩
在高并发系统下,当缓存过期被清除且新缓存还未生成时,即接收到大量请求,这些请求都被转发到主存储器,对存储系统造成巨大的性能压力,这些压力又会拖慢整个系统,进而引发一系列连锁反应,甚至造成系统崩溃。
存储雪崩的解决方案挺多的,比如:
更新锁机制:对缓存更新操作进行加锁保护,保证只有一个线程能够进行缓存更新,未能获取更新锁的线程要么等待,要么就返回空值或者默认值。
后台更新:缓存本身的有效期会永久,后台线程定时去更新。
双key策略:同一份缓存设置两个key,其中key1有过期时间,key2没有过期时间,每次缓存先读取key1,如果key1读不到则去读key2,然后触发一个事件去同时更新key1和key2。
缓存热点
虽然缓存系统本身的性能比较高,但对于一些特别热点的数据,如果大部分甚至所有的业务请求都命中同一份缓存数据,则这份数据所在的缓存服务器的压力也很大。例如,某明星微博发布“我们”来宣告恋爱了,短时间内上千万的用户都会来围观。
缓存热点的解决方案就是复制多份缓存副本,将请求分散到多个缓存服务器上,减轻缓存热点导致的单台缓存服务器压力。以微博为例,对于粉丝数超过 100 万的明星,每条微博都可以生成 100 份缓存,缓存的数据是一样的,通过在缓存的 key 里面加上编号进行区分,每次读缓存时都随机读取其中某份缓存。
缓存预热
如果缓存中存放是热点数据,而热点数据又是缓存系统利用LRU算法对不断访问的数据筛选淘汰出来的,这个过程就需要花费较长的时间,在这段时间内,系统的性能和负载都不太好,那么最好在缓存系统启动的时候就把热点数据加载好,这个缓存预加载的手段就叫做缓存预热。
对于一些元数据,比如配置信息、城市列表、类目信息等,都可以在启动时加载数据库中的全部数据到缓存进行预热。
小结
不管是本地缓存还是分布式缓存,一定要弄清楚哪些数据是真正需要缓存的,既然允许缓存,就需要接受在一定时间内的数据不一致。
分布式消息队列架构
消息队列可能是分布式系统的标配组件了,它的应用场景也挺多的,比如我们所熟知的:
异步处理:应用仅处理主要的业务流程,而非必须的业务逻辑则通过消费MQ消息的方式来异步处理;如果主业务流程包含一些耗时的操作,比如调用第三方接口,也可以使用此方法。
应用解耦:业务相互关联的系统可能存在着非常复杂的调用关系,对系统的可扩展性以及性能等都有较大的影响,这时可以使用消息队列来处理复杂的调用关系,以达到服务间的解耦。
流量削峰:将短时间的高并发请求写入消息队列,而后业务系统根据自己的处理能力,逐条消费,以降低瞬时的大流量对系统的冲击。
还有诸如日志处理、消息通讯等等应用场景。
市面上的消息队列也挺多的,比较知名的有ActiveMQ、RabbitMQ、RocketMQ、Kafka等等;也有挺多公司根据自己的业务需求,定制了自己的消息队列,比如去哪儿的QMQ、腾讯的TubeMQ等等。这么多的消息队列,各有各的侧重点,那我们在引入消息队列时应当考虑哪些因素?又如何选择适合自己业务场景的消息队列?
消息队列的选型要点
衡量一款消息中间件是否符合自己需求需要从多个维度考虑,首先肯定是功能,看看是否符合自己的业务场景,是否能达到开箱即用等等;其次是性能,如果消息队列吞吐量达不到要求就肯定不能选择;然后是可靠性,如果消息无故丢失,任谁也不能忍受吧;最后看运维,是否可以很方便的进行部署、扩容、升降级、故障处理,都是需要考虑的因素。接下来稍微展开一点,更详细的内容可以阅读参考资料相关内容。
首先是功能,不同的消息队列,所支持的功能并不相同,除了最基本的FIFO队列,有的消息队列还支持优先级队列、延迟队列、重试队列、事务消息、强顺序消息等等;消费模式也有推(push)模式和拉(pull)模式;消息传递方式也分为点对点(P2P,即一条消息只能被一个消费者消费) 和发布/订阅模式(Pub/Sub),在选择时,需要根据业务场景,尽量选择功能丰富的消息队列。
其次是性能,消息队列的主要性能指标是吞吐量。吞吐量指单位时间内传递消息的数量,一般来说,吞吐量越大越好。但吞吐量受到硬件(内存、磁盘、网卡)和功能的限制,硬件比较好理解,就拿网卡来说,带宽越大肯定传输越快,吞吐量越大。而为什么还受到功能的限制呢?有些功能比如:事务消息、顺序消息会严重制约消息队列的性能,比如Kafka在开启幂等和事务功能时,其性能会下降。因此,在考虑性能业务时,不能只看数字,还要考虑硬件和功能因素。
其次是可靠性,一般是从消息丢失的保障层面来考虑,除了正常情况下,消息不能丢失以外,还要考虑硬件出现故障时,如何保障消息不丢失?这方面,没有太好的办法,只能根据社区的反馈以及测试来综合考量。
最后是运维,对于RabbitMQ、Kafka、RocketMQ等著名的MQ,官方和社区都提供了大量的插件和系统的软件产品从各个方面来对MQ进行监控、管理以及自动化运维。应该说,几大主流的MQ在运维层面都提供了很好的支持,选哪个都可以。
除此之外,像社区、与团队技术栈的贴合情况等都是大家考虑的因素。
常见问题
在选出合适的消息队列以后就好了吗?当然不是,作为架构师,还得对消息队列可能出现的问题以及解决方案了然于胸,这方面就得靠我们平时多积累多学习,下面介绍两个简单的例子,权当抛砖引玉了。
消息堆积可能是最常见的问题,出现消息堆积无外乎两方面的因素,要么是流量暴涨,导致生产端产生大量消息;要么是消费端出现故障,整体的消费能力减弱。当然解决方案也只能从这两个方面入手,可以考虑在生产端进行限流,但很多时候,消息队列本来就是用来缓解前端压力的,这时候只能从消费端解决。
如果消费端还有增加服务的可能,那么最简单的方法就是增加消费者数量。如果消费能力已经达到上限,比如业务数据库只支持这么大的写入并发,又或者像Kafka和RocketMQ这类消息队列,它们的存储模型是基于partition模型的,每个subject分为一个或多个partition,而消费者又是与partition对应的,没办法拓展消费者数量,这时候可以把一部分消息扔到另外一个MQ中,后面再慢慢消费,优先恢复上游的业务。
客户端一致性也是一个很常见的问题,比如订单支付成功后,需要发送一条消息,这两个操作是需要同时执行成功的。这时候我们可以把发送消息简化成向数据库中插入一条消息,把这个操作和订单支付放到同一个数据库事务中。事务提交后注册回调,提交一个补偿任务从数据库中读出消息并发送,如果发送成功,就把消息状态置为发送成功,甚至消费端在消费成功后,也可以来更新这个消息的状态,达到发送-消费的最终一致性。
小结
消息队列的选择一定要根据自身的业务场景对症下药,不要觉得哪个火就用哪个。市面上,也没有完美的消息队列,有时候选择功能,就需要在性能层面作出牺牲;也没有绝对可靠的消息队列,所以在业务设计上就得有对应补偿方案,哪怕是人工处理也行,最怕的就是陷入完美陷阱,什么都想要。
负载均衡架构
负载均衡的概念不用做过多解释,只需要注意一点:负载均衡的目的并不全是为了让集群中的节点的负载达到均衡,虽然大多数时候是这样的。有时候也会基于业务和性能考虑。比如,我们会让所有的定时任务都集中到一个节点,以避免任务重复执行;有时候,也会把消耗资源更多的请求,转发到硬件配置更好的机器上。
掌握负载均衡主要从两方面入手:分类和算法,即理解不同分类和算法的优缺点以及使用场景。
负载均衡分类
常见的负载均衡系统包括 3 种:
DNS 负载均衡:DNS 解析同一个域名可以返回不同的 IP 地址,以实现地理级别的均衡
硬件负载均衡:通过单独的硬件设备来实现负载均衡功能
软件负载均衡:通过负载均衡软件来实现负载均衡功能
DNS负载的优点是简单,成本低,无需自己开发和维护,而且是就近访问,对访问速度的提升效果非常明显;缺点就是控制器在域名商那儿,没办法定制和扩展,且分配的策略很简单,不能区分服务器差异和状态,更重要的是DNS缓存时间一般都较长,修改配置后,不会马上生效。
目前业界流行的硬件负载均衡设备有两种:F5和A10,这类设备功能和性能强大,稳定性高,且支持安全防护,可以说是哪儿哪儿都好,但就是价格贵,买不起。
软件负载均衡最常见的就是Nginx和LVS,其中Nginx是7层负载均衡,而LVS是4层负载均衡。4层和7层的区别主要在于协议和灵活性,比如Nginx属于HTTP协议层面的负载均衡,当然也支持E-mail等协议,而LVS则是数据链路层的负载均衡,支持所有的网络通讯协议。所以LVS也是目前大型网站使用最广的一种负载均衡手段。
相比于硬件负载,软件负载在性能和功能上都弱很多,但胜在简单、灵活,像Nginx也提供很多插件来实现定制化。
负载均衡算法
负载均衡算法数量较多,而且可以根据一些业务特性进行定制开发,抛开细节上的差异,根据算法期望达到的目的,大体上可以分为下面几类:
任务平分类:将任务或请求平均分配给服务器
负载均衡类:根据系统负载来分配任务
性能最优类:根据服务器响应时间来分配
Hash 类:根据任务关键信息的hash值来决定分配给哪台服务器
其中,属于任务平分类的算法有:轮询或加权轮询,优点就是简单,而缺点就是只是简单,没办法根据服务器状态的差异来分配任务。
负载均衡类,需要根据系统的负载去分配任务,这里的负载指标有很多,比如CPU、内存等等,常见的比如,最少连接数优先或者CPU负载最低优先。实现时,需要先统计每台服务器的连接数或者CPU利用率,而且统计时间也是一个问题。因此,负载最低优先算法如果本身没有设计好,或者不适合业务的运行特点,算法本身就可能成为性能的瓶颈,或者引发很多莫名其妙的问题。所以负载均衡类算法虽然效果看起来很美好,但实际上真正应用的场景反而没有轮询那么多。
和负载最低优先类算法类似,性能优先类算法本质上也是感知了服务器的状态,只是通过响应时间这个外部标准来衡量服务器状态而已。
Hash类则是根据某些关键信息的hash值来进行负载,比如来源于同一个IP的请求分配给一台服务器,这适用于存在事务或者会话的业务。
小结
一般小型系统的负载使用Nginx负载即可,算法上一般都是根据自己的业务定制,简单的使用轮询或加权轮询即可。
分布式关系数据库
关系数据库的分布式架构已经非常成熟可靠,基本可以覆盖大部分应用场景,最常用的莫过于读写分离和分库分表。读写分离的本质是将读的压力分散到集群中的多个节点,但是没有分散写压力;而分库分表既可以分散读的压力,也可以分散写的压力。
读写分离
读写分离的基本原理是将数据库读写操作分散到不同的节点上,其实现的主要方式是:
搭建主从集群,这个集群可以是一主一从,也可以是一主多从,甚至是多主多从
主节点负责读写操作,从节点只负责读操作
主节点通过复制将数据同步到从节点,每台服务器都存储了所有的业务数据
应用服务器将写操作发送到主节点,将读操作发送到从节点
读写分离的实现逻辑并不复杂,但有两个细节点需要注意:复制延迟和*分配机制*。
即使不考虑网络因素,数据从主节点复制到从节点都有延迟,只是看延迟大小能否在可承受范围内,这一点在数据库集群中不可避免,解决方案只能从业务层入手,常见的几种方法是:
写操作后的读操作发给对应的主节点:如果写入数据后,需要立即读取,这时候可以直接把读操作发送给刚才写入的节点
从节点读取失败后再读取主节点:如果从节点读取失败后,即表示数据还未同步到从节点,这时可以从主节点再读取一次
关键业务读写操作全部读主节点,非关键业务采用读写分离:对于某些不能接受延迟的业务,可以直接限制读写操作均从主节点
而将读写区分开的实现方式有两种:硬编码封装和使用中间件。
硬编码即在业务层抽象一个数据访问层,实现读写操作分离和数据库连接管理。如果使用Spring或SpringBoot等框架,可以非常容易地配置出多个不同的数据源,然后编写一个拦截器拦截所有数据库读写请求,不同的请求使用不同的数据源。这种方式实现简单,定制化强,但数据库集群发送故障的时候,或者主从切换时,可能需要修改配置并重启。当然,也可以在代码里实现这部分逻辑,只不过更复杂一点。
另外一种方式就是用中间件,中间件的功能更强大,使用起来更方便。对业务层来说,访问中间件与访问数据库没有区别,因此,一个功能完整的中间件,至少具有以下特点:
完全兼容SQL语法和数据库服务协议
中间件虽然不执行真正的读写操作,但由于所有请求都需要经过中间件,所以中间件需要具备高性能
数据库的主从切换或者故障恢复对业务服务器无感知,中间件必须可以探测数据库服务器的状态
显然数据库中间件的复杂程度要比硬编码高出N个数量级,一般来说,建议硬编码实现,要求较高的场景,建议使用中间件。
分库分表
单台服务器的容量总有上限,但数据达到一定量级,单台数据库服务器的存储能力就会成为瓶颈,这时候就需要对数据进行分片操作,即我们常说的分库分表。
分库是基于业务,即不同业务的数据分散存储在不同的服务器上;分表则是为解决达标数据量限制问题,而根据一定的规则将一张表拆分成多张表,这多张表还是在同一个DB中。
分库分表存在的问题,都是老生常谈了,比如:join问题、事务问题、SQL路由配置、分页、Count以及排序问题等等。解决方案也多得不行,这里就不再赘述。
小结
读写分离和分库分表会对整体的系统架构带来较高的复杂度,因此,建议在经过如下操作后再尝试:
硬件优化:首先检查硬件配置可否升级,比如更换更好的磁盘、升级内存和CPU等
数据库调优:索引是否合理、SQL优化、Buffer等配置优化
业务优化:代码层面调整不合理的逻辑,减少不必要的查询,甚至是拆分大的SQL
使用缓存
使用前面4种方案后,还是不能满足性能要求,在考虑读写分离和分库分表
NoSQL数据库架构
随着互联网的发展,越来越多的数据呈现非结构化的特点,因此传统的关系数据库在很多场景下都显得力不从心,比如关系数据库存储的是行记录,无法存储数据结构,当然,新版本的MySQL增加了JSON数据类型,能够解决部分需求;关系数据库扩展schema很不方便,特别是大数据量时,稍不注意就会把服务器搞死;关系数据库的数据分析能力稍弱,特别是在数据量较多的场景下进行数据分析,I/O会飙到很高;关系数据库没办法做全文搜索。针对这些问题,诞生了不同的NoSQL解决方案,常见的有以下四类:
K-V存储:解决关系数据库无法存储数据结构的问题,以 Redis 为代表
文档数据库:解决关系数据库强 schema 约束的问题,以 MongoDB 为代表
列式数据库:解决关系数据库大数据场景下的 I/O 问题,以 HBase 为代表
全文搜索引擎:解决关系数据库的全文搜索性能问题,以 Elasticsearch 为代表
通过上面的描述,基本上也就知道什么场景使用什么样的数据库,可能大家对Redis都非常熟悉,其应用场景以及架构方案大家都比较熟悉,这里就不说啦。由于我们的大部分业务都在MongoDB上,算是它的重度用户,这里就以MongoDB为例,谈谈NoSQL数据的架构设计。
MongoDB架构设计
在进入话题之前,我想再次强调两点:
MongoDB支持集群环境下的多文档事务,即使跨数据库也可以
MongoDB是支持join操作的
所以,关系数据库能支撑的业务,MongoDB也可以,然后进入正题。
MongoDB集群支撑两种模式:复制集群和分片集群,你可以简单的以关系数据库的读写分离和分库分表来类比。
复制集又称副本集,其至少需要3个节点,且不管何时,部署复制集需要的节点数都是单数,其大致的架构示意图如下图中左图:
当主节点发生故障不可用时,整个集群会自动在剩下的节点中重新选择主节点,然后继续对外提供服务。而为了保证数据安全,MongoDB提供一种被称为Write Concern
的写入机制,其大致原理如下图所示:
当WriteConcern=majority
时,写操作需要被复制到大多数节点后才会返回成功,比如这里3个节点,至少需要写入两个节点才会返回成功。
MongoDB也是通过日志来实现上述功能的,比如MongoDB的oplog
主要用于主从复制,类似于MySQL的binlog
,客户端将数据写入Primary
,Primary
写入数据后记录一条oplog
,Secondary
从Primary
( 或其它Secondary
) 拉去oplog
并重放,来确保复制集里每个节点存储相同的数据。除此之外,写数据时还会写journal日志
,类似于MySQL的redo日志
,是存储引擎层面的操作日志,用于保证数据的一致性。
而对于读操作,也可以通过ReadConcern
来设置隔离级别,还可以通过ReadPreference
来设置从哪儿读,比如其可取值包含:
primary:选择主节点读取数据
primaryPreferred:优先选择主节点,如果主节点不可用则选择从节点读数据
secondary:选择从节点读取数据
secondaryPreferred:优选选择从节点,如果从节点不可用则选择主节点读取数据
相较于副本集,分片集群的架构则更复杂,涉及到的细节也更多,但不管集群内部实现多复杂,对上层业务来说都是透明的。而且从运维的角度看,其故障可以自动恢复,扩容也仅需要修改配置即可,况且还有各种自动化的运维工具,其入门的门槛之低,也算少有了。
小结
这里仅对MongoDB数据库做简要介绍,希望打破大家对MongoDB等数据库的固有印象,多多了解它们的新特性,说不定可以帮助你更好的解决复杂的业务难题。在做架构设计时,关于数据库的选择,不要局限在MySQL,也多看看其他数据库。
分布式锁架构
分布式锁的实现方式有很多,Redis、Zookeeper,甚至是MySQL都可以,其实现的关键是如何保证集群中各个副本数据之间的一致性。比如,使用Redis集群来实现分布式锁,多个客户端连接不同的Redis节点,当其中一个客户端获得锁时,如何把数据同步到其他Redis节点上,保证集群内所有节点的数据一致性?如果不能做到这一点,那么这个分布式锁是存在问题的。因此,使用Redis实现的分布式锁并不保证可靠性,它主要是胜在性能。当然,如果是单机环境下的Redis分布式锁或者使用分布式锁时固定连接集群中的某个节点是没有太大问题的。
在分布式环境下要保证高可靠性的分布式锁,Zookeeper的实现方案会更好一些。这个小节主要介绍ZK是如何保证集群内的数据一致性的。
Zookeeper集群中的所有节点均可对外提供服务,集群中的节点分为主从两种,其中所有节点均可处理客户端的读请求,但只有主节点才能处理写操作,其大致的架构如下图所示。
ZooKeeper 客户端会随机连接到 ZooKeeper 集群中的一个节点,如果是读请求,就直接从当前节点中读取数据;如果是写请求,那么该节点就会向 Leader 转发请求,Leader 接收到读写事务后,会将事务转换为一个事务提案(Proposal),并向集群广播该提案,只要超过半数节点写入数据成功,Leader 会再次向集群广播 Commit 消息,将该提案提交。
Zab协议
Zab 协议是为了解决分布式一致性而设计出的一种协议,它的全称是 Zookeeper 原子广播协议,它能够在发生崩溃时快速恢复服务,达到高可用性。
ZooKeeper 的消息广播过程类似于两阶段提交,针对客户端的读写事务请求,Leader 会生成对应的事务提案,并为其分配 ZXID,随后将这条提案广播给集群中的其它节点。Follower 节点接收到该事务提案后,会先将其以事务日志的形式写入本地磁盘中,写入成功后会给 Leader 反馈一条 Ack 响应。当 Leader 收到超过半数 Follower 节点的 Ack 响应后,会回复客户端写操作成功,并向集群发送 Commit 消息,将该事务提交。Follower 服务器接收到 Commit 消息后,会完成事务的提交,将数据应用到数据副本中。
崩溃恢复
Zab 协议支持崩溃恢复,即当 Leader 节点出现崩溃中止,Follower 无法连接到 Leader 时,Follower 会切换为 LOOKING 状态,发起新一轮的选举。除此之外,如果 Leader 节点无法与过半的服务器正常通信,Leader 节点也会主动切换为 LOOKING 状态,将领导权让位于网络环境较好的节点。
为了维护集群的数据一致性,Zab 协议需要在崩溃恢复的过程中保证下面两个特性:
Zab 协议需要确保已经在 Leader 服务器上提交的事务最终被所有服务器提交;
Zab 协议需要确保丢弃那些只在 Leader 上提出但没有被提交的事务。
对于第一条,本来Leader应当向所有的Follower节点回复Commit消息,但只回复了一半,就崩溃了,这时候可能只有1个Follower节点收到了Commit消息,这时候,一定要保证收到Commit消息的Follower节点称为新的Leader节点,这样才能保证已经提交的事务再次提交到其它节点上。
对于第二条,Leader 节点可能是在等待半数 Follower 的 Ack 响应期间崩溃,也可能是在发送 Commit 消息之前崩溃,甚至可能是在广播事务提案的过程中崩溃。但无论是何种情况,可以确定的是集群中没有任何一个 Follower 将数据提交。因此当一个新 Leader 产生时,它会抛弃记录在事务日志但没有提交的事务。
小节
这里只是对Zab协议做了简单的介绍,协议其实还包含Leader选举、数据恢复等内容,通过这里的简单介绍,可以大致了解Zab协议是如何保证集群间的数据一致性的。Zookeeper集群发生节点分区现象时,都是牺牲了短暂的可用性来保证一致性的,也就是说,当节点出现故障,重新选举并恢复的过程中,整个集群是不可用的。
如果想了解关于Zookeeper的更多内容,可以阅读参考资料4和5,两篇文章对Zab协议以及ZK如何实现Zab协议都做了详细的介绍。
最后
本文是根据这两周的课程内容,对自己的知识结构作了简单的梳理。像分布式关系数据库架构,我自己本身并没有太多经验,所以全文参考了很多资料,其中缓存、数据库、负载均衡的相关内容主要参考极客时间专栏 从0开始学架构;消息队列的相关内容则参考了参考资料中的1~3;Zookeeper相关内容主要参考下面的4和5;MongoDB的相关内容则来自于以前学习课程 [MongoDB高手课](http://gk.link/a/10jM2) 的学习笔记,在此表示感谢。
极客大学架构师训练营第六周学习总结
封面图:Kuma Kum
参考资料
版权声明: 本文为 InfoQ 作者【NORTH】的原创文章。
原文链接:【http://xie.infoq.cn/article/4bdd43573cb3234bb8308aacb】。未经作者许可,禁止转载。
评论 (1 条评论)