架构篇

架构篇

接入层架构

浏览器层:浏览器(browser) -> DNS Server (DNS轮询)

反向代理层: (virtual IP ----- lvs + Keepalived ) * n

Nginx * n

站点层 tomcat * n

1)通过DNS轮询来线性扩展入口lvs层的性能

2)通过keepalived来保证高可用

3)通过lvs来扩展多个nginx

4)通过nginx来做负载均衡,业务七层路由

高并发

常用的一些指标有响应时间(Response Time),吞吐量(Throughput),每秒查询率QPS(Query Per Second),并发用户数等

  • 响应时间:系统对请求做出响应的时间。例如系统处理一个HTTP请求需要200ms,这个200ms就是系统的响应时间。
  • 吞吐量:单位时间内处理的请求数量。
  • QPS:每秒响应请求数。在互联网领域,这个指标和吞吐量区分的没有这么明显。
  • 并发用户数:同时承载正常使用系统功能的用户数量。例如一个即时通讯系统,同时在线量一定程度上代表了系统的并发用户数。

高可用

高可用HA(High Availability)是分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计减少系统不能提供服务的时间。

方法论上,高可用是通过冗余+自动故障转移来实现的。

整个互联网分层系统架构的高可用,又是通过每一层的冗余+自动故障转移来综合实现的,具体的:

(1)【客户端层】到【反向代理层】的高可用,是通过反向代理层的冗余实现的,常见实践是keepalived + virtual IP自动故障转移

(2)【反向代理层】到【站点层】的高可用,是通过站点层的冗余实现的,常见实践是nginx与web-server之间的存活性探测与自动故障转移

(3)【站点层】到【服务层】的高可用,是通过服务层的冗余实现的,常见实践是通过service-connection-pool来保证自动故障转移

(4)【服务层】到【缓存层】的高可用,是通过缓存数据的冗余实现的,常见实践是缓存客户端双读双写,或者利用缓存集群的主从数据同步与sentinel保活与自动故障转移;更多的业务场景,对缓存没有高可用要求,可以使用缓存服务化来对调用方屏蔽底层复杂性

(5)【服务层】到【数据库“读”】的高可用,是通过读库的冗余实现的,常见实践是通过db-connection-pool来保证自动故障转移

(6)【服务层】到【数据库“写”】的高可用,是通过写库的冗余实现的,常见实践是keepalived + virtual IP自动故障转移

依赖与解耦

eg. 数据库换了一个ip,此时往往连接此数据库的上游需要修改配置重启,如果数据库有很多上游调用方,改配置重启的调用方会很多,每次换ip的成本往往很高,成为大家共性的痛点。

公共库导致耦合

  1. 可以把公共库再细分

    有点像接口隔离原则

  2. 可以把公共库服务化

    单独为公共库提供一个独立的服务,比如session,分布式id,缓存服务,公共配置服务

调用方实现通知

假如消息发送方不关注消息接收方的执行结果,如果采用调用的方式来实现通知,会导消息发送方和消息接收方耦合。

  1. MQ来解决调用方和接收方耦合 ,这样添加接收方就不用修改调用方了

配置中的ip导致上下游耦合

  1. 通过内网域名而不是ip来进行下游连接

    只需要运维层面将内网域名指向新的ip,然后统一切断原有旧的连接,连接就能够自动切换到新的ip上来。这个过程不需要所有上游配合,非常帅气,强烈推荐

  2. 虽然运维修改内网DNS,将内网域名指向新的IP如果是长连接调用,新的长连接会连到新的IP上,但旧的长连接仍然连接的是旧IP

    • 连接切断自动重连
    • 运维统一将旧IP上的连接切断,如无意外,服务或者数据库的连接池都有重连功能,重连后就会自动连到新IP上去

下游扩容导致上下游耦合

  • 多个上游需要修改配置重启

  • 问题原因就是 配置私藏 ( 配置数据的扩散在不同的上游服务配置文件中)

  • 解决方案:配置中心

    • MQ 配置中心是常见的互联网架构解耦利器:

      • 前者,逻辑解耦,物理解耦 , 后者,逻辑解耦,物理不解耦
      • 物理不解耦 : 指物理上要建立连接,产生依赖
    • 所有通用配置将由配置中心统一维护,数据只存储一份,不再有“配置私藏”

      上游通过配置中心来订阅下游配置,下游的配置变更,例如扩容时,通过配置中心统一修改

      配置中心将变更后的配置通知所有上游订阅方

      订阅方得知下游服务扩缩容后,通过动态连接池,自动新增或者销毁连接,实现自动扩容与缩容,大部分服务发现都是这么做的

典型架构

跨公网调用(依赖第3方)

  • 内部服务inner-service ----> 依赖 ---> out-service(外部服务)

    内部服务可能对上游业务提供了很多服务接口,当有一个接口跨公网第三方调用超时,可能导致所有接口都不可用,即使大部分接口不依赖于跨公网第三方调用。

  • 内部服务对业务方提供的N个接口,会共用服务容器内的工作线程 ,假设这N个接口的某个接口跨公网依赖于第三方的接口,发生了网络抖动,或者接口超时 。 如果这个接口把工作线程全部占满,其它N-1个不依赖外部接口也不能得到工作线程

优化方案

不根本方案
  • 增大工作线程数(不根本解决问题)
  • 降低超时时间(不根本解决问题)
  • 垂直拆分,N个接口拆分成若干个服务,使得在出问题时,被牵连的接口尽可能少
    • 业务隔离
    • 依旧不根本解决问题,难道一个服务只提供一个接口吗?
