俄罗斯大叔的讲解比那套SD课程细很多,令人有打开新世界之感。觉得需要一套笔记记录之。
需求讨论关注:用户 规模 性能 开销
Users, who and how will use the system.
总量?每小时统计?月度统计?(查询频率低)或者实时需求
Scale, how our system will handle a growing amount of data.
每秒处理多少请求?每个请求查询多少数据?是否有忽增高峰期,多大?
Performance, how fast our system must be.
- 如果不实时,批处理(慢)流处理都可以。
- 如果要求响应时间短,暗示数据必须事先计算好
cost, budget constraints.
- 如果要开发成本小,就用好评的开源
- 如果要维护成本小,用云
-
需求讨论为了:定义功能性/非功能性需求
功能需求:API
(在白板上写)
like 批处理事件,每个事件都是一个对象包括视频、事件类型、发生时间等信息。
非功能需求:system qualities:fast, fault-tolerant, secure
CAP 取舍
-
high-level:需要一个db、一个web服务写入db、一个服务读db
从简单的开始:data —— 存哪些 / 如何存
如何存:
两种方式:
① [批式]存原始数据,当场计算:费钱,读很慢。
适用于不要求时延的
② [流式]聚合数据 :读的快,可实时决策。很难换方式,需要实现聚合管道,难修bug
适用于时延低,需动态更新数据
③ 两者结合:原始数据存储一段时间就清除,并实时计算和存储数字
可快速读取 + 以不同方式聚合数据
但系统更复杂了
存在哪:
SQL & NoSQL :根据非功能需求:性能 可扩展性 可用性,来评估
SQL: 数据规范化
可能需要分区 → 引入轻量级代理服务器,可以查找正确db
→ 处理和查询都只与集群代理通信,代理需要知道分片的忙闲和分片增减
→引入新组建:配置服务:维护检查所有分片
——— 解决了可扩展性和性能。
但可用性:分片死了?丢了?
→ 复制数据 + 读写分离
in all: 代理 + 配置服务 + 领导者 + 副本实例
NoSQL: 非规范化数据
无leader,不需要配置服务监控。允许分片间相互交互信息。
一致性哈希来选择存储节点
仲裁写入与仲裁读取。(> n/2 则通过)
Cassandra:最终一致性 | 版本号判定时序
nosql有四种类型:列、文档、键值、图形
cassandra:异步无主复制、容错、可扩展、可以很好地处理时间序列数据
mongoDB:面向文档,基于领导者复制
HBase:面向列,类cassandra,也有基于leader的架构
处理数据:
like更新数据库中的计数器:
① 每来一个事件计数器就+1
②在内存里累积一段时间,再将累计值加至数据库计数器。(选这个)
push & pull 都是对service来说的
push: other service sends events to service
pull: processing service pulls events from some temporary storage
更好的容错和易扩展性
因为可以当还没推给db时data丢失的情况下,可以再从临时存储中拉。
partition:processing service 从partition读取event,对内存中的event计数,并定期将计数值更入数据库。—— 需要一个组件component来读取event
partition consumer: 用户通过tcp连接来拿数据,无限轮询来拿,拿到了就str→object (deserializes)
deduplication cache: 如果是多线程拿取,这里需要去重。
like:使用分布式缓存来存唯一的事件标识符,比如10min。10min内有相同消息,则只会处理其中一条。
aggregator: 然后event到达内存开始计数,called 聚合器。底层是hash,定期计算data。计算的时候,该hash停止写入,并新建一个hash,用来累积新来的data,旧hash的数据旧传入internal queue
internal queue:将消费(consumption)和进程(processing)解耦decouple。(也可以将queue放在聚合器之前)
database write: 负责把数据写入数据库。单线程or多线程都可以。
dead letter queue:当消息无法路由到正确的目的地时,会发送到这个队列。保护性能,增强可用性。数据库变慢/下游降级也可以用。
另一个选项是,将未传递的消息存储在处理服务机器的本地磁盘上。
data enrichment:由于process中的事件只保存了最少量的信息like id,那么其它需要存的的信息,就放在这个embedded db嵌入式数据库里。这个嵌入数据库和处理服务在同一台机器上,方便快速检索。
state store:
数据在内存中待很久,或者很难现场重新计算时,
定期将整个内存的数据保存到持久存储中。
数据摄取管道 data ingestion path
用户打开video,request通过API 网关,路由到后端服务
分成三个部分:客户端、LB、分区服务
client:
IO:
client用套接字启动连接,给服务端发请求,服务端回应。
阻塞IO会产生大量线程处理请求,可能变慢甚至die,所以需要限速。
可以用非阻塞IO替代,which可以用单线程来处理多个并发连接,服务只是将请求排成队列,然后等会处理。
但是非阻塞IO很难debug
buffering and batching:
事件数量规模很大,不能单独传递,需要将事件组合在一起,就是批处理。
首先将事件放入buffer,等这批满了再传递。
增加了吞吐量、节省成本。但是如果个别出错,就很难挑出来。
timeout & retried:
两种超时:连接超时 (几十毫秒) | 请求超时
如果请求超时,就重试。但是要避免大量请求同时重试,或者重试太多次。
用 exponential backoff 指数退避 和jitter 抖动,增加重试间隔和随机性。
再用circuit breaker 断路器模式增加阈值,如果超过就停止。再过一段时间,尝试少量请求,如果成功就恢复。
Load Balancer:
两种:软件和硬件。硬件是买的网络设备。
另一个层面的LB是服务于TCP HTTP的流量
协议:
TCP的LB只转发(forward)包,不检查包内容
HTTP的LB会 查看消息包,并根据消息内容like cookies做出LB决策
算法:
轮询、LR、LF 、hash
DNS:
client如何知道LB?LB如何知道server?LB如何高可用?
DNS:在dns中注册服务,指定域名ip。
当customer点击域名时,请求被转发到LB。为了让LB了解server,需要明确告知LB每台server的IP。LB中提供API注册和注销server,并且知道哪些可用哪些不可用。
LB会定期ping 每台server,如果是坏的就停止发流量,直到它恢复。
高可用性:用一个辅助LB(和主LB位于不同的数据中心)
Partioner Service:
从客户端获取请求,检索事件,并路由到某个分区
partition获取message,用log形式存在disk。(有序的sort by time)
partition strategy:分区策略
hash(不适合大规模,会有热点)
——> 如何解决:用时间分区 | 直接拆分区by一致性哈希
service discovery:
两种发现模式:服务端发现 & 客户端发现
服务端发现:客户端知道lb,lb知道server
分区服务器和分区之间不需要LB,分区服务器自己就是个LB,来分发event给分区 ——— 客户端发现,每个服务器实例都会在服务注册中心注册。
服务注册中心:高可用web服务,可以健康检查。
客户端检查服务注册中心,得到可用服务器列表。like zookeeper
服务发现还可以做:节点之间互相通信。like cassandra。client只用联系其中一个节点,就能知道整个集群。
replication:
三种:单主复制(扩展DB)、多主复制(多个数据中心之间复制)和无主复制(cassandra)
如果只给leader写数据,如果没复制完之前leader就挂了,就会丢失数据。
如果等复制完成,就会增加延迟。
message format:
文本格式:xml,csv,json。人类可读
二进制格式:Thrift、Protocol Buffers 和 Avro。更快。用数字标签代替字段名称。
Data retrieval path 数据检索路径
client → API 网关 → 许多web service like 检索视频信息的、评论的、推荐的
→ db 从这里拿数据 → 检索时进行数据联合,可能调用多个存储,然后拼接数据 → 然后将结果放在cache
rollup data:将每分钟的计数存几天,然后将这些数据汇聚成每小时的数据,然后再存几个月,更旧的数据旧放在aws s3这种地方。
冷储存:不需快速访问。 热储存:常用数据。
data flow simulation
tecknology stack
netty:高性能的非阻塞IO框架,用于开发网络应用程序
Netflix 的 Hystrix /Polly : 简化了超时、重试、断路器模式
Citrix NetScaler: 著名的硬件负载均衡器
NGINX: 软件负载均衡器
Elastic Load Balancer:当计数系统运行在云中时用。
Kafka:不用自定义patitioner服务和分区
Amazon Kinesis:kafka的公共云对应物
Spark / Flink:流处理框架。处理事件并将它们聚合到内存中 或者用云Kinesis
Cassandra, HBase:存储时间序列数据。and InfluxDB
Hadoop / AWS Redshift : 存原始数据
AWS S3 :汇总数据并 归档
Vitess :用于扩展和管理大型 MySQL 实例集群的数据库解决方案
Redis:分布式缓存
RabbitMQ:死信队列机制 or Amazon SQS
RocksDB: 高性能嵌入式数据库,存本地富余信息
Zookeeper:分布式配置服务,进行分区的领导者选举和管理服务发现(or Eureka Web)
CloudWatch:云。监控每个系统设计组件 or 开源框架堆栈:Elasticsearch、Logstash、Kibana
MurmurHash:散列函数,用于Partitioner 服务对数据进行分区
瓶颈性能取舍
how to identify bottlenecks?
要识别瓶颈,需要在高负载下测试
几种性能测试:
负载测试:测量系统在特定预期负载下的行为
压力测试:超出正常运营能力时达到的临界点
找到breaking point:内存、CPU、网络、磁盘 IO
浸泡测试:长时间测试具有典型生产负载的系统
找到资源中的泄漏like 内存泄漏 leaks
可以用Apache JMeter等工具
how do you make sure the system is running healthy?
健康监测:
监控的四个黄金信号:延迟、流量、错误和饱和度 latency, traffic, errors, and saturation.
指标:衡量的变量,例如错误计数或处理时间
仪表板:提供服务核心指标的摘要视图
警报:针对服务中发生的某些问题而发送给服务所有者的通知
how to make sure the system produces accurate results?
构建审计系统
两种:weak strong
弱审计系统:一个持续运行的端到端测试。但不是100%可靠,尤其在长期运行中。
我们每分钟在系统中生成几个视频观看事件时,调用查询服务并验证返回值是否等于预期计数
强审核系统:使用与主系统完全不同的路径计算视频观看次数
like 将原始事件存储在 Hadoop 中并使用 MapReduce 对事件进行计数。然后比较两个系统的结果。
called Lambda Architecture:关键思想是将事件并行发送到批处理系统和流处理系统
如果我们对延迟不敏感,我们应该使用像 MapReduce 这样的批处理框架,如果是,我们应该使用流处理框架,但除非绝对必须,否则不要尝试同时进行两者
单点故障怎么办,处理速度跟不上怎么办
将事件批处理并存在对象存储服务中,例如 AWS S3。
每次持久化一批事件时,都会向消息代理发送一条消息,例如 SQS。
然后我们有一个大型机器集群,例如 EC2,从 SQS 检索消息,从 S3 读取相应批次的事件并处理每个事件。
这种方法比流处理慢一点,但比批处理快。
总结:
- 明确需求:定义 API, 系统到底应该做什么。
定义 API,我们与面试官讨论我们需要设计系统的哪些特定行为或功能。
写下函数定义,及输入参数和返回值。
可以进行多次迭代来更新 API。
- 讨论非功能性需求,并找出她最感兴趣的系统质量。
建议关注可扩展性、可用性和性能。
以及一致性、耐用性、可维护性和成本
尽量选择不超过 3 种
- 概述系统的高级架构,在白板上画出一些关键部件。
考虑数据如何进入系统、如何离开系统以及数据在系统内部的存储位置
- 深入研究其中的几个组件。面试官将帮助我们了解我们应该关注哪些组件。
设计特定组件时,从数据开始,它是如何存储、传输和处理的。
5.讨论瓶颈以及如何处理它们。