架构篇
架构篇
接入层架构
浏览器层:浏览器(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的成本往往很高,成为大家共性的痛点。
公共库导致耦合
-
可以把公共库再细分
有点像接口隔离原则
-
可以把公共库服务化
单独为公共库提供一个独立的服务,比如session,分布式id,缓存服务,公共配置服务
调用方实现通知
假如消息发送方不关注消息接收方的执行结果,如果采用调用的方式来实现通知,会导消息发送方和消息接收方耦合。
- MQ来解决调用方和接收方耦合 ,这样添加接收方就不用修改调用方了
配置中的ip导致上下游耦合
-
通过内网域名而不是ip来进行下游连接
只需要运维层面将内网域名指向新的ip,然后统一切断原有旧的连接,连接就能够自动切换到新的ip上来。这个过程不需要所有上游配合,非常帅气,强烈推荐
-
虽然运维修改内网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
- 业务调用方调用内部service
- 内部service调用异步代理service
- 异步代理service通过OpenID在本地拿取数据
- 异步代理service将数据返回内部service
- 内部service返回结果给业务调用方
-
异步远程 :
async-proxy <-----> out-service (wechat-opensdk)
->db/redis
- 异步代理service定期跨公网调用微信服务
- 微信服务返回数据
- 刷新本地数据
-
优缺点
有时候,内部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实现索引字段上的查询需求。
-
-
全文索引能够快速实现业务上分词的需求,并且快速提升性能(分词后倒排,至少不要全表扫描了),但也存在一些问题:
-
只适用于MyISAM
-
由于全文索引利用的是数据库特性,搜索需求和普通CURD需求耦合在数据库中:检索需求并发大时,可能影响CURD的请求;CURD并发大时,检索会非常的慢;
-
数据量达到百万级别,性能还是会显著降低,查询返回时间很长,业务难以接受
-
比较难水平扩展
开源外置索引
- 索引数据与原始数据分离,前者满足搜索需求,后者满足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)模拟调用方调用站点、服务,来对站点和服务进行监控
到底什么样的监控,才能说明系统是正常的呢?
回答:只有站在调用者的角度,对被调用方的可用性可靠性的评判才是最准确的