异步代理
方案架构
  • 增加一个代理,向服务屏蔽究竟是“本地实时”还是“异步远程”去获取返回结果

  • 比如 通过OpenID实时获取微信用户基本信息的场景

    • 本地实时 : biz <-> inner-service <->async-proxy->db/redis

      1. 业务调用方调用内部service
      2. 内部service调用异步代理service
      3. 异步代理service通过OpenID在本地拿取数据
      4. 异步代理service将数据返回内部service
      5. 内部service返回结果给业务调用方
    • 异步远程 :

      async-proxy <-----> out-service (wechat-opensdk)

      ​ ->db/redis

      1. 异步代理service定期跨公网调用微信服务
      2. 微信服务返回数据
      3. 刷新本地数据
优缺点

有时候,内部service和异步代理async-proxy可以合成一个service

优点:公网抖动,第三方接口超时,不影响内部接口调用

缺点:本地返回的不是最新数据(很多业务可以接受数据延时)

DNS运用

水平扩展Nginx层

典型的互联网架构中,可以通过增加web-server来扩充web层的性能,但反向代理nginx仍是整个系统的唯一入口,如果系统吞吐超过nginx的性能极限,难以扩容,此时就需要dns-server来配合水平扩展。

用户就近访问

根据用户ip来返回最近的服务器ip,称为“智能dns”,cdn以及多机房多活中最常用。

Session架构设计

反向代理hash一致性

  • 反向代理hash一致性:四层hash和七层hash都可以做,保证一个用户的请求落在一台web-server上
  • 七层代理hash
    反向代理使用http协议中的某些业务属性来做hash,例如sid,city_id,user_id等,能够更加灵活的实施hash策略,以保证同一个浏览器用户的请求落在同一个web-server上
    • 所谓四层就是基于IP+端口的负载均衡;
    • 七层就是基于URL等应用层信息的负载均衡;
    • 同理,还有基于MAC地址的二层负载均衡和基于IP地址的三层负载均衡。 换句换说,二层负载均衡会通过一个虚拟MAC地址接收请求,然后再分配到真实的MAC地址;三层负载均衡会通过一个虚拟IP地址接收请求,然后再分配到真实的IP地址;
    • 四层通过虚拟IP+端口接收请求,然后再分配到真实的服务器;
    • 七层通过虚拟的URL或主机名接收请求,然后再分配到真实的服务器。
  • 缺点: 如果web-server重启,一部分session会丢失,产生业务影响,例如部分用户重新登录
    •如果web-server水平扩展,rehash后session重新分布,也会有一部分用户路由不到正确的session

Redis统一存储

没有安全隐患, 可以水平扩展,数据库/缓存水平切分即可 ,web-server重启或者扩容都不会有session丢失

不足:增加了一次网络调用,并且需要修改应用代码

如果有session高可用需求,cache可以做高可用,但大部分情况下session可以丢失,一般也不需要考虑高可用。

  • Spring Session

搜索架构

站点搜索

相对于全网搜索 ,我们需要了解的还是站点搜索比较多

原始阶段-LIKE
  • 数据库的模糊查询
  • 不支持分词,容易造成锁表 (%keyword% 全表扫描)
全文索引
  • 还是建立上关系型数据库上 (FULLTEXT)

    • 能快速提高效率

    • 支持分词 并对原有系统架构影响尽可能小

      alter table t_tiezi add fulltext(title,content) 使用match和against实现索引字段上的查询需求。

  • 全文索引能够快速实现业务上分词的需求,并且快速提升性能(分词后倒排,至少不要全表扫描了),但也存在一些问题

  1. 只适用于MyISAM

  2. 由于全文索引利用的是数据库特性,搜索需求和普通CURD需求耦合在数据库中:检索需求并发大时,可能影响CURD的请求;CURD并发大时,检索会非常的慢;

  3. 数据量达到百万级别,性能还是会显著降低,查询返回时间很长,业务难以接受

  4. 比较难水平扩展

开源外置索引
  • 索引数据与原始数据分离,前者满足搜索需求,后者满足CURD需求,通过一定的机制(双写,通知,定期重建 (MQ))来保证数据的一致性。
  • Solr,Lucene,ES (推荐ElasticSearch )
    • ES完全能满足10亿数据量,5k吞吐量的常见搜索业务需求,强烈推荐。
    • 数据量十亿,访问qps一万是能够承受的
自研搜索引擎

100亿数据量;并发量也进一步增加,达到每秒10万吞吐;业务个性也逐步增加的时候,就需要自研搜索引擎了,定制化实现搜索内核了。

索引分级
  • 索引分级

  • dump&merge

    • dumper:将在线的数据导出

      merger:将离线的数据合并到高一级别的索引中去

    • 小时库,天库,全量库

    • 【小时库】,一小时一次,合并到【天库】中去;

      【天库】,一天一次,合并到【全量库】中去;

      这样就保证了小时库和天库的数据量都不会特别大;

      如果数据量和并发量更大,还能增加星期库,月库来缓冲。

无锁缓存

可以通过Map转Array的方式来最小化锁冲突,一条记录一个锁 (连接池就是如此)

可以把锁去掉,最大化并发,但带来的数据完整性的破坏

通过签名的方式保证数据的完整性,实现无锁缓存

监控平台架构

创业型公司快速实施立体化多维度监控总结:

(1)机器、操作系统维度监控:zabbix

(2)进程、端口维度监控:分发型监控 + 汇总型监控

(3)错误日志与关键字维度监控

(4)keepalive接口与所有接口统一处理时间统一上报监控

(5)模拟调用方调用站点、服务,来对站点和服务进行监控

到底什么样的监控,才能说明系统是正常的呢?

回答:只有站在调用者的角度,对被调用方的可用性可靠性的评判才是最准确的

posted @ 2021-06-27 17:35  沉梦匠心  阅读(85)  评论(0)    收藏  举报