互联网应用服务端的经常使用技术思想与机制纲要

拓展技术边界,提高业务创新能力。html


不患不知,而患不精。精益求精,技术过硬是必要的。理解互联网技术的底层原理与实现,对提高设计能力很是有益处,亦能为业务创新提供重要的技术保障。技术深度不够会限制业务创新能力。java

本文旨在聚合互联网应用的服务端所用到的主要且重要的技术思想、原理、机制、技巧。

node

综述

软件的使命是处理数据。人赋予数据以意义。mysql

数据处理的要求:灵活、高性能、高可靠、高可用、一致、海量、安全、自动化、智能化。redis

  • 灵活:数据建模与存储、架构与编程、微服务、框架、代理、组件与配置化。
  • 高性能:数据结构与算法、并发、缓存、精简开销、参数调优、编译优化、服务器性能、服务指标测量。
  • 高可用:限流、降级、冗余。架构上要支持水平扩展,运维上多节点部署、负载均衡、弹性伸缩容(容器化)。
  • 高可靠:分布式 + 容错。
  • 一致: 复制、状态机、一致协议、重试补偿对帐。
  • 海量:分布式 + 大数据存储 + 实时计算 + 离线分析
  • 安全:机密性、完整性、可用性。机密性 - 数据内容不泄露;完整性 - 数据内容不被篡改;可用性 - 保护资源随需所得。
  • 智能化:让机器从大量已有数据中自动学习和分析模式,增长反馈环节,来改善产品、业务的前置环节。

视角

解决问题视角算法

  • 解决什么问题,有哪些情形,要考虑什么错误和异常 ?What - Problem - Circumstances - Errors - Exceptions
  • 为何要解决这个问题 ? 形成了什么麻烦,有哪些应用场景 ? Why - Troublesome - Applications
  • 如何解决:理念、方法、实践如何 ? How - Thoughts - Methods - Practices
  • 方案对比:优势和缺点,适用场合 ? Evaluation - Benefits - Drawbacks

系统设计视角sql

  • 核心抽象、概念及类。 Abstraction - Conception
  • 主流程、重要环节、核心类的串联。 Flow - Segments - Links
  • 问题分解、技术难点与方案、可行性论证。Decomposition - Keypoints - Solutions - Feasibility
  • 关键数据集及算法。 Core Dateset - Algorithm

技术决策视角数据库

  • 场景考量:通用场景,仍是特定场景? 场景是否有特殊性,能够用更高效的方式有针对性地解决?场景下的语境上下文是怎样的 ? Scene - Context
  • 方案权衡:要达成的目标是怎样的? 哪些质量因素是尤其重要的?要兼顾考虑哪些质量因素?可用资源如何? Goal - Quality Requirements - Available Resources

思想

纵观各类技术:基本思想是在不一样语境里反复使用着的。apache

  • 功能与灵活: 标识、标记/状态位、元数据;分离与解耦。
  • 时间复杂度: 二分、映射、分治与组合、分摊、缓存、缓冲;动态规划;预处理;空间换时间。
  • 空间复杂度: 位图、冗余编码、虚拟化;时间换空间。
  • 高效读:缓存优先于主存,主存优先于磁盘,缓冲优先于直接读写;减小磁盘读次数,减小更慢存储介质的读次数。
  • 可靠写:主存优先于缓存、磁盘优先于主存;提交/确认/回滚机制。
  • 提高响应:批量、异步、并发。
  • 可维护性:抽象(问题本质)、封装(实现隐藏)、复用(提高效率)、多态(加强灵活性)、组合(功能集成)。

灵活

灵活是指可以低成本、高质量地添加新功能和优化现有功能。

编程

数据组织与存储

  • 问题分析 -> 抽象 -> 提取出关键数据集 -> 数据范式与结构化 -> 数据 Shema -> 一致性与完整性 -> 组织与存取。
  • 数据特色:结构化、半结构化、非结构化;记录、文本、二进制流;
  • 数据关联:聚合关系(有意义的实体);对应关系(一对1、一对多、多对多);映射关系;泛化与特化(类似与相异);衍生关系(计算与推理)。
  • 数据变化:静态不可变、只变动一次、不多变动、频繁变动、瞬时变动不少次。
  • 数据操做:插入、查询、更新、删除(CRUD);过滤、排序、分组、聚合(FSGA)。
  • 业务驱动:业务 -> 问题 -> 视角 -> 数据 Schema -> 采集数据 -> 处理数据。

事务

事务是数据处理的基本保证之一。 ACID (原子性、一致性、隔离性、持久性)是事务保证的四个重要特性。

  • 事务类型:(带保存点)扁平事务、链事务、嵌套事务、分布式事务。
  • 事务的实现机制: redo 和 undo 日志、锁技术、MVCC 机制。
  • 缓冲池:为了保证读写数据性能,MySQL 读写提供了缓冲池(Buffer Pool,BP)。 事务开启后,数据更新数据先写入 BP 里,在提交事务后再经过后台线程按期同步到磁盘文件中。当事务执行中发生异常时, BP 的数据可能未能刷入磁盘,或者刷入了一部分,所以存在数据丢失或不一致的风险。这样,就须要在事务提交前记录 redo 日志和 undo 日志,这样就能在断电后从新执行事务保证数据不丢失,或者回滚事务保证数据原子性。在事务提交后将 redo 日志写入磁盘。
  • 原子性和持久性:原子性是指一个事务中的全部操做要么所有成功,要么什么都不作。持久性是指一个事务一旦提交即便断电也不会丢失。redo -- 记录事务执行过程当中对页的物理修改的日志(512 字节、块形式),用于保证事务的持久性,undo -- 记录事务更新操做的逆向逻辑的日志,用来保证事务的原子性。开启事务后,每次更新执行都会分别记录 redo 日志到 redo log buffer, 记录 undo 日志到 undo log buffer ,并将数据更新改动保存到缓冲池 BP ;提交事务后,经过后台线程同步到磁盘的 redo 日志文件和 undo 日志文件,并将缓冲池 BP 中的更新改动写入到磁盘中。若是事务提交以前断电或失败,则什么都不会发生;若是事务提交以后断电,则能够经过读取 redo 日志来重作事务;若是事务执行失败,经过 undo 日志中的回滚语句,完成回滚操做。
  • 隔离性:隔离性是指一个事务不能看到未提交事务的数据。隔离性经过锁技术和 MVCC 来实现。锁技术 -- 共享锁和排他锁,读读并行,读写或写写串行;MVCC -- 根据每行记录的隐藏列(建立时间版本号和修改时间版本号)来实现读写控制。
  • 一致性:一致性是指数据从事务前的一致状态转换为事务后的一致状态。事务完成后仍然知足数据的完整性约束。
  • Savepoint:通知系统记录指定执行点的状态集。当事务执行出错时,能够回滚到指定 Savepoint 的状态,而没必要回滚到事务前。好比旅行计划,是不能所有回滚的。扁平事务能够看作是只有一个 Savepoint (事务执行开始前记录)。 Savepoint 的编号是单调递增的。
  • 嵌套事务: 一棵事务树。只有顶层事务提交时,全部子事务才能提交;当父事务回滚时,其下的全部子事务都会回滚。子事务能够继承父事务的部分锁。嵌套事务可用于实现相关事务的并行性。

架构与编程

对于单个微服务而言,能够经过以下手段来加强灵活性:

  • 建模存储:数据模型、数据 Schema 、完整性和一致性约束定义。
  • 领域驱动:DDD 设计,稳定的精炼的可持续演进的领域模型,六边形架构,聚合根,充血模型。
  • 架构模式:分层、微内核+插件、PipeLine 、事件驱动、订阅-推送、Actor 等。
  • 柔性编程:应用设计模式、设计原则(SOLID, KISS)、组件化、持续小幅重构。

微服务

  • 单体应用的问题:直接经过访问 DB 来共用数据库,数据管理容易缺少明确 owner 而混乱,DB 设计变动难度高;缺少明确的域的划分,功能边界不清晰,重复功能实现和重复代码难以维护,业务耦合,依赖关系混乱;协做成本高,系统总体稳定性差。

  • 微服务:将单体应用分解为多个具备明确领域定义的业务子域,将每一个相对独立的业务子域实现成单独的微服务,微服务独立管理各自子域的问题,采用不一样的架构和方案来适配自身领域的问题,最终全部微服务集成起来完成总体应用功能。实现独立自治和发展、模块化、分工协做等。

  • 微服务要解决的问题是服务治理。主要包括:限流/熔断降级、配置管理、日志中心、监控预警、链路跟踪、故障隔离、动态扩容、分流发布、全链路压测、中间件支撑、团队组织架构适配与管理。基本解决方案: RPC 框架和统一应用框架接入。

  • 一个较为棘手的问题:应用一致性。解决方案有:分布式事务、消息补偿、对帐和自动修复。

  • 分布式事务解决方案:2PC,TCC,本地消息表(消息解耦)、消息事务。


RPC:Dubbo

  • Dubbo 官方文档已经给出了详细的解释和引读,推荐阅读。Dubbo 官网地址在文末参考文献中给出。
  • 核心抽象是服务。Dubbo 服务是经过 URL, ServiceConfig 来表示的。 URL 是服务的表示,做为配置信息的统一格式,全部扩展点都经过传递 URL 携带配置信息;而 ServiceConfig 是基于 URL 的服务的配置,包括与服务导出有关的全部信息。服务的导出入口在 ServiceBean.onApplicationEvent 方法里,即 Spring 容器初始化完成以后。
  • Dubbo 十层架构理解:整体可分为 RPC 服务和 RPC 通讯两部分。在 RPC 服务部分,首先是最接近于业务方的服务接口和实现层(Service),有了服务就须要服务的生产者和消费者配置(Config - XML 或注解),还须要服务的注册与订阅(Register - Dubbo, ZK, redis), 与之相配套的服务监控(Monitor)。一般裸服务是不够的,每每会有代理(Proxy - javassist,JDK)、负载均衡(LoadBalance - ConstentHash, LeastActive, Random, RoundRobin)和容错(Cluster)。在 RPC 通讯部分,须要协议层(Protocol - Dubbo, HTTP, Rest, Hessian, InJVM, Tether, Thrift Nova, RMI, WebService, Redis, 直连)、信息交换层(Exchange - 封装 Request-Response 语义)、传输层(Transport - netty, mina), 以及数据在网络和内存中的序列化和反序列化(Serialize - Hession2, Dubbo, FastJson, JDK)。
  • 扩展机制:Dubbo SPI ,核心类是 ExtensionLoader , 核心成员是 EXTENSION_LOADERS[Class, ExtensionLoader], EXTENSION_INSTANCES[Class, Object] 。带缓存的实现。
  • 序列化与反序列化:考虑跨平台、性能(占用空间及网络带宽传输)和兼容性。Hession2 -- 自描述序列化,跨语言、占用空间小,要注意子类和父类的同名变量在反序列化时有覆盖问题。 FastJson -- 序列化行为可定制、序列化后可读性好、容易调试、丢失类型信息。反序列化时容易有安全漏洞,敏感信息要么不序列化,要么加密后再序列化。

