Nivelle 开拓视野冲破艰险看见世界 身临其境贴近彼此感受生活

架构学习(一)之基本原则

2017-10-14
nivelle

大型系统原则

高并发原则

  • 无状态:如果设计的应用是无状态的,那么应用就比较容器进行水平扩展.实际生产环境可能是这样的:应用无状态,配置文件有状态.
  • 拆分:常见的拆分主要有如下几种情况.

1.系统维度:按照系统功能维度/业务拆分,比如商品系统,购物车,结算,订单系统

2.功能维度:对一个系统进行功能再拆分,比如,优惠券系统可以拆分为后台创建系统,领取系统,用卷系统.

3.读写维度:根据读写比例特征进行拆分.比如,商品系统,交易的各个系统都会读取数据,度的量大于写,因此可以拆分成商品写服务,商品读服务;读服务可以考虑使用缓存提升性能;写的量太大时,需要考虑分库分表;有些聚合读取的场景,如商品详情页,可考虑数据异构拆分系统,将分散在多处的数据聚合到一处存储,以提升系统的性能和可靠性.

4.AOP 维度:根据访问特征,an咋好AOP进行拆分,比如,商品详情页可以分为CDN,页面渲染系统;CDN就是一个AOP系统.

5.维度模块:按照基础或者代码维护特征进行拆分,如基础模块分库分表,数据库连接池等;代码结构一般按照三层结构(Web,Service,DAO)进行划分.

  • 服务化:<font color=red>进程内服务–单机远程服务–集群手动注册服务–自动注册和发现服务–服务的分组/隔离/路由–服务治理如限流/黑白名单.</font>
  • 消息队列:解耦一些不需要同步调用的服务或者订阅一些自己系统关系的变化.使用消息队列可以实现服务解耦(一对多消费),异步处理,流量削峰/缓冲等.消息队列需要注意消息的失败重试以及消息重复接收的问题.
    1. 大流量缓冲:系统流量飙升时,可以通过一些特殊设计保证系统平稳过多,一般是牺牲强一致性,而保证最终一致性即可.
    2. 数据校对:在使用了异步机制的场景下,可能存在消息的丢失,需要考虑进行数据校对和修正来保证数据的一致性和完整性.可以通过Worker定期去扫描原始表,通过对业务数据进行校对,有问题的要进行补偿.
  • 数据异构:

1.订单分库分表一般按照ID进行分,如果要查询某个用户的订单列表,则需要聚合多个表的数据后才能返回,这样会导致订单表的读性能很低.此时需要对订单进行异构,异构一套用户订单表,按照用户ID进行分库分表.另外.还需要考虑对历史订单数据进行归档处理,以便提升服务的性能和稳定性.

2.数据闭环:数据闭环如商品详情页,因为数据来源太多,影响服务稳定性的因素就非常多了.因此,最好把使用到的数据进行异构存储,形成数据闭环,基本步骤如下:

- 数据异构:通过MQ机制接收数据变更,然后原子化存储到合适的存储引擎,如redis或者持久化KV存储
   
- 数据聚合(可选):数据聚合的目的是吧这些数据做个聚合,这样前端就可以一个调用拿到所有数据,此步骤一般存储到KV存储中.

- 前端展示:前端通过一次或少量几次调用就可以拿到所有数据.
  • 缓存银弹:

image

  1. 浏览器端缓存:设置请求的过期时间,如对应头Expires,Cache-control进行控制.适用于对实时性不太敏感的数据.
  2. App客户端缓存:将一些素材提前下发到客户端进行缓存,首页数据也可缓存起来,在网路异常情况下作为托底数据展示给用户.
  3. CDN缓存:将一些数据等信息推送到离用户近的CDN节点,让用户能再离他最近的节点,让用户能再离他最近的节点找到想要的数据.一般有两种机制:推送机制(当内容变更后主动推送到CDN边缘节点)和拉取机制(先访问变越节点,当没有内容时,回源到源服务器拿到内容并存储到节点上).使用CDN时要考虑URL设计,不能有随机数,否则每次都穿透CDN回源到源服务器,相当于CDN没有任何效果.
  4. 接入层缓存:对于没有CDN缓存的应用来说,可以考虑使用如Nginx搭建一层接入层,该层可以考虑使用如下机制实现:

    • Url重写:将URL按照指定的顺序或者格式重写,去除随机数
    • 一致性哈希:按照指定的参数(如分类/商品编号)做一致性Hash,从而保证相同数据落到一台服务器上
    • proxy_cache:使用内存级/ssd级代理缓存内容
    • proxy_cache_lock:使用lock机制,将多个回源合并为一个,以减少回源量,并设置相应的lock超时时间.
    • shared_dice:如果架构使用了nginx+lua实现,则可以考虑使用lua shared_dict进行cache,最大的好处就是reload缓存不会丢失.
  5. 应用层缓存:使用tomcat时,可以使用堆内缓存,堆外缓存,堆内缓存的最大问题就是重启时内存中的缓存会丢失,可以使用local redis cache来代替堆外内存;或在接入陈使用shared_dict来将缓存前置,以减少风暴.

