标签:定位 最小 扩容 负责人 多个 软件 界面 tps 封装
朱晔的互联网架构实践心得S1E8:三十种架构设计模式(下)
接上文,继续剩下的15个模式。
一直有一个说法就是不到没路可走的时候不要考虑数据库分片。有的时候业务量大到单个业务表在经过缓存+队列削峰等措施之后的平均的TPS超过1万,单表实在是扛不住,还是只能考虑分片手段。
分片前:
分片后:
相信互联网公司90%+肯定都使用了这个模式。把静态资源从动态网站中剥离由Nginx等高性能服务器来处理静态资源,然后使用三方CDN对静态资源进行加速,不但减轻了动态网站的负载而且数据在边缘节点加速让用户的访问跟快,使用单独的一个或多个子域名做静态资源还能提高下载资源的并行度提高网页加载的速度。
使用CDN来进行资源加速一般有主动数据传送到CDN存储和在CDN配置回源站拉取两种方式,文件类一般使用主动推送数据,静态资源类一般使用回源方式。在使用CDN的时候考虑下面的问题:
在第三第五两篇文章中我都提到了索引表的做法。出于下面的原因,我们会考虑索引表:
不过需要考虑一点索引只有在数据区分度高的情况下才能发挥价值,如果90%以上的数据都是相同的值,那么走索引进行查询性能会比全表扫还要差一点。
这里说的是不同的前端配以不同的专用后端。比如PC网站和APP的后端是两套程序。这种模式是否适合其实还是看两端的后端提供的数据差异有多大,我们总是希望可以尽量统一一套后端,业务逻辑不用重复写,但是我们要考虑到PC网站和APP的差异性:
考虑到这些差异,我们是在一个工程内根据来源做适配,还是独立两套工程来做独立的后端取决于差异度有多大了。
这个模式从资源节省的角度来说我们的计算单元任务可以进行一些合并,减少因为资源限制导致不必要的开销。
对于分布式服务,我们趋向于把服务设计为无状态可以任意扩展的,但是在某些业务场景下我们不得不在服务中选举出一个Leader(Primary节点,Master节点)来做一些不适合重复做的协调管理工作。这个时候我们需要有算法来做选举。
最常见的实现方式是使用Zookeeper来实现,我们知道ZK的znode有Sequence和NonSequence两种,前者多个客户端只有一个可创建成功同名节点,后者创建后会自动加上序列号命名多个客户端可以创建多个同名节点,利用这个特性有两种常见实现方式:
在软件设计模式中过滤器构成的管道这种模式很常见(图上的业务逻辑就是Handler,之前的那些Task就是Filter,模式上可以是Filter+Handler也可以是Filter+Handler+Filter也可以是Handler+Filter),不管是Spring MVC框架也好,Netty这种网络框架也好都提供了这样的设计。每一个过滤器单独完成一个功能,可以独立插拔随意组合配置成一套管道,不但数据处理的整个过程清晰可见还增加了灵活性。
对于架构上也可以有这样的模式,在数据源进入到业务逻辑处理之前(或之后,或前后),我们可以配置一系列的数据过滤器完成各种数据转化和处理的任务。Task和Task之间可以是同步调用,也可以使用MQ做一定的可伸缩性设计。还可以把过滤器的配置信息保存在配置系统中甚至根据上下文动态构建出管道,实现更灵活的前置或后置流程处理。
这里说的是消息队列的消息消费者是一组对等的消费者,通过竞争方式来拉取数据执行。之前提到过这是MQ的最常见的一种模式,一般而言我们会部署多个消费节点进行负载均衡,在负载较大的时候可以方便得增加消费者进行消费能力扩容。不过对于这种模式消费者应当是对等的无状态的,在某个消费者在消费失败的时候消息重新回到队列随后可能会被另一个消费者进行处理。
重试适用于瞬态故障,之后会提到断路器模式,两种模式可以结合使用。首先说说重试的几个发起人:
重试也要考虑几种策略:
这个模式说的是三者的角色:
三个角色相互配合完成复杂的,具有较多远程服务参与的任务,确保任务的最终有效执行。在之前架构三马车一文中说到定时任务的时候提到过一种任务驱动表的模式,说到了一些驱动表的实现细节,其实整体和这个模式是类似的思想。当我们的一个复杂逻辑有多个步骤构成,每一步都依赖外部服务,这个时候我们可以选择全程MQ+补偿方式(乐观方式),也可以选择全程任务驱动的被动模式(悲观方式),具体选择取决于更看重可靠性还是及时性。
资源隔离有好几个层次,可以在进程内部做线程池或队列的隔离,在微服务的服务划分上考虑隔离出单独的物理服务,或是在服务器层面通过虚拟化技术或Docker技术进行资源隔离。隔离了就不会相互影响,但是会有成本、性能、管理便利性方面的开销。实现能够根据需求分析出可能的资源相互影响的点,提前规划隔离往往可以避免很多问题的发生。之前有遇到过几个事故是这样的:
分布式应用环节多网络环境复杂,如果遇到依赖服务调用失败的情况我们或许可以进行重试期待服务马上可以恢复,但是在某些时候依赖的服务是彻底挂了而不是网络故障无法及时恢复,如果不考虑进行熔断的,可能服务调用方会被服务提供方拖死。这个时候可以引入断路器机制,如图所示断路器一般采用三态实现,瞬间恢复可能会让底层服务压力过大:
实现模式的时候考虑下面注意点:
实现上我们可以看一下Netflix的Hystrix进行进一步了解。
这个模式说的是失败时必须进行撤销的操作,可以由一组补偿程序来做相应的补偿。在这里我想说的更广一点,在服务调用的时候,调用失败有几种可能:
所以在出现服务调用失败或超时的时候,服务端执行究竟有没有成功客户端是不明确的(只有客户端收到了明确的服务端返回的业务错误才真正代表执行失败),这个时候需要有补偿逻辑来同步服务端的执行状态。如果这样的补偿不可避免而且需要补偿的服务特别多,这样的逻辑逐一来写是一件很烦的事情,我们可以把这个工作封装成一个补偿中间件来处理:
这样,我们在服务调用的时候就不需要考虑补偿逻辑的实现,只要实现这个标准即可。
这个模式说的是,在访问敏感资源的时候,我们可以不必让应用程序在其中作为一个代理转一层做权限控制,而是生成一个密钥,让用户直接拿着密钥到资源池换数据。
一些CDN在提供资源上传下载服务的时候一般都会提供类似的安全策略,需要实现生成Token才能去使用下载和上传服务,避免了CDN数据被非法利用作为图床的可能。
实现上比较简单,应用程序和资源提供方约定好Token的生成算法,对资源+请求资源的时间+密钥联合在一起做签名,资源提供方如果校验到签名不正确或Token过期或资源不匹配都将拒绝服务。
这个模式说的是将身份验证委托给专门的程序或模块来做。使用专门的模块来统一负责登录授权不仅仅可以提供单点登录的功能,而且服务实现上更简单不需要每次都考虑登录那套东西。实现上可以看一下Spring Security实现的OAuth 2.0。
总结一下,对于其中的很多模式,我们可以发现其实在之前的一些介绍或多或少有一些涉及。这里提到的30种模式有些体现的是一些设计细节,有些体现的是一种设计理念,它们大多时候是组合使用的,适合的就是最好的,大家可以细细品味一下每种模式的适合场景,在合适的时候可以想到它或许会有一种豁然开朗的感觉。
标签:定位 最小 扩容 负责人 多个 软件 界面 tps 封装
原文地址:https://www.cnblogs.com/lovecindywang/p/9674123.html