统一应用框架:Spring

  • IoC: Bean 生命周期定义和操做定义。定义一个包含多个阶段的生命周期,在每一个阶段能够设置一些钩子方法做为扩展点。
  • AOP: Pointcut - Advice - Joint - Aspect 。 接口用 JDK 代理,其余用 CGLIB 代理。JDK 代理将方法委托给目标对象,CGLIB 代理则经过加载并修改目标对象的字节码、继承并覆写目标对象的方法而实现。

分布式事务

  • 核心问题:1. 如何将分布式事务拆分为多个本地事务; 2. 如何组合多个本地事务,并尽量保证一致性。
  • 解决方案:2PC、TCC、本地消息表、消息事务、Sagas 事务模型。
  • 2PC:经过一个全局协调者来组合本地事务。分准备和提交两个阶段。准备阶段,协调者向全部参与者发送投票消息,询问参与者事务是否能够提交事务,参与者向协调者回复 OK ;提交阶段,协调者通知全部参与者提交事务并执行。若是有任一执行失败,则所有回滚。优势:尽量保证强一致性,牺牲可用性。问题:同步阻塞;协调者的单点问题;不一致问题,好比协调者只通知了部分参与者提交;任一节点失败致使所有回滚,代价巨大。
  • TCC:Try-Confirm-Cancel。对每一个操做都注册一个确认和撤销操做。Try -- 主要是对业务系统作检测及资源预留; Confirm -- 业务确认提交;Cancel -- 业务执行出错时执行撤销操做。TCC 自己实现简单、代价稍小,不过业务层要写不少补偿代码(有些场景可能难以作补偿),数据一致性弱于 2PC。
  • 本地消息事务:经过本地消息表来组合事务。将分布式事务拆分红多个本地事务,本地事务与本地消息表关联。当一个事务执行后,在本地消息表存储一条消息。业务操做和消息存储都放在一个事务里。另外一个本地事务消费这条消息,执行相应的事务操做。若执行成功,则修改这条消息状态为成功,事务均执行成功;若执行失败,要么重试直至成功(可支持幂等),要么修改消息状态为失败,回滚事务,并通知消息生产方进行回滚或补偿。须要处理事务级联问题、事务与本地消息表的耦合问题,须要封装好。
  • 事务消息:经过消息系统来组合事务。消息系统支持事务。
  • Sagas 事务模型:经过工做流引擎来组合事务。将长时运行事务拆分为多个短的本地事务,并经过工做流引擎来进行管理和执行。

分布式Trace

  • TraceId + Span + javaagent + 字节码修改。

消息系统

服务解耦一般采用消息队列。

  • 设计考量:及时性(消费延迟、准实时或批量处理)、可靠性保证、高吞吐量和动态吞吐量、数据格式、数据转换(ETL,ELT)、安全性(认证机制和加密传输,审计和追踪)、故障处理、耦合性和灵活性(临时数据管道、缺失元数据、末端处理)、Collection API 的开箱即用。
  • 设计要点:RPC协议、存储;消息投递、防丢,防重、堆积、广播、事务;最终一致性。
  • 能力特性:顺序、延迟、流控;批量、异步。

可靠性保证

可靠性保证:“至多一次”,“至少一次”,“仅有一次”。消息的可靠性保证,不只仅取决于消息系统,还取决于与消息系统集成的上下游一整套的可靠性措施。

  • 使用者角度:生产者和消费者的消息链接配置,消息链接组件可靠性(适配业务端的生产和消费速率);消息体尽量精简,保持在小体积(避免大致积消息在高并发下的发送 RT 上涨);避免消息里有大 JSON,防止 JSON 过长截断致使脏数据( 避免消息队列堵塞);消息消费后要 ACK(避免消息堆积);数据格式的一致性(避免消息生产者和消费者之间的升级耦合);异常重试消息发送或消费(避免数据丢失);幂等处理消息(避免资损问题);生产者写入消息的速度控制;消费者的消费速率保证。
  • 设计者角度:可靠生产,可靠存储,可靠消费。可靠生产 -- 消息生产确认;可靠存储 -- 复制、持久化,多副本,落盘后发送;可靠消费 -- 最少一次,确认机制、重试、幂等处理。
  • 网络角度:可靠传输协议 TCP,见高可靠部分

KafKa

  • 基本概念:Topic, Partition ; Producer, Consumer ;Broker ; Leader/Follower 。
  • 一个主题 Topic 能够写入多个分区 Partition,实现 Kafka 的数据冗余和伸缩性。生产者 Producer 能够为某种消息指定消息键,经过 Kafka 分区器为消息键生成散列值,从而写入指定分区,这样,一样的消息键能够写入同一个分区。写入分区时,消息会生成一个递增的偏移量。 消费者 Consumer 能够根据偏移量按消息生成的顺序进行消费。多个消费者能够共同订阅同一个主题,造成消费者群组。群组保证每一个分区只能被一个消费者使用。订阅同一个主题的不一样消费群组的消费是互不影响的。消费者经过轮询的方式处理从分区获取的数据。
  • Kafka 不须要消费者进行确认。消费者能够跟踪消费的偏移量(经过往主题为 _consumer_offset 的特殊主题发送消息)。当消费者崩溃或有新消费者加入群组时(相似服务器扩容或缩容),会致使分区的再均衡。消费者须要读取最后一次提交的偏移量并从这里开始从新工做。这样可能会有重复消息或丢失消息的几率。消费者须要自行处理偏移量。
  • Broker。一个独立的 Kafka 服务器称为 Broker 。Broker 处理生产者的消息,并提供给消费者服务。同时也承担集群控制器的角色。Kafka 经过 ZK 来管理 Broker 的关联。Broker 会注册到 ZK 的一个临时节点 /kafka/brokers/ids/ 上,当 Broker 崩溃、退出集群、长时间垃圾回收致使停顿时,Broker 会从 ZK 上断开链接,Kafka 就会获得通知,临时节点会被移除。控制器 Broker 会注册到 临时节点 /controller 上,其余 Broker 会建立该临时节点的 Watch 监听对象。
  • 消息投递能力:多生产者/多消费者、分批处理(批量投递消息)、顺序保证(按消息生产顺序投递)、保留期限(保留天数或消息量大小)和非实时读取、分区多副本。
  • 可靠性保证:分区多副本。复制系数(可用性与硬件存储的权衡)、分区副本在不一样的 Broker 上、Broker 分布在不一样机架、首领副本崩溃的权衡、生产者发送确认副本配置(acks=0,1,all, 丢数据、不一致与吞吐量的权衡)、生产者的消息错误重试(风险是消息重复)、消费者检测并读取消息偏移量。Kafka 只容许首领副本读取生产者数据,跟随者副本只负责保持与首领副本一致。不可用或不一致的情形 -- 跟随者副本不一样步而首领副本又崩溃的状况下。若是不容许不一致(好比银行),则不能容许不一样步的副本提高为首领副本。若是丢失数据可接受而不可用不能接受,则可提高不一样步副本为首领副本。

代理

  • 代理的目标是性能、路由、安全、透明、迟加载、隐藏复杂实现细节。
  • 代理技术:四层代理(IP+Port,LVS)、七层代理(IP+Port+Application,Ngnix)。四层代理性能更高,七层代理更灵活。
  • 代理模式:静态代理、动态代理。

代理模式

  • 参与者: 目标实例、代理实例、代理逻辑。 目标实例是已知的,须要代理的逻辑须要指定,代理实例须要生成。
  • 静态代理: 编写和目标实例具备相同行为的代理实例,并将对目标实例的请求转发给这个代理实例上。因为老是须要为目标对象手动编写代理实例,所以称为静态代理。静态代理容易理解,但不够灵活。
  • 动态代理:动态生成和目标实例具备相同行为的代理实例,并将对目标实例的请求转发给这个代理实例上。动态体如今能够为不一样行为的目标对象生成相应的代理实例,而不是手动去编写代理实现。经常使用动态代理有 JDK 代理和 CGBLIB 代理。
  • JDK 代理:经过 java.lang.reflect.Proxy.newProxyInstance + 反射机制实现。适合对接口代理。代理逻辑经过 InvocationHandler 接口定义,Proxy 将实现 InvocationHandler 的实例传入构造器,生成动态代理实例。代理实例的类继承自 Proxy 。Proxy 经过proxyClassCache 来管理 ProxyClass 和 ProxyFactory ,并在 getProxyClass0 的时候去缓存 ProxyClass 的信息。

HTTP代理

  • HTTP 代理就像客户端与服务器之间的拦截器。既充当客户端的角色,又充当服务器的角色。代理能够级联,组合使用。能够经过 Trace 方法和 响应头的 Via 首部来追踪报文途径的网关和代理(Via 有安全与隐私问题)。
  • HTTP 代理的做用:过滤(不宜内容)、访问控制与审计追踪(安全)、安全防火墙(安全)、流量监控(安全)、缓存(性能,下降网络开销和拥塞)、反向代理(性能)、内容路由器(增值服务)、转码与压缩(国际化与性能)、匿名(安全与隐私)、路由与负载均衡(稳定性)。
  • HTTP 代理的部署: 出口(LAN 出网点,过滤、安全)、入口(缓存与性能)、边缘(反向代理)、对等交换点(缓存与安全)。
  • 使用 HTTP 代理的方式: 浏览器配置、交换或路由设备拦截、修改 DNS 、重定向。客户端代理配置 -- PAC 文件(提供一个URI, 指向用 JS 写的代理自动配置文件,会动态计算适合的代理配置);自动代理发现 -- WPAD ,按顺序尝试 DHCP(动态主机配置协议)、 SLP(服务定位协议)、DNS Known Hosts、DNS SRV 等技术,自动发现合适的 PAC 文件。
  • HTTP 代理的问题及方案:客户端发给代理的 HTTP 请求报文里应当是包含主机名的完整 URI。但客户端并不老是知道对方是代理,或者并不知道代理的存在。所以通用代理须要进行“缺失主机名的部分 URI 补全”,拿到主机名拼成完整的 URI(没有代理时浏览器也会作相似的事情)。某些代理会对 URI 作细微修改,影响互操做性。代理的容错机制(解析到的主机是已停用服务器时)。
  • 代理认证:客户端发送请求,代理发现没有认证,会返回 407 响应码,客户端拿到 407 后搜索和拿到证书,重发请求,代理认证经过。