local redis cache ,通过在应用服务器上部署一组Redis,应用直接读取本质redis上的数据,多机之间使用主从同步机制同步数据.这种机制没有网络消耗,性能是最优的.

  1. 分布式缓存:如果数据量太大,单服务器存储不了,那么可以使用分片机制将流量分散到多态,或者直接使用分布式缓存.常见的分片规则就是一致性哈希了.

image

高可用原则

  • 降级:对于高可用服务,很重要的一个设计就是降级开关,在设计降级开关的时候,主要依据如下思路
  1. 开关集中化管理:通过推送机制把开关推送到各个应用.

  2. 可降级的多级读服务:比如服务调用降级为只读本地缓存,只读分布式缓存,只读默认降级数据(如库存状态默认有货)

image

  1. 开关前置化:如架构师Nginx-Tomcat,可以将开关前置到Nginx接入层,在Nginx层做开关,请求流量回源后端应用或者只是一小部分流量回源.

image

  1. 业务降级:当高并发流量来袭,在电商系统大促是保障用户能下单,能支付是核心要求,并保障数据最终一致性即可.这样就可以把一些同步调用改成异步调用,优先处理高优先级数据或特殊特征的数据,合理分配进入系统的流量,以保障系统.
  • 限流: 限流的目的是防止恶意流量,恶意攻击或者防止流量超出系统峰值.可以考虑的思路如下:
  1. 恶意请求流量只访问到cache
  2. 对于穿透到后端应用的流量可以考虑使用nginx的limit模块处理
  3. 对于恶意IP可以使用nginx deny进行屏蔽
  • 切流量:对于一个大型应用,切流量非常重要,比如多机房环境下某个机房挂了,或者某个机架挂了,或者某台服务器挂了,都需要切流量,可以使用如下手段进行切换.
  1. DNS:切换机房入口
  2. HttpDNS:主要App场景下,在客户端分配好流量入口,绕过运营商LocalDNS并实现精准流量调度
  3. LVS/HaProxy:切换故障的Nginx接入层
  4. Nginx:切换故障的应用层
  • 可回滚:版本话的目的是实现可审计科追溯,并且可回滚.常见的有事物回滚,代码库回滚,部署版本回滚,数据版本回滚,静态资源版本回滚.

业务涉及原则

  • 防重设计:对于防重设计可以考虑的解决方案有防重Key,防重表.在设计系统时,需要将每条记录记录下来.
  • 幂等设计:交易系.统中,经常会用到消息,而现在消息中间件基本不保证不发生重复消息的消费.因此,需要业务系统在重复消息消费时进行幂等处理.还要使用第三方支付时,第三方支付会进行异步回调,也要考虑回调的幂等处理
  • 流程可定义:将相对独立的模块进行定义,需要时将不同的流程进行关联.
  • 状态与状态机:
  1. 在设计交易订单系统时,会存在正向状态(待付款,待发货,已发货,完成)和逆向状态(取消,退款)等,正向状态和逆向状态应该根据系统的特征来决定要不要分离存储.

  2. 状态设计时应有状态轨迹,方便用户跟踪当前订单的轨迹并记录相关日志,万一出问题是可回溯问题.订单状态的变迁,比如待支付,已支付待发货,待收货,完成的迁移.

  3. 考虑并发修改问题,一个订单同时只能有一个修改;状态变更的有序问题,以及状态变更消息的先到后到问题

  • 后台操作系统可反馈
  • 后台程序审批化
  • 文档和注释

上一篇 java内存模型.

评论