朱晔的互联网架构实践心得S1E7:三十种架构设计模式(上)前端
【下载本文PDF进行阅读】算法
设计模式是前人经过大量的实践总结出来的一些经验总结和最佳实践。在通过多年的软件开发实践以后,回过头来去看23种设计模式你会发现不少平时写代码的套路和OO的套路和设计模式里总结的相似,这也说明了你悟到的东西和别人悟到的同样,通过大量实践总能趋向性得出一些最佳实践的结论。架构设计也是同样,这里结合本身的理解分析一下微软给出的云架构的一些模式。话说微软干这方面的事情真的很厉害,以前翻译过的《微软应用架构指南》写的也很不错。有了模式的好处是,技术人员和技术人员之间的对话能够绝不费力的经过几个模式关键词进行交流,就像如今你们沟通提到职责链模式,若是双方都理解这个模式的意义那么这五个字替代的可能就是半小时的解释。废话很少说,接下去来看一下这些其实已经很熟悉亲切的模式。数据库
进程外的代理服务(以前介绍中间件的时候也提到了,不少框架层面的事情能够以软件框架的形式寄宿在进程内,也能够以独立的代理形式作一个网络中间件)。这里的大使模式意思就是这么一个网络代理进程,用于和远端的服务进行通信,完成下面的工做:后端
因为是独立进程的网络服务,因此这个模式适合于咱们有多语言多框架都须要干一样的事情,那么咱们的框架中客户端部分的不少工做能够移出来放到大使服务中去。固然了,多一层网络调用多一层开销,大使服务的部署也要考虑到性能不必定能够集中部署,这些都是要考虑的问题。设计模式
使用一层防腐层来做为新老系统通信的中间人。这样新系统能够彻底使用新的通信方式和架构方式,老的系统又不用进行特别改造能够暂时保留,等老系统不用以后能够废弃这个反腐层。这种模式适合新老系统迁移的过渡方案,不属于永久使用的架构设计模式。缓存
这个模式说的就是能够有一个外部的配置服务来保存配置信息,在以前第五篇文章介绍中间件的时候我详细说明过配置服务的功能。无论是处于管理运维的角度仍是方便安全的角度,具备配置共享配置外存特色的独立配置服务对于大型的网站来讲必不可少。实现的话有不少开源项目提供了配置服务,见以前个人文章。安全
应用程序若是须要和多个服务交互的话,在中间构建起一个聚合网关层,网关并发发出多个请求给后面的服务,而后汇总数据给到应用程序。这种模式有几个好处:性能优化
固然,使用这种模式须要考虑到网关的负载、高可用、高性能(异步IO)等等。服务器
其实这种模式不只仅用于纯后端服务之间的通信,不少面向前端的API请求都会作一个聚合层,这样前端能够只发一个请求的状况下任意向后端一次性索取多个API的返回,减小网络请求次数提升性能。网络
实现上最简单的方式可使用OpenResty或Nginx实现。
名字有点难以理解,其实这种模式咱们可能一直在用。就是用一个代理网关层作一些和业务无关的又麻烦的点,好比SSL,实现上用Nginx实现就很简单。咱们常常会对外启用HTTPS服务,而后对内服务实际提供的是HTTP接口,经过网关作一下协议转换。
这也是很常见的做法,咱们对外的接口多是/cart、/order、/search这样的API,在其背后实际上是不一样的服务,经过网关层进行转发,不只仅能够作后端服务的负载均衡和故障转移,在后端服务变动切换对外API路径(好比版本升级)的时候咱们也能够进行灵活的路由,确保了对外接口的一致性。可使用Nginx来实现,相信大部分公司都是由Nginx这样的网关来对外的,不会把域名直接解析到底层服务上对外。
这个模式实际上是挺重要的一点,有几个点须要注意:
实现上,咱们应当把health端口做为插件形式集成到系统,配置一下便可启用,用不着每个系统都本身开发一套。若是使用SpringBoot的话能够直接使用Actuator模块实现。
名字挺吓人,这个模式说的是如何作迁移。经过创建一个门面来做为后端新老服务的路由,慢慢把服务替换为新服务,最后当全部的服务都是新服务后删除这个门面便可。这样对于消费者感知不到这个迁移的过程。在上一篇文章中咱们提到的换引擎的方式其实说的是保留原有的门面,也是经过这个门面作底层引擎的替换。其实我以为对于减小外围影响这种模式是彻底能够理所固然想到的,真正难的过程仍是以前说的数据迁移和底层服务实现的过程。
这个模式说的不是广义上的缓存使用,而是其中的一种使用方式。咱们对于缓存的使用通常有这么几种方式:
这个模式说的是后一种方式,对于数据变更不大,这种模式是性能最好的,几乎实现了100%的命中率,并且若是数据量不大能够容纳进进程的话不须要跨进程通信。往细致一点去想,这里还有一层性能优化的点,由于咱们在内存中维护了一套复杂的全量数据的数据结构,内存中对象的引用只是指针引用,内存中的数据搜索能够很快,对于数据量不大可是关系复杂的数据,这个搜索效率能够是数据库的几百倍。实现上通常会在应用程序启动的时候把数据彻底加入内存,在后续经过一些策略进行数据更新:
英文缩写是CQRS,看到这个关键字你可能会以为有点熟悉了。CQRS原来讲的是咱们能够有两套数据模型分别用于读和写。好处是,咱们可让读和写具备彻底不一样的数据结构,减小相互的干扰,减小权限控制的复杂度。这里说的不必定是指架构层面咱们能够这么作,也指在程序内部,咱们能够有两套命令模型来处理读写这两个事情,分别进行优化和定制。
如今通常的作法是相似于上图的作法,为读写配置两套独立的数据源,而且和事件溯源的方式结合起来作(见后面一节)。咱们来讲说读写两套模型在存储上分离这个事情,在《相辅相成的存储五件套》一文中咱们的架构图其实就有这方面的意思。对于读写这两个事情,咱们彻底能够不用一套数据源,为读创建专门的物化视图,能够针对读进行优化,避免在读的时候作不少Join的工做,能够把性能作到极致(后面会有物化视图模式的介绍)。事件溯源+CQRS+物化视图三者通常会结合起来使用。
事件溯源(ES)是一种有趣的模式,说的是咱们记录的不是数据的当前状态而是叠加的数据变化序列(是否是想到了区块链的数据记录方式)。传统的CRUD方式由于有更新这个操做,因此会产生性能并发方面的局限性,并且咱们还须要配备额外的日志来作审计,不然就产生了信息丢失。而事件溯源模式记录的是事件而不是当前状态,因此有下面的特色:
其实有一些业务场景下这种模式会比CRUD存储更适合:
反过来讲,业务逻辑很简单的系统,须要强一致性的系统,数据不多更新的系统不适合这种模式。不知你所了解到的采用ES模式的业务场景有哪些?你们一块儿交流一下。
咱们在使用数据存储的时候每每会更多考虑存储而不是读取。咱们使用各类数据库范式来设计数据库,在读取数据的时候咱们须要作大量的关联查询以输出符合须要的查询结果。这个时候性能每每会成为瓶颈,物化视图是一种空间换时间的作法。与其在查询的时候作关联,倒不如提早保存一份面向于查询和输出的数据格式。所以,物化视图适合下面的场景:
可是由于须要考虑到物化视图计算保存的开销,因此也不太适合于数据变化太频繁的状况,由于数据加工须要时间,因此不适合须要数据强一致性的场景。
实现上通常是基于消息监听作额外维护一套物化视图的数据源和主流程解耦。惠普的Vertica是一款高性能的列式分析数据库,它的一个特性就是物化视图,经过事先提供SQL语句直接缓存面向于统计的查询结果,极大程度提升了性能,也是空间换时间的思想。
消息队列咱们太熟悉了,以前咱们也反复提升过好屡次,甚至我说这是架构三马车之一。这个模式在这里强调的是削峰的优点。这里我还想提几点:
区别于FIFO结构的队列,优先级队列容许消息标识处理优先级。这里实现上如上面两个图有两种方式:
在方案选择和实现上要考虑消息优先级是否须要绝对按照优先级来处理,仍是说相对优先处理便可,若是须要绝对优先那么除了消息位置重排还须要有抢占处理。还有,若是咱们采用第二种多池的方式来处理的话可能会发生低级别的消息处理时间比高级别的消息更快的可能性(若是二者处理业务逻辑是彻底不一样的话)。
实现上的话RabbitMQ 3.5以上版本支持了消息优先级,实现的是第一种方式,在消息有缓冲的堆积的时候进行消息重排,消费端能够先看到先处理优先级高的消息,这种方式在消费速度大于产出速度的场景下是没法实现高级别消息优先处理的。
补充一点,对于队列中的消息,还有一种须要特别考虑的就是一直停留在队列的消息应当视为低优先级或是死信消息来处理,最好是有单独的消费者来处理,避免此类消息影响了整个队列的处理,见过不少个事故是因为队列中被废弃消息卡死致使完全丧失处理能力的。
在作压力测试的时候咱们会发现,随着压力的上升系统的吞吐慢慢变大并且这个时候响应时间能够基本保持可控(1秒内),当压力突破一个边界后,响应时间一会儿会不可控,随之系统的吞吐就会降低,最后会完全崩溃。任何系统对于压力的负荷是有边界的,超过这个边界以后系统的SLA确定没法知足标准,致使你们都没法好好用这个服务。由于系统的扩展每每不是秒级能够作到的,因此这个时候最快的手段就是限流,只有限流了才能保护如今的系统不至于突破这个边界完全崩溃。对于业务量超大的系统搞活动,对关键服务甚至入口层面作限流是必然的,别无它法,淘宝双11凌晨0点那一刻也能看到必定比例的下单被限流了。
常见的限流算法有这么几种:
令牌桶算法限制的是平均流入速度,容许必定程度的突发请求,漏桶算法限制的是常量的流出速率用于平滑流入的速度。实现上,经常使用的一些开源类库都会有相关的实现,好比google的Guava提供的RateLimiter就是令牌桶算法。
限流模式有下面的一些注意事项:
在下篇中咱们将会继续介绍数据、安全、消息、弹性方面的一些架构模式。