组件和配置化

  • 组件化: 配置化的基本前提。组件须要定义良好的行为规范和接口规范。
  • 流程编排:须要将整个流程划分为若干阶段,定义每一个阶段的行为和目标、阶段之间的链接。
  • 动态语言脚本。好比订单导出使用 Groovy 脚本配置报表字段逻辑。 脚本注意作成缓存对象,避免可能的内存泄漏。
  • 选项参数。选项参数的原型是命令行参数。用户能够经过选项参数来选择策略、调节性能等。
  • 规则引擎。 将业务逻辑表达为若干条规则,而后用工做流将规则集合串联起来。

发布

  • 分流发布:灰度发布、蓝绿发布。小批量验证。分流系数可动态配置和生效。
  • 容器化部署。

高性能

提高性能,便是用更少的资源作更多的事情。资源类型包括:CPU 时间片、内存空间、磁盘空间、网络带宽、链接池、缓存等。弄清楚应用程序依赖哪些资源类型以及依赖程度如何。

性能一般用 RT 和 吞吐量来衡量。 RT 是单个请求的处理时间, 吞吐量是指定时间内处理的请求数。应用也分为响应敏感型和吞吐量敏感型。好比订单详情,就是响应敏感型,RT 太高会致使超时,表现为服务不稳定; 而订单导出,则是吞吐量敏感型,单个请求处理长一点不影响体验,单位时间的吞吐量越高越好。

数据结构和算法的设计是性能提高的基本层面。并发是经过多个 Woker 并发或并行处理的思路。缓存是不一样读写速度的存储介质及淘汰算法的权衡。精简开销和参数调优更可能是针对热点和耗时进行细节上的调优。编译优化是语言层面的优化。高配服务器能直接提高性能,但成本较高。最后,性能测量必不可少。只有理论值是不够的。

高性能的核心:更少的 CPU 周期(数据结构、算法、内存计算),更少的等待时间(IO 读写、加锁与解锁、内核与用户态的切换)。

常见硬件性能:L1 cache (0.5ns) > 分支预测失败(5ns) OR L2 cache (7ns) > Mutex 加锁与解锁 (25ns) > 内存访问 (100ns) > 固态盘 SSD 访问延迟(0.1ms) > 机房内网络来回 (0.5ms) > 千兆网络发送 1MB (10ms) OR SATA 磁盘寻道 (10ms) OR SATA 顺序读取 1MB 数据 (20ms) > 异地机房网络来回 (30-100ms) 【来源:《大规模分布式存储系统》2.1.4 】

数据结构与算法

时间复杂度和空间复杂度。 在空间存储充足的状况下,一般考虑时间复杂度。固然,在移动端以及大数据存储方面,空间复杂度也须要仔细考虑。

  • 基本数据结构:数组、位图、链表、栈、队列、堆、表、哈希散列、二叉查找树、红黑树、DAG、JSON 。
  • 基本算法:分治、递归、动态规划、贪心算法;排序、查找;深度优先遍历、广度优先遍历。
  • 预处理思想:经过预排序、预索引、拓扑结构等构造特定的数据结构,以支持高效查找,好比 KMP, RETE ,倒排索引,都运用了这种思想。
  • 高效查找:有序查找、哈希查找。有序查找 -- 构建有序结构,好比有序链表、跳表、二叉查找树、红黑树,B+ 树,从而使用二分查找来提高查找效率,减小比较次数,查找时间复杂度 O(logn) ; 哈希查找 -- 构建哈希 key-value 映射结构,解决哈希冲突,查找时间复杂度 O(1) 。哈希查找须要仔细选择哈希函数(经过参数调优来提高性能),不支持 rank 和 select 操做(第 K 大元素),一般用于 K-V 结构的存储系统; 顺序查找的查找效率与哈希接近,支持 rank 和 select 操做,一般用于有序表、关系型数据库等。能够结合两种查找结构使用。好比 java8 的 HashMap 是哈希查找与顺序查找的结合。
  • 最优排序:快速排序、合并排序,时间复杂度 O(nlogn) , 根据特殊情形能够时间换空间或空间换时间策略得到更好性能
  • 空间效率:压缩算法(RLE、增量编码、哈夫曼编码、Rice编码、LZ77编码、位表示、Trie前缀树); snappy, gzip, lz4; 图像视频压缩(MLP、CNN、GAN)。
  • 生成全局惟一ID: snowflake 算法,生成 64 bit 的 long 型数值做为惟一ID。41bit (毫秒数) + 10 bit (5bit 数据中心 + 5bit 机器号) + 12bit (毫秒内的偏移量) + 1bit (=0)。
  • 判断 key 存在性 : 位图(稠密、不重复)、布隆过滤器,O(logn)。位图可用于稠密不重复数组的排序。
  • 动态变化的查找: 一致性哈希。环状队列 + 多哈希 + 虚拟节点。构建一个 serverMap = TreeMap[VirtualNodeHash, Server] 的有序映射。对数据进行哈希 h 后,在 serverMap 找到第一个不小于 h 的键 S,将该数据分布到服务器 serverMap[S] 上。哈希算法可采用 Fowler-Noll-Vo 哈希算法。一致性哈希的性质:平衡性、单调性、分散性、负载、平滑性。
  • 全文检索: 倒排索引。关键词分割、关键词所在的文档及位置的存储、词典索引构建与优化。
  • 动态规划:子问题求解、计算结果缓存与复用。要学会划分可复用的子问题,并理清楚子问题与原问题之间的关联。
  • 堆:排序 O(nlogn)、前 K 最大或最小值 O(n) 。可用于实现优先级队列。
  • 字符串匹配:朴素字符串匹配 - 双重循环,简单,低频场景;有限自动机 - 根据模式字符串构造有限自动机,再匹配文本,适用模式串的不一样字符数不多的情形; KMP - 根据模式字符串及构造后缀匹配数组(PMT,Partial Match Table,前缀集合与后缀集合的交集字符串的最长长度),避免无用位移测试,适用生产环境。
  • SkipList: 有序链表,空间换时间,经过在每一个节点上新增多级索引指向后继节点,实现跳跃查找,平均 O(logN) ,最坏 O(N) 查找效率;批量顺序操做;比平衡树实现简单。Redis 使用 SkipList 实现有序集合键。
  • 布隆过滤器: 使用多个哈希函数将一个值映射成多个哈希值,并投影到位图上,并置为 1。若是指定 key 经过哈希函数映射到位图上,有一个位为 0, 则必定不存在这个 key 。利用布隆过滤器减小磁盘 IO 或者网络请求。
  • 红黑树:平衡二叉树。最坏状况下查找效率 O(logn)。 2-3 树的变体。指向左孩子的指针为红色的节点标识 3-节点。左旋与右旋。
  • 哈希计算:主要基于整数或二进制位来计算。若是是浮点、字符串、IP地址、对象,先将其转换成整数型,再进行计算。最好能用到 key 里面全部相异的部分。常见的哈希函数有取模(模最好是素数)、MurmurHash2 算法(源码能够在 GitHub 上搜到)。能够编写一个程序,来检测哈希值的分布均匀度。若是哈希计算代价比较昂贵,须要作哈希值缓存。
  • 位操做技巧:通常用于求哈希值、高效实现特殊运算(好比 m mod 2^n == m & (n-1) )、状态位设置、节省空间等。
  • 字符串处理技巧:从尾部开始编辑,能够避免覆盖问题。
  • 链表技巧:快指针与慢指针。

容器

  • HashMap: 数组 + 链表(在必要时会变成红黑树)。 核心操做是 put, resize, get。线程不安全。并发下会发生数据丢失( put 更新 value 时)、死循环( resize 复制数据时)。
  • LinkedHashMap: 保持插入序的 Map 。节点采用双向链表。新插入节点或已访问节点移至链表尾部。线程不安全。
  • TreeMap: 红黑树实现。可顺序查找的 Map 。线程不安全。

分区

“【总结系列】互联网服务端技术体系:可扩展之数据分区”

数据库索引

“【总结系列】互联网服务端技术体系:高性能之数据库索引”

并发

“【总结系列】互联网服务端技术体系:高性能之并发”

缓存

精简开销

  • 热点分析,定位开销大的地方。好比订单导出的热点耗时区域在批量获取订单的详情内容上。
  • 移除没必要要:去掉没必要要的访问、去掉重复开销。好比获取订单详情时不须要的字段就不去访问相应的 API。
  • 精简链路:去掉重复调用,合并调用,调用结果缓存与传递。好比交易能够拿到商品信息并缓存、传给营销中心。
  • 无锁化:CAS 机制。去掉加锁和释放锁的操做耗时,增长了 CPU 轮询开销(两害权衡取其轻)。
  • 非阻塞IO:Select 和 Epoll 机制。减小没必要要的线程切换和数据拷贝。

非阻塞IO

  • 阻塞IO:须要为每一个 Socket 创建线程(线程数量有限),建立大量线程(空间开销大),线程容易被IO阻塞等待(利用率低),大量的线程切换开销(时间耗费在与业务无关的事情上)。
  • 非阻塞IO:IO 多路复用思想;Select机制、Epoll 机制。
  • Select 机制 : Buffer - Channel - Selector 。 一个线程能够管理多个 selector ,每一个 selector 能够轮询监听多个 Channel 的事件并读取或写入数据,每一个 channel 与一个 Buffer 相连,提高 IO 读写的效率。select 相对于阻塞IO有进步,但仍然有缺点:1. 每次 select 都须要将 fd 集合从用户态拷贝到内核态,并在内核态遍历全部的 fd ;fd 越多开销越大; 2. 一个进程支持的 fd 集合大小受限,默认是 1024 ; 3. 若是没有 fd 处于就绪状态,select 会阻塞。
  • Epoll 机制:三个函数 --- epoll_create ,epoll_ctl,epoll_wait 。epoll_create 会建立一个 epoll 实例, 其中包含一棵红黑树 rbr 存储须要监控的 fd 集合,双链表 rdlist 存储返回给用户的知足条件的事件; epoll_ctl 向 epoll 实例注册给 fd 要监听的事件;epoll_wait 等待内核返回监听 fd 的事件发生,返回已就绪的事件及数量。Epoll 有 LT 和 ET 两种工做模式。LT --- 检测到 fd 的事件就绪通知应用程序后,能够暂时不处理,事件放回就绪链表中,待下次调用 epoll_wait 时再通知;ET --- 必须当即处理。 Epoll 的优势: fd是共享在用户态和内核态之间的(mmap技术),不须要将 fd 在内核态和用户态之间拷贝,不须要遍历就能够得到就绪的 IO 事件。一个进程支持的 fd 集合大小只受操做系统限制。
  • Netty: ByteBuf - Channel - ChannelEvent - EventLoop - ChannelPipeline - Channel Handler - Callback - Channel Handler Context - ChannelFuture 。Channel 是数据流 ByteBuf 的出入通道,网络链接和数据事件发生的起点。当 Channel 的某个事件 ChannelEvent 发生时,EventLoop 会轮询 ChannelEvent 已经注册的 Channel Handler ,并执行 Channel Handler 对应的回调函数 Callback。多个 Channel Handler 能够构成一个 Channel Pipeline。 Future 提供了异步返回结果和通知的方式。Future 能够注册 FutureListener ,从而在 Future 执行完成时回调 FutureListener 的方法 operationComplete。一个 Channel 在其整个生命周期里只关联一个 EventLoop 且它的全部 ChannelEvent 都由这个 EventLoop 处理。EventLoop 是 netty 的事件并发执行模型。
  • Redis 多路复用: evport, epoll , kqueue , select

HTTP2优化

  • HPack 压缩首部算法,经常使用首部作成静态表映射获取, 哈夫曼编码。
  • 借鉴 TCP ,数据划分为更小的二进制帧进行传输;
  • 多路复用,单链接多流并行;
  • 服务器主动推送相关资源,而非浏览器发送屡次请求,减小 TCP 链接耗时;
  • 应用层重置链接和请求优先级;
  • 流量控制。

参数调优

  • JVM 调优
  • 应用配置参数调优。首先提取影响系统功能、性能、稳定性、可用性等的重要因子,而后经过配置平台来管理。参数配置能够包括策略选择、超时设置、重试次数、批次处理数、限流因子等。好比订单导出的策略选择有报表维度(商品/订单/商品订单均要)、文件上传下载维度(本地服务器或云存储); 订单导出并发批量拉取订单详情,能够配置分批处理的订单数,并发拉取的订单数,每批次订单处理的时间间隔、超时重试次数等。

服务指标测量

在工程上,测量是尤其重要的。理论只是给出了定性值,而测量将给出更精确的量化值。

  • 基本指标: 单机 RT 和 QPS。运行屡次单个请求,取请求响应时间的平均、峰值、最小值、百分位数。平均值即为 RT 值。QPS = 1 / RT(ms) 。
  • 并发指标: 并发数。经过压测来测试并发负载能力。

高可靠

分布式

分布式经过异构、冗余实现容错和扩展能力。

  • CAP:一致性、可用性、分区容错。 一致性要求不高的,一般采用 AP; 资金敏感的,一般采用 CP。
  • BASE: 基本可用,软状态、最终一致性。牺牲强一致性来得到可用性。
  • 协议:Paxos,Raft

选举算法

选举算法是分布式系统的基础。许多分布式都是基于 Master-Slaves 或 Leader - Followers 模式。通常 Leader 负责写数据,而 Followers 负责同步写入的数据到本身的副本上保持与主一致。

  • 选举算法:Bully 算法、FastLeader 算法(ZK)。Bully -- ID 最大的做为 Leader ,有频繁换主的风险。

选举机制的组成部分:

  • 选举轮次标识:时间戳或编号,防止过时选举消息干扰;
  • 选举检查项:好比服务器状态;
  • 选举依据:根据什么来比较,判断谁应当成为 Leader ;
  • 投票统计:每一个服务器都投出本身的一票,并接收别人的投票作某种处理,和本身的投票来作统计;
  • 选出 Leader : 根据统计结果,大部分服务器都能达到共识,Leader 从共识中产出。

ZK

  • 实现分布式协同服务。CP 模型、ZAB 协议、ZNode、树形结构、Watch 通知。 ZK 只能存少许元数据,不适合作分布式存储系统。
  • ZK 的数据结构:节点以树的形式组织起来。节点分 持久/ 临时节点,有序/无序节点。
  • ZK 服务器数最好为奇数个:1. 偶数个只容许更少服务器崩溃,更脆弱; 2. 防止脑裂现象。
  • ZK 以事务的方式执行全部操做,并确保全部事务以原子方式执行,不受其它事务的干扰。ZK 事务具备原子性和幂等性。zxId 做为事务的标识。zxId 是 64 位的值,分为 32 位时间戳和32位的计数器。32位时间戳能够用于识别当前时段的 Leader 和事务时间戳。
  • 选举算法: 每一个服务拥有一个 Eo (sId, zxId) ,并向其它服务器发送本身的 Eo (sId, zxId) 。 收到 Er 的每一个服务器将 Er 与 Eo 进行比较。若是 Er.zxId > Eo.zxId 或者 Er.zxId = Eo.zxId AND Er.sId > Eo.sId ,则用 Er 替换 Eo 。最终大部分服务器都会拥有相同的 Er。从而选举出具备 Er 的那个服务做为 Leader 。
  • ZAB:S1- Leader 向全部 Follower 发送一个事务的 Proposal 消息 P; S2- 每一个 Follower 收到消息 P 后响应一个 ACK ,表示赞成该事务; S3- 当仲裁数量的服务器发送 ACK 消息后,Leader 将通知 Follower 进行提交事务。

容错

  • 异常:磁盘故障、服务器宕机、网络丢包、网络分区、RPC超时、脏数据、链接池满、内存耗尽。
  • 发生频率:磁盘故障 > 服务器单机故障 > DNS 故障 > 机架故障 > 路由器重启
  • 可预见错误处理、异常捕获、规范定义的错误码和错误消息。
  • 快速失败(无幂等的写操做)、切换服务器重试(有幂等的读)、失败忽略并记录日志(不影响流程和总体读)、补偿重试(非关键数据写入、后台任务)、并行调用(一个成功便可)、广播调用(只要一个失败就失败)。
  • 幂等方案:惟一索引、乐观锁、Token 机制(防页面重复提交)、分布式锁、select+insert、状态机幂等、查询/删除自然幂等。
  • 重试:指定重试次数、定时任务重试
  • 补偿:失败队列、补偿任务。

健壮性

健壮性是极为重要的程序质量属性。分为代码健壮性和业务健壮性。健壮性体如今代码和业务上的错误和异常处理上,避免总体失败,避免数据泄露、不一致、资损等故障。要作出健壮性好的设计和程序,就要预先思考清楚:流程中有哪些可能的错误和异常,每一种对应的处理措施是什么 ? 这样,才能让逻辑思惟更加缜密,也是锻炼逻辑思惟的一种有效之法。

  • 代码健壮性体如今避免局部失败致使总体失败。常见考虑:参数校验以拦截不合法请求、越界异常捕获、JSON 脏数据异常捕获、类型转换异常捕获、底层异常捕获(链接异常、DB 异常、网络超时异常等)。
  • 业务健壮性体如今业务的闭合环。在整个业务过程当中会发生什么异常,致使什么问题(体验或资损问题),如何处理。好比同城异常检测要考虑商家又快递发货的情形。

TCP可靠传输

  • 网络的不可靠性: IP 数据包的丢失、重复、失序。
  • TCP 可靠机制:全双工链接、字节流、数据包有序编号、数据校验和、确认、定时器与超时重传、重复丢弃、流量控制(滑动窗口机制)、拥塞控制。
  • TIME_WAIT :TCP 链接关闭的过程当中,客户端在发送服务端 FIN 包的 ACK 以后,进入 TIME_WAIT 状态,须要等待 2MSL 才能关闭。这是为了确保 ACK 可靠到达服务端。
  • TCP 重传机制: 超时重传和快速重传。超时重传依赖于超时时间的设定,快速重传依赖确认包的接收。能够说,选择不一样的依赖因子决定了不一样的解决途径,也就有不一样的优势和缺点。
  • 超时重传机制:TCP 发送数据包后,会启动相应的定时器,若是超时 RTO 尚未收到数据确认包,就会从新发送该数据包。超时 RTO 的选择很是重要。若 RTO > RTT, 则可能网络利用率低,若 RTO < RTT 则可能拥塞。RTO 的选取基于 RTT 的统计及 BEB (二进制指数退避算法),重传次数及重传最大超时时间(RFC1122)。 RTO 估计: EWMA RTT 权重估计法(RTO = min(ubound, max(lbound,(SRTT)β)), RTO = (1-2)*SRTT, SRTT ← α(SRTT) + (1 − α) RTTs, α 取 80%-90%, β 取 1.3-2.0),标准估计法(查阅相关文献)。TCP 能够记录每一次的链接和传输,做为性能调优的依据(srtt 和 rttvar),能够作成自适应的。 TCP 重传时,能够将多个数据包重组起来一块儿发送(总大小不能超过 MSS 和 MTU),以提高网络传输效率,也能够减轻重传数据包的二义性。因为 RTO 一般至少是 RTT 的 2 倍,所以超时重传机制容易致使网络利用率不高。
  • 快速重传机制: TCP 收到失序数据包后会当即发送确认包,意在告诉发送方迅速发送须要的数据包以填补“包漏洞”(可能存在包丢失)。若是有 dupthresh 个包从新确认了,则极可能存在包丢失,将启动快速重传机制,而不是依赖超时。也就是说,依赖确认包的快速发送和重复确认包来决定是否重传。SACK (选择性确认)用来解决数据包重复和失序的问题,老是发送最近收到确认的序列号的范围值。发送者能够推理出最须要发送的未确认的包,并优先发送这些数据包。可使用位图来检测没有收到的确认序号。快速重传更高效,不过会产生误重传问题。

故障检测与恢复

  • 故障检测主要靠监控。服务器监控(CPU、内存利用率、Load、IO RW、Net RW)、服务监控(RT、QPS、消费速率
    、延迟、网络链接;来源、TOPN)、Java 监控(ThreadPool、Heap、GC 等)、异常监控(失败次数、失败比例;超时,消费堆积或不均)、业务监控(瞬时峰值、瞬时下跌、同比上涨或下跌、大数据对象)、对帐监控(数据一致性检测,尤为资金相关)。
  • 心跳机制:心跳机制用于故障检测。每台工做机每隔指定时间将本身的CPU、内存、磁盘、网络、IO读写、负载等状况上报给 Master, Master 会判断服务器是否正常,以决定是否分发流量给该服务器。每台服务器与 Master 有个超时设置,若是达到超时没有收到该服务器的心跳信息,则会判断服务器出现问题。心跳机制须要 KeepAlive (链接最大空闲时间) 属性,使用 MQTT 协议。
  • 租约机制:每台工做机向 Master 申请具备必定租约期限的服务时间。当服务时间快到时,再向 Master 继续申请延长租约的有效期。若是由于网络或工做机故障致使租约没法响应,则会将该工做机隔离,再也不提供服务。
  • WAL(Write Ahead Log): 预写日志,恢复日志。基本机理是复制状态机、备忘录、备份。关系数据库系统中用于提供原子性和持久性。in-place 和 shadow paging 。提高磁盘写性能:随机读写改顺序读写、缓冲单条读写改批量读写、单线程读写改并发读写。同步 WAL 文件和数据库文件的行为被称为 checkpoint(检查点)。实现方法:DB - undo, redo 机制;ES - fsync 机制;ZK - 先写 WAL,再更新内存,最后通知客户端;按期将内存中的目录树进行 Snapshot,落磁盘; ETCD - wal 和 snap 目录; HBase - 更新数据前写 WAL, 且写 WAL 与数据更新在同一事务。
  • 检查点(CheckPoint):系统按期将内存状态以检查点文件的形式dump到磁盘中,并记录检查点时刻对应的操做日志回放点。 检查点能够快速提高故障恢复的速度。
  • 备份。备份是应对数据故障(丢失、不一致)的重要保障。备份只是实现数据的恢复,并不能彻底实现故障恢复。故障恢复是指服务或服务器回到正常可用状态。

MySQL备份

  • 逻辑备份和物理备份。逻辑备份是可读文件、恢复简单灵活(恢复工具及选项)、在不一样机器上运行、与存储引擎无关、避免物理环境致使的数据损坏,但恢复时间较长,须要 MySQL 来完成、须要测试恢复;物理备份一般是不可读的二进制文件,恢复更简单(只要拷贝文件到目的路径)、恢复时间很短,但 InnoDB 的原始文件一般比逻辑备份的文件更大。两种方式可混合。

高可用

可用性策略

  • 可用性评估:平均失效时间(MTBF)和平均恢复时间(MTTR)。不可用时间 -- 99%-5256m,99.9%-526m,99.99%-53m, 99.999%-5m。得到可用性与投入成本是非线性的。越高的可用性须要越高的成本,所以评估应用所需的可用性是必要的。提炼应用的核心模块,对更小的系统提高可用性成本会更小。使用风险敞口来评估优先考虑的可用性需求。风险敞口 = 失效几率 * 失效损失。
  • 宕机缘由分析: 1. 运行环境,磁盘故障居多; 2. 性能问题,大流量杀手;3. 软件 bug; 4. 变动管理操做出错。
  • 高可用策略:思路 - 避免致使宕机的主要缘由、快速恢复故障。三大措施 - 限流、降级、冗余。方法层面 - 避免单点失效、冗余和故障转移。执行层面 - 主备、对称布局;评估组件切换时间和故障恢复时长(估算和演练);备库演练(记录切换时的工做负载);虚拟IP、代理、端口转发、NAT;运维操做安全准则、故障演练。

限流

大流量杀手是形成故障的一大主因。限流措施是针对大流量的。

  • 计数器算法:实现简单。有流量不均衡、临界大并发问题。
  • 滑动窗口算法:切分时间窗格,分而治之。可解决临界大并发问题。切分窗口足够细,才能作到精确控制限流值。
  • 漏桶算法: 桶的容量恒定,以固定速率流出。若是流入数量超过桶的容量,则丢弃请求。桶能够用有界队列实现。控制出口,能够用于削锋和平滑流量。如有短期的突发流量,因为处理速率恒定,可能致使大量请求被丢弃。能够用“漏桶+缓冲区”的组合,多出来的就放在缓冲区里,待漏桶处理完后再放入。
  • 令牌桶算法:得到令牌做为业务可执行的许可,桶做为限流的容器。以固定速率增长令牌值,直到令牌桶的最大值。获取令牌则可执行,获取不到则阻塞或直接返回。使用一个有界阻塞队列保存令牌,一个定时任务用等速率生成令牌放入队列,访问量进入系统时,从队列获取令牌再进入系统。令牌桶能够作到最大流量控制,不过不能作到固定流速输出。

RateLimiter

RateLimiter 是一个限流器,通常用于瞬时大流量场景下对请求流量进行指定速率的限制,将流量速率控制在应用可以处理的范围内,保持应用的稳定性。RateLimiter 接受每秒 N 个请求,N 是可配置的。RateLimiter 的思路是:

  • 因为要知足在指定时间内的速率限制要求,请求来临时间点及请求申请许可数的不肯定性, RateLimiter 要作的是对过去请求及所消耗时间的建模。
  • 基本逻辑:若是 QPS = 5, 那么申请一个许可的每两个相邻的请求相隔 200ms ( stableIntervalMicros )。若是已经够了,则要当即处理;若是不够,则要等待知足 200ms 再处理。申请 N 个许可的当前请求与最近一个请求应该相隔 N * 200ms 。
  • “Past underutilization” :空闲时段的许可建模,使用 storedPermits 来表达。Past underutilization 意味着过去一段时间请求量很是少,利用率低,且服务器可能须要预热(好比创建链接、加载缓存)处理。storedPermits 用来表示在过去一段空闲时间内储存的令牌数(能够当即使用)。对于每一次申请许可的请求,RateLimiter 从两个地方获取许可:1. storedPermits ; 2. fresh permits 。
  • 对于每一次申请 permits 的请求,老是须要根据上一次计算获得的 nextFreeTicketMicros 来计算这次须要等待的时间 microsToNextFreeTicket (delay of current request) ,若大于 0 则要休眠。

RateLimiter 支持两种方式:带热身的 warmUp 方式和支持突发流量的 Bursty 方式。默认 Bursty。

  • RateLimiter 的计算是以微秒 toMicros 为单位的。限流计算主要是关于时间和数量的。RateLimiter 在初始化时会启动一个计时器,全部的时间都是相对于启动时刻点的相对纳秒数。
  • resync: 若是当前请求姗姗来迟(当前时间点迟于上一个请求计算获得的 nextFreeTicketMicros ),则计算能够储存的令牌数 storedPermits。这个方法完成了空闲时间建模。
  • reserveNextTicket:核心方法。根据上一个请求计算的 nextFreeTicketMicros 来计算当前请求须要延迟的时间以及下一个请求的必须等待的时间点 nextFreeTicketMicros。因为 RateLimiter 采用的是预消费模式,所以,每个当前请求会受到上一个请求的执行时间的影响。
  • storedPermitsToWaitTime:从 storedPermits 获取所须要的 permitsToTake 须要等待的时间。对于 Bursty 来讲是 0, 对于 warmUp 来讲是经过函数计算的(从 halfMaxPermits 到 maxPermits 的一条倾斜直线,斜率 slope = 2*stableIntervalMicros/halfMaxPermits )。这是两种限流方式的最主要区别。
  • maxPermits: Bursty 经过指定秒数来指定最大 maxPermits, maxPermits = secs * permitsPerSecond; 而 warmUp 经过热身时间来指定最大 maxPermits, maxPermits = warmUpTime * permitsPerSecond.
  • RateLimiter 支持并发。使用了 synchronized(object) 的方式,经过减小持锁逻辑增大并发度。

熔断降级

某个基础服务的不可用,可能致使雪崩效应(整个基础设施的级联性故障)。降级的目的:1. 防止雪崩效应; 2. 防止局部次要失败影响总体可用性。

  • 思路:快速失败和 Fallback 机制。
  • 断路器设计模式:检查故障并封装逻辑来阻止在系统维护期间、外部系统暂时出现故障期间和意外系统故障期间,错误反复不断地产生。断路状态机:CLOSE - HALF OPEN - OPEN。断路器使用称为 HALF-OPEN 状态的监视和反馈机制来了解 Supplier Mircoservice 是否以及什么时候恢复。 它使用这种机制按期对 Supplier Mircoservice 进行尝试调用,以检查它是否已经恢复。 若是对Supplier Mircoservice 的调用超时,则断路器保持在 OPEN 状态。 若是调用返回成功,则电路切换到CLOSED状态。 而后,断路器在 HALF-OPEN 状态期间将全部外部调用返回到服务并发生错误。与命令模式结合使用。断路状况:失败百分比、失败绝对次数、强制直接熔断。
  • 请求拦截(AOP)、消息队列、特征识别、实时计算与分析、反馈式熔断和快速返回
  • 熔断实现原理:STEP1- 解析熔断配置,构造请求(方法签名)与 fallback 的映射关系 FallbackMethodMap[Method, FallbackConfig],其中 FallbackConfig 包含 fallbackMethod 及调用该方法的失败阈值配置;STEP2- 作一个 AOP,使用 ConcurrentHashMap[Method, FailureCount] 收集请求(方法签名)及请求失败结果,当该请求的失败统计 FailureStat 匹配指定配置阈值 FallbackConfig 时,就触发熔断,调用 fallbackMethod 。
  • 断路器的负载问题:1. 断路器可看作是一个高可用的微服务,其做用是统计应用成功或失败的情形,并给出合适的策略;2. 能够作成自适应的。记录最近若干次调用状况,若是均是成功,则不调用断路器;只有当调用出现异常时,才开启断路器模式;3. 断路器是锦上添加的做用,即便断路器失效,也不该当影响服务的正常调用;4. 断路器能够持续监听服务的状态,在业务应用中加一个依赖服务的标志位 Map,当某个服务出现问题时,断路器能够推送消息给该应用,设置标志位。这样,能够异步去设置断路,而没必要每次都调用一次断路器。
  • 滑动窗口机制:将统计时长切分为多个桶,多个桶能够聚合成一个滑动窗口。

冗余

冗余是保证可用性的基础。

  • 冗余是指一份数据有多个副本存储在不一样的节点上。冗余是应对容错和提高可用性的重要机制,同时又会带来一致性问题。
  • 副本协议:中心化副本协议,primary-backup 协议、去中心化副本协议。主节点写入成功后,发送操做日志给指定节点或所有节点的副本,指定节点或所有节点根据操做日志写入成功后回复主节点,主节点回复写入成功。
  • 一致与可用:强一致复制 -- 若是全部副本写入成功,主节点才返回客户端成功,则是强一致同步复制。但若是某个副本失败,则总体写入失败,此时没法达成可用性。弱一致复制 -- 只要主副本写入成功,就返回客户端成功,经过线程异步发送给其余节点去更新副本,则是弱一致同步复制。某个副本写入失败,不影响总体写入的结果。此时,能够达成可用性,但没法达成强一致性。
  • 多个副本不能所有放在同一个节点或机架上。不然,节点或机架故障时,数据会丢失,没法达成可用性。

数据拷贝

  • 数据拷贝是指数据在两个端点之间进行传输。复制是带有策略和目标的大规模数据集的可靠拷贝。互联网从拷贝起航,通往复制。
  • 赋值:赋值是数据在内存中的两个变量之间进行拷贝。对于嵌套结构,赋值可分为深拷贝和浅拷贝。浅拷贝只拷贝数据的引用(引用两份,数据一份),引用的数据是共享的;深拷贝则是建立了彻底相同的数据,即存在两份一样的数据,引用的数据是隔离互不影响的。
  • IO:数据在网络/磁盘和内存之间传输。Linux 将磁盘、网络等设备抽象为文件,从而可以用统一的方式来处理 IO。 要访问一个设备时,内核返回一个非负整数的文件描述符(进程中当前没有打开的最小描述符),用来操做这个对应于设备的文件。内核中保持一个偏移量,用来定位读写到的文件位置。一个打开的文件包括三部分:描述符(文件的引用,进程专享)、文件表项(文件位置、引用计数等,全部进程共享)、v_node 引用项(指向全部进程共享的 v_node 表,存放文件元数据)。IO 要考虑效率,使用缓冲机制、 IO中断和 DMA 技术来实现。
  • 缓冲机制。数据先从网络/磁盘拷贝到内核的数据缓冲区,再拷贝到用户程序空间的数据区。反之亦然。用户程序只与操做系统的数据缓冲区打交道,而与磁盘传输解耦。还有一种直接 IO 的方式,应用程序不通过内核缓冲区来传输数据,不过也须要本身去设计一套缓存机制,好比 DBMS 。
  • I/O 中断与 DMA:PIO -- CPU 发出 IO 请求,磁盘将数据放入磁盘缓冲区,而后向 CPU 发出中断信号,CPU 将数据从磁盘缓冲区拷贝到内核缓冲区再拷贝到用户缓冲区,全程须要耗费 CPU 资源,可能致使系统在 IO 读写时无响应。DMA -- 磁盘将数据放入磁盘缓冲区后,系统主内存与磁盘或网络之间的数据传输能够绕开 CPU 的全程调度,DMA 会与外部设备交互,完成数据在外部设备和内核缓冲区之间的传输。
  • 零拷贝技术:即便是 DMA ,也会多一步内核缓冲区与用户缓冲区之间的传输开销,耗费 CPU。零拷贝技术是为了不这层开销。主要经过 mmap 来实现。 mmap 是基于虚拟内存管理实现的。mmap 的目的是将内核中读缓冲区(read buffer)的地址与用户空间的缓冲区(user buffer)进行映射,从而实现内核缓冲区与应用程序内存的共享,省去了数据在内核读缓冲区(read buffer)到用户缓冲区(user buffer)之间的传输过程。
  • 健壮性。 IO 读写会遇到不足值(传输字节数比所须要的少)的问题。磁盘 IO 遇到不足值一般是 EOF,而网络传输考虑缓冲约束和延迟,要频繁处理不足值问题。这就须要一个健壮读写 IO 的系统函数 RIO。
  • IO SDK。磁盘读写首选标准 IO, 网络读写首选 RIO。标准 IO 和 RIO 都难以知足需求时,使用系统 IO。

复制机制

  • 分布式冗余机制:数据分片存储、冗余副本、宕机检测、自动副本迁移、多余副本删除。
  • 复制拓扑:主从 Leader - Followers 或 Master - Slave ; 多主 Multi-Leaders ; 无主 Leaderless 。大多数采用的是主从拓扑。
  • 复制依据:复制一般要根据某个相似日志文件来进行。MySQL 基于 binlog 日志,Redis 基于 redis 命令的快照文件。 通常是依据数据变动文件。能够基于语句、二进制日志、逻辑日志、WAL。
  • 复制方式:同步或异步。同步能够保证至少有一个 backup 可用,但会阻塞写操做。因为复制节点的不可靠、网络延迟等可能致使复制节点回复时间长,所以,复制操做一般采用异步机制。或者采用半同步机制(指定部分复制节点回复 OK 就返回给用户)。复制能够自动配置、基于条件触发。基于应用代码的复制能够更加灵活可控。
  • 复制步骤:历史数据复制和增量复制。STEP1 -- 首先 Leader 生成一份某个时间点的快照文件,发送给 Follower , Follower 执行快照文件里的全部操做变动; STEP2 -- Follower 链接 Leader ,请求自快照生成时间点以后的全部数据变动请求,并以此处理后,就与 Leader 的数据保持一致了。

复制延迟
复制延迟会致使主从节点的数据在一段时间内的不一致,这种不一致会给用户带来困扰。在实现最终一致性时,须要评估复制延迟对系统的影响,并判断须要做出哪些一致性的保证。

  • Read-After-Write :写数据后要求当即能读到。方案:1. 能够检测数据是否被修改过,若修改过读主,不然读从。能够经过 profile 信息来记录和判断是否修改过; 2. 监控主从延迟。 在延迟内读主,延迟外读从; 3. 客户端记录最近更新的时间戳,服务端能够根据这个时间戳决定由主仍是由某个已更新的历来响应读。 跨设备或跨数据中心的 Read-After-Write 一致性须要考虑更多的因素,好比节点的时钟不一样步。
  • Monitonic Reads:复制延迟可能致使在不一样的 Followers 上读到的数据不一致。好比用户反复刷新页面时,在一个已更新的 Follower 读到最新数据,随后在一个没有更新的 Follower 读不到数据,就像时间回退了同样(Moving Backward in time)。方案:保证同一个请求被转发到同一个 Follower,好比使用 ID 的哈希映射到特定节点上。
  • Consistent Prefix Reads: 复制延迟可能致使果在因以前看到。

复制容错

复制容错主要是基于分布式容错机制。

  • Follow Failure : Follow 会持续保存一份读取从 Leader 接收的 replica log ,请求自 Failed 的时间点以后的数据变动,并执行到所在节点。
  • Leader Failure: 首先要基于一致性协议选择一个新的 Leader ,并配置使用新的 Leader 。客户端的写请求会发给新的 Leader 。Leader Failure 须要考虑的问题 -- 判断原来 Leader 宕机的超时时间选择; 新 Leader 不必定跟上了原来 Leader 的数据更新;有多个新 Leader(脑裂)。

MySQL复制机制

  • 复制用途:多数据中心的备份、密集读操做的负载均衡、避免单点失败、故障切换、升级测试。
  • 复制机制:基于语句的复制和基于行的复制。MySQL 复制的基础是 binlog 日志。在主库记录 binlog 日志( SQL 或 被更新的数据记录),在备库重放日志的方式实现异步的数据复制。能够将读操做指向备库,从而加强读操做的可扩展性。复制对主库的开销:读取二进制日志、锁竞争、主库处于高吞吐量时。
  • 复制步骤:STEP1-主库在每次准备提交事务完成数据更新前将数据更新的事件记录到 binlog 中。按照事务提交的顺序来记录。记录 binlog 后,通知引擎提交事务;STEP2-备库 IO 线程将主库的 binlog 复制到本地的 relaylog 里(备库起IO线程-与主库创建客户端链接-主库起特殊的二进制转储线程读取主库的二进制日志,备库IO线程将二进制日志读取到 relaylog 里);STEP3-备库 SQL 线程从 relaylog 里读取事件并在备库执行。
  • 复制方式:基于语句的复制 --- 实现简单、二进制日志紧凑,占用存储和网络带宽不多、操做灵活、容易定位问题。缺点是:语句执行是否成功及快慢、结果可能依赖环境因素、使用触发器或存储过程的语句的复制可能失败、执行代价很高但只选择或更新不多行的语句复制成本高、只能串行须要加更多锁。mysqlbinlog 工具是基于语句复制的好工具。基于行的复制 --- 将实际数据记录在二进制日志里。适用场景普遍、能够正确复制每一行、执行代价高但更新少数行的语句更高效复制。缺点:有些简单语句的更新行数多,二进制日志不少,增长主库的负载;没法判断执行了哪些 SQL,难以排查问题。默认基于语句的复制。
  • 复制拓扑:一个主库能够对应多个备库,一个备库只能对应一个主库且必须有惟一的服务器ID(惟一ID用于打破一些复制无限循环)。备库能够用于 --- 不一样角色使用、待用主库、容灾、数据恢复、培训测试等。经常使用推荐拓扑:一主多备、主动-被动模式下的主-主复制、主库-分发主库-备库、树形。
  • 复制方案:选择性复制-分库分表复制-水平数据划分、OLTP 与 OLAP 功能分离、备库上实现数据归档、日志服务器。
  • 主从复制延迟: 1. 主库并发,从库单线程,经过 sharding 打散或升级 Mysql 5.7 开启基于逻辑时钟的并行复制; 2. 主库大事务拆分为小事务,大语句拆分为小语句; 3. 对大表执行 DDL,找到阻塞的 DDL 并干掉; 4. 缺少主键或惟一索引,检查表设计,确保有自增主键和合适索引; 5. 主从配置不一致,尽可能统一配置; 6. 从库压力过大,分解为多个从库。

Redis复制机制

  • 持久化:快照和 AOF 。
  • 快照机制:使用 BGSAVE 建立快照。 BGSAVE 是异步的,会 fork 子进程来写入硬盘,父进程仍然处理请求。SAVE 是同步的,没法在建立快照的同时响应命令。快照存在丢数据的几率,适合于非核心数据场景,丢失数据也无损失的。配置 save timeInSecs writingTimes 表示从最近一次建立快照开始,在 timeInSecs 秒内若是有 writingTimes 次写入,则会使用 BGSAVE 建立快照。若是有多个 save 配置,则任一个知足都会建立快照。
  • AOF机制。 Redis 将执行的命令写入到 AOF 的末尾,记录数据发生的变化。只要自某个起始点从新执行 AOF 文件中包含的全部写命令就能够恢复AOF 所包含的数据变化。选项有 appendonly (yes or no) 和 appendsync (always, everysec, no)。always 会最小化数据丢失的几率,但会对 Redis 处理命令的性能有很大影响。通常推荐 everysec 选项。AOF 机制要解决 AOF 文件膨胀的问题。能够发送 BGREWRITEAOF 命令来合并 AOF 中的冗余命令,减小 AOF 文件大小。BGREWRITEAOF 也是起一个子进程来异步执行。BGREWRITEAOF 的配置选项:auto-aof-rewrite-percentage (大小膨胀到原来的百分比) 和 auto-aof-rewrite-min-size (大小膨胀到最低多少 MB)。
  • 一主多从。从服务器用来减轻读请求对主服务器的负载压力。主从复制过程:STEP1: 从服务器发送 SLAVEOF host port 链接主服务器,发送 SYNC 命令;STEP2: 主服务器执行 BGSAVE 生成快照文件,并往缓冲区记录增量写命令;STEP3: 主服务器的快照文件生成以后,会发送给从服务器,快照文件发送完毕以后,会发送缓冲区写入的增量写命令;STEP4:从服务器解析快照文件,覆盖原有数据;STEP5:从服务器接受主服务器发送来的缓冲区增量写命令,执行命令实现增量同步。STEP6:主服务器发送完缓冲区的增量写命令后,每接收一条写命令,执行完后就向从服务器发送相同的写命令,从服务器接收到写命令并执行,从而与主服务器保持一致。NOTE: 从服务器在同步时,会清空原有的数据。Redis 不支持主主复制。
  • 主从链。从服务器也能够拥有从服务器,从而构成树状的主从链。当从服务器开始解释快照时,会断开从从之间的链接。

一致性

数据的多副本在必定时刻点以后保持一致性的要求。

分类

要保证数据一致性,根据应用实际需求,有多种一致性的定义。

  • 时间点一致性:若是全部相关的数据组件在任意时刻都是一致的,那么能够称做为时间点一致性。
  • 事务一致性:事务的一致性指的是数据库的数据从事务执行以前的有效状态变动为事务执行以后的另外一种有效状态。数据有效状态是指必须知足数据的约束与完整性(事先在数据库里定义的数据规则集合)。若是事务成功地完成,那么系统中全部变化将正确地应用,系统处于新的有效状态。若是在事务中出现错误,那么系统中的全部变化将自动地回滚,系统返回到原始有效状态。
  • 应用一致性:在应用程序中涉及多个不一样的单机事务,在全部的单机事务完成以前和完成以后,多个应用的数据老是符合指定的规则集合。
  • 区别与联系:事务一致性和应用一致性,能够当作数据约束和完整的有效性,这些数据不必定是同一个数据项的定义(好比支付成功后送优惠与送积分,退款后退券与退积分);时间点的一致性,主要是多副本的数据内容要相同,一般属于同一个数据项定义(好比DB、ES、HBase 上的订单状态是同一个值)。

程度

一致性程度的观察角度:从全部的复制节点来看。

  • 一致性程度: 线性一致性 > 顺序一致性 > 因果一致性 > 最终一致性
  • 线性一致性:亦称原子一致性。任何进程的写操做,若是一个节点进程的读操做读到了最新值,则全部后续的读操做都应当读到最新值。反之,则不知足线性一致性。难点:分布式下的统一的全局时钟。
  • 顺序一致性:顺序一致性是指全部的进程以相同的顺序看到全部的修改。读操做未必能及时获得此前其余进程对同一数据的写更新。可是每一个进程读到的该数据的不一样值的顺序是一致的。
  • 因果一致性: 有因果关系的操做知足顺序一致性。好比一个线程先写一个变量,再读一个变量,那么它读到的必定是写以后的值。就知足因果一致性。
  • 最终一致性:不须要关注中间变化的顺序,只须要保证在某个时间点一致便可。

手段

  • 协议算法
  • 异步复制
  • 实时计算与同步
  • 重试、补偿

Raft协议

  • Raft 是一个基于复制状态机的一致性算法,用于分布式存储系统。Raft 将一致性保证分解以下子问题:Leader election, Log replication, safety, and membership changes。Raft 保证一个强有力的 Leader ,只容许 Leader 发送复制日志 ;复制状态机一般使用复制日志实现,每一个服务器存储一个包含一系列命令的日志,其状态机按顺序执行日志中的命令。 每一个日志中命令都相同而且顺序也同样,所以每一个状态机处理相同的命令序列。 这样就能获得相同的状态和相同的输出序列;Safety 机制用来保证在 Leader, Follower 崩溃的状况下,依然可以保证 Leader 总有全部已提交的日志。一致性算法的工做就是保证复制日志的一致性。
  • Leader election:时间被随机划分为多个时间片。每一个时间片做为一个“任期”,在任期内进行选举,选举出来的 Leader 负责管理 log replication。Leader 周期性地向全部 follower 发送心跳(不包含日志条目的 AppendEntries RPC)来维持本身的地位。每一个服务器都有一个任期号。若是 RequestVote RPC 中的任期号比本身小,那么 candidate 就会拒绝此次的 RPC 而且继续保持 candidate 状态。Raft 算法使用随机选举超时时间的方法来确保不多发生选票瓜分的状况,就算发生也能很快地解决。
  • Log replication:Leader 一旦被选举出来,就开始为客户端请求提供服务。客户端的每个请求都包含一条将被复制状态机执行的指令。Leader 把该指令做为一个新的条目追加到日志中去,而后并行的发起 AppendEntries RPC 给其余的服务器,让它们复制该条目。当该条目被安全地复制,leader 会应用该条目到它的状态机中(状态机执行该指令)而后把执行的结果返回给客户端。若是 follower 崩溃或者运行缓慢,或者网络丢包,leader 会不断地重试 AppendEntries RPC(即便已经回复了客户端)直到全部的 follower 最终都存储了全部的日志条目。复制状态机用于解决分布式系统中的各类容错问题。
  • Safety:Raft 使用投票的方式来阻止 candidate 赢得选举除非该 candidate 包含了全部已经提交的日志条目。候选人为了赢得选举必须与集群中的过半节点通讯,这意味着至少其中一个服务器节点包含了全部已提交的日志条目。若是 candidate 的日志至少和过半的服务器节点同样新(接下来会精确地定义“新”),那么他必定包含了全部已经提交的日志条目。RequestVote RPC 执行了这样的限制: RPC 中包含了 candidate 的日志信息,若是投票者本身的日志比 candidate 的还新,它会拒绝掉该投票请求。Raft 经过比较两份日志中最后一条日志条目的索引值和任期号来定义谁的日志比较新。若是两份日志最后条目的任期号不一样,那么任期号大的日志更新。若是两份日志最后条目的任期号相同,那么日志较长的那个更新。Safety 五法则:1. 至多只有一个 Leader 被选举; 2. Leader 只能添加新日志条目,不能修改或删除日志条目; 3. 两条日志条目具备相同的 index 和 任期,则视为等同; 4. 若是一个日志条目被提交,那么它必定出如今最高任期的 Leader 的提交日志条目里; 5. 若是一个节点服务器应用了某个 index 在状态机上,那么没有任何一个其余节点服务器会在相同的 index 具备不一样的日志条目。

海量

搜索

ES

  • 存储模型:JSON 串,没法部分刷新。只能写入新的文档,而后删除旧的文档,按期刷新清理。
  • 实现原理:倒排索引 - trie 前缀树 - Skip List - 增量编码。倒排索引 - 构建关键字词典,存储关键字及其文档编号、文档位置; trie 前缀树 - 为关键字词典构建词典索引(term index)以 FST 放在内存里,减小磁盘 random access 次数,节省空间;SkipList - 联合搜索的有序 DOC ID 列表的求交集;增量编码 - 获取到 DOC ID 列表后,对 DOC ID 进行增量编码存储,求出每一个 DOC ID 针对前一个 DOC ID 的增量,只存储增量值。
  • 四种查询: query and fetch, query then fetch, DFS query and fetch, DFS query then fetch。 ES 查询数据实际上包括两个步骤:查询到符合搜索条件的 DOC ID 列表(query),根据 DOC ID 列表获取 DOC 数据(fetch)。 and 就是把两个步骤合成一个步骤执行,能够节省网络请求开销(若是数据量不大),then 则是先查询后取数据(减小拿到没必要要的数据量)。DFS 是为了让结果更加精确。
  • 深分页问题:[ from,size ] 须要从 N 个分片中各取出 (from+size) 条数据,总共 N*(from+size) 条数据。from 很大时,要取出很是多的数据,才能返回指定的少许数据。 深分页方案:scroll 与 search_after 及对比。search_after 无状态,指定惟一标识及排序,能够实时查下一页,不支持跳页查询,集群资源消耗小;scroll 有状态,取一段时间的快照,新写入数据查不到,scroll context 开销大,集群资源消耗大。
  • 多表同步问题:1. 顺序队列方案,使用业务 ID 做为消息投递顺序依据; 2. 非顺序队列方案,全局中间存储、全局版本号递增、加锁。全局版本号更新加锁。顺序队列方案的问题:吞吐量扩展困难(取决于消息中间件实现)、容易堵塞形成消息堆积。非顺序队列方案:解决了顺序队列方案的难扩展、易堵塞问题,代价是:方案更复杂,在高并发下加锁 RT 会上涨。

存储

存储引擎

  • 哈希存储引擎:K-V 存储 。 Append-Only , 老文件只读不写,只写一个活跃文件;内存哈希表存储 key 和 value 的位置;老文件会作合并并创建索引文件(可用于快速故障恢复)。
  • B+ 树存储引擎:数据存于叶子节点,内节点存储更多的 key 值,减小键值比较时的磁盘读。
  • LSM 存储引擎(LogStructuredMergeTree): 数据修改增量存储在内存里,达到必定大小时批量写入磁盘。读取时须要合并历史数据和新的增量修改。优点是:磁盘顺序写。 LevelDB实现:操做日志 Commit Log(写以前记录操做,故障恢复)、可变 MemTable(增量写)、不可变 MemTable(合并刷入磁盘)、SSTable(刷入磁盘持久化后的文件)。

DB

  • 适用场景:适合固定 Schema 的结构化方式来存储大量结构化数据。大规模结构化数据存取的优先选择。
  • 主要优点:数据存储的规范性、完整性;事务与持久化。插入和查询的效率高,但更新的代价较高。
  • 开发要点:数据模型与表设计(规范性和完整性,场景走查)、索引设计与 SQL 语句(性能考量)、并发控制与事务(数据一致性保障)、链接配置优化(稳定性)。
  • 注意事项:须要在数据规范范式与冗余之间达成一个平衡点;避免死锁。

Redis

  • 适用场景:适合小规模数据子集的频繁快速读写。好比应用数据缓存、分布式锁、数据聚合/排名、关联关系、任务队列等。
  • 主要优点:内存读写,性能高、集中化,更新效率高。
  • 开发要点:须要可以灵活使用数据结构(字符串、列表、集合、散列、有序集合)及组合来实现业务功能。
  • 注意事项:须要限制数据集的最大值及遏制快速膨胀,处理好过时清除问题。选择适当的缓存策略和缓存读写策略,避免缓存雪崩/击穿/穿透问题/一致性问题。

HBase

  • 适用场景:适合大规模非结构化数据的存储读写。
  • 部署架构:Master/Slave,LSM, [ Master, RegionServer, HRegion, HDFS,Zookeeper ] 。
  • 数据存储模型 :[ Rowkey, ColumnFamily, ColumnName, Value, Timestamp ] (逻辑) ; [ Store, MemStore, StoreFile, HFile, HLog ] (物理)。一个 HRegion 包含 一个 HLog, 多个 Store 。 一个 Store 包含 一个 MemStore ,多个 StoreFile, 多个 HFile 。
  • 列写入:动态列写入,无需事先声明;列写入的过滤机制:对于新的列值 ,若其 Timestamp 比现有的小,则数据不会写入。
  • 查询及优化:1. 根据 rowkey 查询 HRegion 地址 (zk 缓存以及 hbase:meta 表); 2. RegionServer 处理读请求。Scanner 对象,最小堆。客户端读优化: get 请求 - 启动时表预热,rowkey 均衡,适量批量 get ,指定列族; scan 请求 - 改造为 batchGet ,设置合适的 startKey 和 endKey ,设置 caching 数量。 服务端优化:读缓存配置、本地化率,SSD 盘,短路读等。
  • 开发要点: Rowkey 设计及 region 分布平衡;启动预热表、超时设置、主备设置; BatchGet 替代 Scan 。
  • 注意事项: 避免数据分布不均匀。

文件系统

  • 音视频存储

分析

  • Hive: Hive SQL

实时计算

  • Storm, Spark , Flink

安全

  • 常见安全问题:缓冲区溢出、XSS、SQL注入、DDOS
  • 安全评估:资产(数据)等级分析、威胁分析、风险分析、解决方案确认
  • 机密性保证:加密(对称加密和非对称加密)、脱敏(敏感信息避免模糊化处理)、代理隐藏、权限管控
  • 完整性保证:数字签名
  • 可用性保证:黑名单、限流、防火墙、清洗流量
  • 威胁建模:STRIDE 模型。假装、篡改、抵赖、信息泄露、拒绝服务、提高权限。
  • 风险分析:DREAD 模型。Damage Potential(完整验证权限、执行管理员操做、非法上传文件)、Reproducibility (随意再次攻击)、Exploitability(易学性,初学者能够很快学会)、Affected Users(全部用户、默认配置、关键用户)、Discoverability(漏洞很显眼,攻击条件易得)
  • Secure by Default: 白名单原则 , 最小权限原则。 采用黑名单容易绕过或遗漏。白名单要避免范围过大的通配符。
  • 纵深防护原则:从不一样层次、不一样方面实施安全方案,构成防护总体。避免薄弱环节与短板。
  • 数据与代码分离原则:防止 缓冲区溢出、SQL 注入、XSS。
  • 不可预测原则:让系统的关键运行区域显示出随机性,从而大幅提高恶意构造请求来攻击的难度和成本。请求 token 机制。
  • 源头堵截原则:在源头上堵截漏洞,而不要依靠不少人经过学习来掌握防护技术。让防护变得易学易用。

智能化

  • 机器学习

平台

操做系统

内存管理

  • 虚拟内存。创建从虚拟地址空间到物理内存地址的映射。由 MMU 中的地址翻译硬件、页表(PT,常驻物理内存,进程专享)、操做系统共同实现。虚拟内存的用途:1. 磁盘空间的缓存工具; 2. 内存管理工具,为进程提供独立的地址空间,互不影响; 3. 灵活的内存分配,虚拟地址能够连续,而对应的物理地址能够不连续;4. 保护内存不受进程破坏,不容许修改只读代码段和内核的代码与数据,不容许修改其余进程的地址空间的数据,不容许修改共享页面。能够为 PT 添加一些许可位来控制进程的读写权限。
  • 内存布局。指各类元数据、数据、代码段在内存中的有序布置。设计良好的内存布局,更有利于访问数据和代码。具体示例可参考 ELF 可重定向目标文件格式。JVM 也有本身的内存布局。
  • 内存分配。内存分配策略 - 首次适配(简单)、最佳适配(碎片小,但很难再次分配,须要合并)、最坏适配(分配后仍然可再次分配,大块内存难以知足)。算法 - Buddy, CMA, slab。Bump the Pointer(若是未使用内存与已使用内存分离了,Serial, ParNew)、Free List(找适配大小的空闲块,Mark-Sweep, CMS)。分配内存的并发问题解决方案:CAS 机制和 预先分配的 TLAB(本地线程缓冲分配)。指向对象的指针或引用在虚拟机栈上分配。

程序执行

  • 连接与加载。将各类代码片断、数据片断进行收集并组合起来,构成完整的可执行目标文件,并加载到内存中执行的过程。连接与加载涉及模块和库依赖、符号引用、做用域、动态共享库之类的话题。连接器的主要任务:符号引用和重定向。每一个可重定向目标模块都有一个符号表,用来作符号解析。

JVM

内存管理

  • 内存布局:PC寄存器(指令地址)、虚拟机栈(方法执行)、本地方法栈(Native方法执行)、堆(对象内存分配)、方法区(类加载信息、运行时常量池、静态变量、即时编译信息等)、直接内存(NewIO)。每一个线程都有专属的【虚拟机栈、PC寄存器 、本地方法栈】。
  • 对象建立和初始化:new 参数 -> 定位类引用 -> 检查类是否加载、解析、初始化 -> 若是没有则执行类加载过程 -> 为对象分配内存 -> 分配空间清零 -> 对象头内容设置 -> 执行 _init 方法初始化对象。
  • 对象内存布局:对象头(MarkWord)、实例数据、对齐填充。 对象头 -- 对象哈希码、分代年龄、轻量锁、重量锁、偏向、类元数据指针。实例数据 -- 虚拟机字段分配策略和程序指定顺序。字节数相同的类型的字段放在一块儿分配。对齐填充 -- 对象起始地址必须是 8 的整数倍。访问对象的方式:对象句柄池和直接指针。使用对象句柄的好处是引用的地址是稳定的,只要改变句柄的值(对象移动时),使用直接指针的好处是效率更高,减小一次指向过程。
  • 对象引用分析:对象可达性遍历判断对象是否活跃。强、软、弱、虚引用。强 - 只要有引用就不回收;软 - 在抛出 OOM 以前进行回收;弱 - 下一次 GC 时回收;虚 - 持有虚引用的对象在GC时能够收到系统通知。
  • GC算法:复制算法(新生代,优化:迭代避免溢出、多空间提高空间利用率);标记-清除算法(老年代,优化:多空闲链表、位图标记、延迟清除)。 GC 收集器:三个维度,单线程与多线程,Client 与 Server,新生代和老年代。Serial- 单线程,Client, Par- 多线程。CMS 更好的响应,Parallel Scavenge 更好的吞吐。G1 :可预测停顿时间。 GC 日志分析。
  • 内存问题:OOM, Memory Leak , Memory Overflow,Stack Overflow ,Method Area Overflow。 OOM -- 线程转储,定位缘由;Memory Overflow -- 堆内存空间不足,死循环; Memory Leak 一般是资源没有正确释放,没法及时 GC ,使用 MAT 等工具分析从 GC ROOT 的引用链;Stack Overflow -- 递归过深,分配数组大小过大;Method Area Overflow -- 字节码技术动态生成或加强的大量类载入方法区可能致使方法区溢出(CGLib, JSP, OSGi)。
  • FullGC 缘由及方案:1. 大对象列表,很大的列表,拆分小对象/小列表,及时释放; 2. Old 空间小,能够调大 Old 区; 3. 不要轻易手动调用 System.gc 方法。

类加载

  • 平台无关性和语言无关性:字节码与虚拟机。将高级语言编译成符合虚拟机规范的字节码,从而可以在符合规范的虚拟机上执行字节码,最终映射到本地机器指令集。操做系统平台和编程语言对应用程序是透明的。
  • 重要元素:Class 类文件 - 字节码指令集 - 类加载器 - 加载机制。Class 类文件是 Java 类或接口的字节码表示,字节码指令集是字节码的主要元素。类加载器根据 Class 类文件在虚拟机中建立类对应的 Class 对象 。Java 类加载器包含 Bootstrap 加载器、Ext 加载器、App 加载器、Customized 加载器。类加载的顺序是先用前面的加载器进行加载,找不到则用下一个。类或接口的惟一标识:类或接口的名称 + 定义类加载器。不一样类加载器加载同一个类或接口在 JVM 是不一样的。
  • 类加载过程:加载-连接-与初始化:加载 -- 查找类或接口的二进制表示,并据此来建立类或接口的 Class 对象; 连接 -- 将类或接口连接到虚拟机运行时状态,连接包括验证、准备、解析; 初始化 -- 执行初始化方法
  • 加载:运行时常量池存储着类、类的成员和方法的各类信息的二进制表示,符号引用来自于 CONSTANT_XXX_info 结构。
  • 初始化:调用 new, getstatic, putstatic, invokestatic 指令时触发。初始化须要保证多线程环境的安全性,由虚拟机负责实现。每一个类或接口 C 都有一个对应的惟一初始化锁 LC。初始化以前须要先获取这个锁 LC ,分状况进行处理并释放锁。

安全机制

  • 安全模型:沙箱模型、限定于 JVM 范围。主要是对本地系统资源的访问限制。类加载验证。安全策略。域、代理、权限组。doPrivileged。

小结

数据结构与算法是构建互联网的基本材料,网络、存储、操做系统、分布式是现代互联网应用的服务端的基石,而语言、平台、框架、中间件则是在基石之上的基础设施,它们共同构建了稳固的互联网大厦。

体系结构是一种强大的方法论。打造一个知识体系,构建底层逻辑,理解上层变化,自下而上打通,持续融入所学。

PS:之前,我常以为业界写具体技术的书比较多,而总结软件设计思想和案例的书太少,不利于开发者的设计能力的快速提高。如今看来,业界到处都有优秀的框架、中间件、平台设计,简直是宝山在眼前,而我却视若无睹。不过,要能从这宝山里挖掘财富,其实也不容易。

参考文献

相关文章
相关标签/搜索