career-anchor-tech
技术
分布式
为什么用分布式
- 增大系统容量
- 加强系统可用(关键业务保护)
其他原因 - 模块化,增加重用、开发效率
分布式问题-复杂
- 架构设计: 分布式事务、调度、技术多元化
- 测试、排错
- 部署运维
70年代模块化编程
80年代面向事件设计
90年代基于接口/构件设计
现代 Service-Oriented Architecture面向服务的架构
SOA原则
- 模块化、可组合、可重用
- 交互和表示使用通用标准
- 开发运维(改配置)
- 通信协议、数据格式(错误码)
- 服务 识别分类、提供发布、监控跟踪
- 服务关键度和服务间调用路径
- 隔离服务和数据库
- design for failure, 自动化恢复故障
- 多层架构统一视图
多层架构
- 基础层:机器、网络、设备
- 中间件:Tomcat、MySQL、Redis、Kafka
- 业务应用
- 接入网关或是CDN、DNS
微服务:服务间整合通过服务编排实现,如工作流引擎、网关、容器化调度K8s
技术栈
- 提高性能
- 缓存:分区、更新、命中
- 浏览器,网络,应用,数据库,文件系统,硬盘,CPU 都用缓存
- 缓存集群,需要路由和分片
- 负载均衡-网关:负载、路由、发现
- 水平扩展、分担请求
- 异步调用:消息队列、持久、异步事务
- 实时性差
- 数据镜像:同步、读写分离、数据一致性
- copy
- 数据分区:分区策略、数据访问层、一致性
- 地理分区
- 跨库JOIN、事务
- 读写分离、分库分表
- 稳定性
- 服务拆分-治理:拆分依赖、发现调用、隔离、关键度定义
- 目标:隔离故障、重用模块
- 兼容:架构版本管理、生命周期管理、服务调度功能(编排、聚合、事务处理)
- 服务冗余-调度:弹性伸缩、故障迁移、服务熔断
- 目标:解决单点故障
- 问题:有状态的服务冗余,在弹性伸缩时,需要考虑数据复制或重新分片
- 限流降级:异步队列、负载、路由、降级、熔断、限流,异地灾备
- 兜底:系统扛不住压力时停掉部分服务,或拒绝部分用户
- 高可用架构:多租户、灾备多活、高可用服务
- 高可用运维:全栈监控、Devops、自动化运维
- 做自动化测试部署、灰度发布、产线自动化控制
- 自动伸缩、故障迁移、配置管理、状态管理、资源调度隔离管理(CPU、内存、网络)
关键技术
- 架构监控
- 基础层:CPU、内存、磁盘、网络
- 中间层:消息队列、缓存、数据库、应用容器、网关、RPC框架、JVM
- 应用层:API请求、吞吐量、响应时间、错误码、SQL、调用链路、函数调用栈、业务指标
功能
-
监控
-
关联分析
-
跨系统调用链
- 服务实例和主机关联
- top n:调用量、请求耗时、热点
-
报警和自动处理
-
性能分析
-
资源/服务调度
- 计算机资源:CPU、内存、磁盘、网络
- 服务调度:编排、副本、容量伸缩、故障迁移、生命周期管理
- 架构:多租户、版本管理、部署运行更新销毁、灰度发布
-
状态/数据调度
- 数据可用:多副本
- 数据一致:读写一致性策略
- 分布式:索引、分片
状态存储到redis、mysql
解决数据异常丢失:数据副本
分布式事务一致
-
应用层,两阶段提交,CA
-
数据层,paxos,CP
数据节点调度解决应是由分布式存储系统解决
CAP
- Consistency(一致性) :要么读取到最新数据,要么失败
- Availability(可用性):可读取到数据,不保证最新
- Partition tolerance(分区容忍性):保证服务正常运行,不管出现任何数据同步问题
- 流量调度
- 服务治理:发现、路由、降级、熔断、保护
- 流量控制:负载均衡、流量分配、流量控制、异地灾备
- 流量管理:协议转换、请求校验、数据缓存、数据计算
目标
- 依据系统运行,自动调度
- 资源耗尽,保护系统运行
技术 - 高性能
- 集群:分组、各节点共享数据
- 业务逻辑 gateway
- 服务化:admin api 管理配置变更不停机
弹力设计
异步,重试,幂等;熔断,限流,服务降级,服务状态;补偿事务
故障隔离
- 服务种类
- 系统模块:用户、商品、社区;使用不同的域名、服务器和数据库
- 通常引入异步模型、重试、流控、熔断等设计
- 问题:多服务调用降低响应时间;设计用户交互,分页获取数据;
- 问题:数据仓库合并复杂,需要用框架或中间件抽取;
- 问题:跨板块交互;使用Pub/Sub;
- 问题:分布式事务;两阶段提交 Plan-Reserve-Commit/Cannel,相似于 TCC- try confirm/cancel
- 问题:连锁故障;业务流程节点保存数据,故障恢复后可加载继续执行
- 多租户
- 大客户设置专门的服务实例/集群;小用户共享服务实例
- 设计:完全独立资源(Docker-Container);共享服务、独立数据分区;共享服务、数据分区;
- 重点
- 定义业务大小和粒度,认真做业务需求和系统分析;
- 考虑复杂度(开发运维复杂)、成本、性能、资源使用的问题
- 监控
异步
系统间通讯;一个人不能同时接打很多电话,但可以接发很多邮件
同步优势:实时、系统只需要接口
同步缺点:接收请求有限(一对一,做不到一对多;吞吐量低)、调用链路长响应慢、连锁故障-耦合
异步通信方式
- Pub/Sub:直接订阅
- Broker:中间人订阅,最佳
- 注意
- Broker 高可用(系统关键)高性能(可水平扩展)、持久化(不能丢消息);
- 设计时消息不依赖时间顺序
- 业务流程的状态变迁 由总控方管理,知道业务处理到哪步,可以在故障清除后继续处理
- 设计常见于银行对账程序,对账系统就是总控,确保数据正确,银行T+1(隔天)结算
- 消息传递中,业务需要像TCP协议那样的send/ack机制,因此需要处理方有幂等处理(无论收到多少次,只处理一次)
- 注意
- 事件驱动,Broker使用的是此模式
- 下单服务-事件-订单服务-事件-
- 优点:服务”自包含“,彼此不直接依赖,服务间添加Adapter(日志、认证、版本、限流、降级、熔断)容易
- 缺点:业务流程需可视化工具来展示;事件乱序,需要状态机;分布式事务需要两阶段提交
幂等
多次请求,只处理一次:f(x)=f(f(x))
源于
系统间调用的服务状态,timeout 不确定下游。是接收到了请求,还是收到请求是正确处理了,还是响应 成功/失败的结果时遇到网络问题
- success
- failed
- timeout
- 可能是网络传输丢包
- 没有请求到
- 请求到了没有正常返回
不确定的影响:多次调用,订单重复创建、库存重复扣减、钱多扣一次
解决:接口支持幂等
- 全局ID:标识同一笔交易 Snowflake生成 Long型ID
- 处理流程
- insert into... values... on duplicate key update ... 不存在插入、存在更新;
- update xxx set status='paid' where id =xxx and status='unpaid'; 更新
- 需要存储(RDBMS、NoSQL)共享,可用可扩展
HTTP的幂等性
- get Y
- post 创建(URL不是创建者本身,是执行创建动作的操作者)N
- 幂等设计 Post/Redrict/Get:表单隐藏 唯一ID,防止用户多次提交;后端保存时也保存ID,用ID做排他限制;302跳转展示提交内容表单置为过期避免回退按钮重新提交
- put 创建或更新(URL是创建或更新的资源本身) Y
服务状态
状态,就是程序的一些数据或上下文,如
- 幂等设计中的存储每次请求的唯一标识
- 用户登录的Session,用于判断请求合法性
- 服务组合的上下文Context
- 程序调用结果
- 服务配置
无状态的服务stateless
易于扩展和运维
函数思想是无状态的,只描述逻辑算法,不保存数据,不修改输入数据,返回计算结果,只要修改就要COPY
实现:服务存储分离,状态转移到 Redis/MySQL/强一致性存储/分布式文件系统;这些存储服务必须高扩展,增加缓存快速响应
有状态的服务 stateful
优势
- 数据本地化,低延时
- 更高的可用性和更强的数据一致性
原因:Sticky Session,保证客户端的请求落在同一个实例(同一个机器)上,可用于做数据分片。这样的模型更容易理解和实现,不需要将数据同步到不同节点中去
Sticky Session的实现:持久化的长连接;或是Hash算法如UUID求模,用一致性HASH;
缺点:节点的负载和数据不均匀;
解决:长连接的服务端变成热点,就主动断开,这需要客户端配合,不然容易出现BUG - 需要一个元数据索引,映射后端服务实例和请求;
- 还需要一个路由,根据元数据索引来路由,根据后端服务的压力重新组织元数据索引映射,开源项目 ringpop-node / 用Gossip协议,在各节点间散播消息来同步元数据(实现复杂),开源项目 roleans
服务状态的容错设计
数据运行时就复制给其他节点,如 Kafka、Redis、ElasticSearch,两阶段提交保证一致性
故障恢复加载大量数据会很慢,需要使用分布式文件系统,将数据持续化到硬盘,新宿主机上启动服务,可以远程挂载数据,启动时就加载大量数据,可以从其他节点只复制少量数据
补偿事务
组合服务间的强一致性一般需要底层完成,或使用 Sticky Session 在一台机器上完成;业务上大多只需要最终一致性,如果需要强一致性,用两阶段提交
关系型数据库事务 ACID,保证数据库的一致性
分布式系统中,在高性能要求下,提出BASE,允许系统出现暂时性问题,Design for Failure
- Basic Availability:基本可用,允许暂时不可用快速恢复
- Soft-state:软状态,为提高性能,让服务暂时保存一些 非强一致性的状态/数据
- Eventual Consistency:最终一致性,系统在短暂时间段内不一致,但最终看到的数据一致
保证在短时间内,就算有数据不同步的风险,也允许新的交易发生,后面对出现问题的事务处理掉,保证最终一致性
ACID:同一时间不可能用多个用户下单,订单流程排队
BASE:异步批量处理订单,通知结果
日常生活也存在,当条件不满足/有变化时,需要做事务的补偿
出门旅游
- 向公司请假,拿到假期
- 订飞机票或火车票
- 订酒店
- 租车
这些事要向不同的组织或系统请求,前三件必须成功,设想 - 返程车票没有订到,去不了了,需要退掉车票、酒店、车、假期
- 车没有租到,事情还可以继续
- 飞机晚点,要调整酒店、车
技术世界中,线上运维系统要发布一个新服务或对服务水平扩展,部署系统需要管理好整个过程和相关的运行状态
- 找到机器
- 初始化环境
- 部署应用
- 健康检查
- 接入流量
业务补偿需要将服务做成幂等性的,一个事务失败或超时,不断重试,重试失败要把整个状态恢复到之前,如果请求有变化,要启动整个事务的业务更新机制
业务补偿的目标
- 清楚的描述要达到的状态 ,如果条件不满足要回退的状态
- 当整条业务跑起来,可以串行或并行地做这些事,尽量执行完一个业务流程,达不到状态,通过补偿机制回滚之前的状态
- 上游重试、下游幂等
- 流程控制方(工作流引擎,需要高可用、稳定)在一个组件中控制状态
- 补偿的业务逻辑和流程可以并行,不一定要反向操作
- 补偿的业务逻辑是强业务相关,很难做到通用
重试
普遍的设计模式,当单体应用服务化,在一个进程内的调用变成远程调用,会涉及网络的问题。网络有很多组件:DNS、网卡、交换机、路由器、负载均衡,这些设备可能不稳定,数据传输中,只要一个环节出问题,会导致整个连接的问题
重试场景
期待:故障是暂时的
重试设计时,需要定义出重试的条件,如 调用超时,被调用端返回了可以重试的错误(繁忙中、流控中、维护中、资源不足)
不需要做重试的错误
- 业务错误:没有权限、非法数据
- 技术错误:HTTP 503-可能触发了BUG
重试策略
- 重试最大次数/重试时间范围
- 重试异常类型
- 延时开始重试
- 指数级退避,和TCP的拥塞控制有点像
Spring Retry重试策略:注解配置,重试最大次数 / 超时时间内允许重试/可组合
设计重点
- 考虑被调用方幂等
- 重试结束后熔断
- 有事务相关的操作,更希望它成功,我们可能需要一个较长时间的重试,保存请求上下文在数据库中,过会再捞出来重试
熔断
保护电器免被烧坏
优势
- 在系统从错误恢复的时候提供稳定性,快速拒绝可能导致错误的服务调用,停止可能无效的重试,减少错误对系统的影响
- 提高系统响应时间,减少等待 超时的操作或者 永远不返回的结果
- 诊断错误修正,再次尝试调用
设计
熔断是 错误操作的代理,记录最近调用发生错误的次数,允许操作继续,或立即返回错误
可以使用状态机实现
- 闭合 Closed:一个调用失败的计数器,超出阈值则切换到 Open 状态。开启一个超时时钟/连续失败的次数,超出阈值,则切到 Half-Open 状态,给系统一次机会修正调用失败的错误,在特定的时间间隔内自动重试,避免偶然错误进入断开状态。
- 断开 Open:应用程序请求,可以先取缓存中(本地内存,缓存的机制是取全站一样的数据,避免缓存太多)成功的数据,没缓存再返回错误
- 半开 Half-Open:允许一定数量的请求取调用服务,如果调用成功,切到 Closed 状态,将错误计数器重置。如果调用失败,切到 Open 状态。 Half-Open 有效防止正在恢复的服务被突然的大量请求再次拖垮
熔断器设计模式在每次状态切换时会发出一个事件,这个信息可以被监控并通知到管理员
开源实例 Hystrix
设计重点
- 错误类型:识别返回多错误 1. 先走重试(限流、超时),失败几次后打开熔断 2. 远程服务挂掉,直接熔断
- 日志监控:记录所有请求,能看到熔断器保护服务的执行情况
- 测试服务可用:断开状态下,可以定时ping一下远程服务的健康检查接口,而不是使用计时器来自动切到 Half-Open 状态,避免影响用户的真实请求
- 手动重置:服务恢复后,管理员可以强制将熔断器切到 Closed 状态,同理也可以切到 Open 状态
- 并发问题:熔断器不应阻塞并发请求/ 增加请求调用负担,尤其时调用结果的统计,使用无锁的数据结构/atomic 原子操作
- 资源分区:只对有问题的分区(数据库分库分表) 做熔断
- 重试请求:处理方幂等支持
限流
对并发访问进行限速,保护系统免过载
场景
- 数据库连接池
- 线程池
- Nginx 限制瞬时并发连接数的 limit_conn模块、限制每秒平均速率的 limit_req模块
- 限制 MQ的生产速率
策略
一旦达到限制的速率,就触发响应的限流行为
- 拒绝服务:统计哪个客户端的请求最多,直接拒掉这个客户端,可以阻挡恶意请求
- 服务降级:让服务有足够的资源处理请求。1. 不重要的服务停掉,释放 CPU、内存、数据资源 2. 只返回部分数据
- 特权请求:有限资源分给 VIP 用户。多租户下,大客户有权优先处理
- 延时处理:队列缓冲请求,队列满只能拒绝用户。队列任务超时,要返回系统繁忙
- 弹性伸缩:自动化运维自动化伸缩,需要应用性能的监控系统,感知到最繁忙的 TOP5 服务。需要自动化的发布、部署、服务注册。要快,否则系统要被压垮了。数据库压力大时,弹力伸缩是没用的,这时应限流。
实现方式
- 计数器:请求来时+1,请求完后-1,Counter > 阈值。拒绝请求保护系统负载
- 队列算法:FIFO队列 1. 高优先级队列和低优先级队列 2. 分欸不同比例的处理时间到不同的队列,避免低优先级饿死,PULL更好,难确定队长
- 漏斗算法:在队列请求上加一个限流器。TCP 的 sync backing 的队列缓冲请求,TCP的滑动窗口也是用于流控的队列。
- 令牌桶算法:中间人,在桶内按一定的速率放Token,请求需要拿到 Token才能被处理。可以做成第三方的一个服务,对全局进行流控
- 上面的算法都要设置限流值,每次发布服务时要做性能测试,找到系统最大的性能值,有时不能确定限流值
- 服务依赖蜀山少数,不同用户请求有不同数据集,数据不断变化,很难给出一个值
- 不同的API有不同性能
- 自动化伸缩的服务调整阈值很难
- 基于响应时间的动态限流:动态感知系统压力自动化限流,典型实现是TCP协议的拥塞控制 使用RTT-Round Trip Time 探测网络延时和性能,设置滑动窗口大小。让发送速率和网络性能匹配
- 阈值:记录下每次调用的响应时间,在一个时间区间内(如过去10s)的请求计算成响应时间的 P90 或 P99值(响应时间排序,看90%的位置)
- 要点:
- 计算 P90 值,在大量请求下比较耗内存CPU,因为要对大量数据排序
- 不记录所有请求,采样
- 蓄水池的近似算法
- 记录当前 QPS,发现 P90 响应太慢,将 QPS 减半,像 TCP 一样走慢启动的方式,直到再开始变慢,再减去 1/4 的 QPS。有点像阻尼运行的过程,流量会在一个值上下做小幅震动。后端扩容伸缩后性能变好,系统会自动适应后端最大性能
- 计算 P90 值,在大量请求下比较耗内存CPU,因为要对大量数据排序
设计要点
目标
- 向用户承诺 SLA,保证系统在某个速度下的响应时间和可用性
- 阻止多租户下,某一用户把资源耗尽,让所有用户无法访问
- 应对突发流量
- 节约成本。我们不会为了一个不常见的尖峰把系统扩容到最大尺寸。期望在有限资源下承受比较高的流量
设计 - 限流在架构早期考虑
- 限流模块性能必须好
- 限流应该有手动开关
- 限流应该有监控事件通知,可以及时跟进。应该有自动化扩容,可以缓解系统压力
- 限流定义特定的限流错误码,客户端看到限流,可以调整发送速度
- 限流应该让后端服务感知到。在协议头塞一个标识,如 http header 放一个限流的级别,后端服务可以根据限流决定是否做降级
降级
资源不足而访问量过大的矛盾
- 降低一致性:强一致 变 最终一致
- 写:异步简化流程:电商下单交易系统,要 结算账单、扣除库存、发起支付、发货
- 快速结算订单,不占库存。库存不够,取消订单
- 在线支付降级成用户到付
- 降低数据一致性
- 读:使用缓存或直接去掉数据,不显示数量,只显示有没有
- 只查缓存,使用 cache aside / read through
- 写:异步简化流程:电商下单交易系统,要 结算账单、扣除库存、发起支付、发货
- 停止次要功能
- 停止不重要的功能,如电商中的搜索功能,用户评论功能
- 次要功能 限流,或退化成简单功能
- 用户体验差,最好做些补偿,如 把用户切到一个送积分卡,或是红包抽奖的网页
- 简化功能:简化业务流程,或不再返回全量数据
- 一个API一般有两个版本,一个版本返回全量数据,另一个最小可用数据
- 一篇文章,一个API会把商品详情页或文章内容和所有评论都返回前端,降级下不返回评论,因为评论涉及更多数据库操作
设计要点
- 涉及业务侵入,要对业务做仔细梳理
- 定义降级的关键条件,如 吞吐量过大,响应时间过慢,失败次数过多,有网络或是服务故障。应急预案最好写成代码可以自动化执行
- 写操作需要记流水账,方便对账
- 降级开关可以是系统配置开关,降级时推送配置。在对外服务api上,签名加开关参数,可以由上游者来驱动
- 降级属于应急方案,不常用,平时做些演练
管理设计
分布式锁
多线程访问共享资源需要加锁,否则可能数据错误
分布式系统下需要分布式的锁,可以用 DB、Redis 实现,要求
- 安全Safety:任意时刻,只一个请求获取锁
- 避免死锁:请求最终一定可以获取锁,即便锁住某个资源的客户端在释放锁前崩溃或网络不可达
- 容错:只要锁服务集群中的大部分节点存货,就可以进行加锁解锁
Redis锁
加锁
set resource_name my_random_value NX PX 30000
解锁
// 只解自己加的锁
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
锁的值
- 官方推荐:/dev/urandom 中取20个byte作为随机数 / RC4加密算法在 /dev/urandom 中得到一个种子Seed,生成一个伪随机流
- 时间戳+请求编号生成随机数,安全性差些但对于大多数场景够安全
- 锁服务需要一个递增的版本号,些数据时带上版本号,数据库保存数据版本号,对数据做检查
乐观锁到CAS
数据库也保留版本号
update xxx set xxx=xx version=version+1 where version = #{version}
这是乐观锁最常用的实现方式,可以不用分布式锁
也可以 fence token,更新提交时检查数据库时间戳,和自己更新前取到的时间戳对比
设计重点
- 使用redis做分布式锁,在锁服务上加个过期时间,超时自动解锁,避免锁不释放
- 锁服务应高可用,并且可以持久化,可重入,非阻塞
- 分布式保证集群下同一方法互斥。请求方法前先请求到标识,否则快速失败
- 锁服务自动解锁,之前的请求以为自己还有锁时,可以在更新数据时,像 redis 那样 check and set,这时也就不需要锁了
- redis 锁更适用于进程间的同步互斥,更新数据更适合用数据库 cas
配置中心
软件配置
- 连接信息、线程池大小、队列长度
- 日志级别
- 算法策略
- 软件运行环境:java内存大小,应用启动参数,包括操作系统参数
Windows 下的 ini,Linux 下的 conf
分布式系统下,使用集中式的配置中心
设计
- 区分软件配置
- 静态:初始化配置。操作系统网络配置,docker 进程配置,
- 动态:运行时会修改。日志级别,降级开关,环境开关
- 按运行环境分
- 按依赖分:内部配置或依赖外部的配置mysql,redis
- 按层次分:基础层:操作系统;中间层,应用层
- 配置中心模型
- 按层,基础层运维或架构师配置,value是选项,不能随便输入,最好有相关模版初始化全套配置参数,有配置管理工具;应用层配置要有相应命名规范,确保不冲突
- 外部依赖配置放到服务发现中心
- 不同环境,配置项的值不一样,要注意
- 要有版本管理,关联软件版本号
配置中心架构
- 配置录入
- 变更通知组件 pub/sub
- pull,1. 反向校验请求者角色 2. 服务启动时拉一份配置 3. 有时需要拉配置中心证书
- 配置变更控制器放在每台主机上,配置步骤多,效率更好,事务成功率更大
- 平台层的配置变更,有的放在启动命令上,参数通过shell脚本做成配置项,更改系统环境变凉,重启达到变更
- 操作系统的配置变更和平台层的最好模块化掉,利于减少配置复杂性
- 应用层配置更新的标准化
- 老项目兼容配置文件和api方式的配置
- sdk,observer 订阅修改事件,耦合语言
- 运维脚本统一操作应用变更。脚本中各应用方用不同方式变更配置,bug多
- 为每个服务单独做一个agent
设计重点
- 重点在于统计和标准化软件配置
- 如果配置很复杂,应该静下心来梳理下配置参数,模块化简化配置
- 控制层和业务逻辑层分离
- 配置更新控制器需要应用服务配合,如 reload ,优雅启停,api
- 配置更新控制器负责启动服务,运维层实现
边车模式
通过给一个三轮车添加边车的方式,扩展现有的服务和功能,实现控制和逻辑的分离
服务中做好业务逻辑,由边车实现控制功能:监视、日志记录、限流、熔断、服务注册、协议适配转换
设计
边车是服务的Agent,服务的对外通信通过Agent实现,Agent和服务一起创建、停用
- SDK、lib 与服务集成
- 前提:弹力设计可标准化
- 优点:利于利用资源,提高应用性能
- 缺点:侵入应用、受编程语言和技术限制、软件包升级时需重新打包发布
- Sidecar 运维时与应用集成起来
- 优点:不侵入应用、不受语言和技术限制、可做到控制和逻辑的分开升级和部署
- 缺点:增加应用服务的依赖性;增加应用延迟;增加管理、托管、部署的复杂度
- 适用于:老系统;多语言分布式服务;不同供应商提供的应用服务;标准化控制面的技术和动作
- 不适用于:架构不复杂,直接用 API Gateway / Nginx / HaProxy;服务间的协议不标准且无法转换;不需要分布式的架构
和应用部署在同一个节点中,有相同的生命周期,监控应用服务的进程信息,两者通信不存在明显延迟;
帮服务注册到服务发现系统,对服务做健康检查,将不健康的服务剔除;
应用服务调用外部服务时,Sidecar做路由;接管进出流量,做日志监控、调用链跟踪、流控熔断;
服务控制系统通过 Sidecar 控制应用服务,如流控、下线;
设计重点
- 进程间通信,用网络远程调用的方式,不侵入应用服务
- 服务协议:对内尽量兼容 服务应用,对外使用更开放的标准协议
- 同时设计 服务打包、构建、部署、管控、运维,Docker可以降低复杂度
- Sidecar 内不要设计业务逻辑
- 小心通用功能的添加,如重试,除非所有操作是幂等的
- 应用服务和 Sidecar 的上下文传递:HTTP请求头选择退出重试、执行最大重试次数;Sidecar告诉应用限流发生,远程服务不可用等信息
服务网格
Service Mesh:Sidecar 集群
只要 Service Mesh 做好,只需要加入应用服务就好了
Service Mesh:一个基础设施,轻量级的服务通信的网络代理,对应用透明无侵入,解耦分布式中控制面的东西;像TCP协议,管理 丢包重传、拥塞控制、流量控制;
开源软件Conduit:Rust负责数据、Go负责控制;其核心的 Sidecar 叫 Envoy(使者)
K8s 和 Docker 进程的失败不会导致应用的异常运行,但 Service Mesh 不行,因为调度了流量,需要高可靠,且故障有workaround 方式:本机 Sidecar , 集中 Sidecar(Gateway实现),本机出问题走集中
Service Mesh 独立部署,而 Sidecar 和应用一起部署。Service Mesh 可以更好的和 K8s 配合
网关模式
一级 Gateway 接入所有流量,分发给子系统 (和面向对象中的Facade很像)
二级 Gateway 接入子系统流量
Gateway 封装内部系统架构,提供API给客户端。还可以做 鉴权、监控、负载均衡、缓存、熔断降级限流,请求分片和管理,静态响应处理
设计
-
请求路由
-
服务注册:注册API接口的URI、方法、HTTP头
-
负载均衡:简单点就是Round-Robin,复杂点可设置权重分发,再复杂点做 Session 粘连
-
弹力设计
-
安全:SSL加密及证书管理、Session 验证、授权、数据校验、防范恶意攻击
还可以做 -
灰度发布:对相同服务不同版本的实例导流,收集数据;利于提升软件质量、产品试错
-
API聚合:多个请求聚合成一个请求,拼装响应结果,需要客户端配置
-
API编排:网页使用DSL编排工作流API
-
Gateway:简化 Sidecar,只管进不管出
-
Sidecar:改造已有服务
-
Service Mesh:统一管理 Sidecar
设计重点
- 高性能
- 使用高性能语言 C、C++、Go、Java
- 异步请求Linux-epoll、Windows-IO Completion Port、Java-Netty、Vert.x、Spring Reactor 的NIO;更推荐 Go-goroutine+channel
- 高可用:所有流量经过网关
- 集群化:自己同步数据,不要依赖第三方同步
- 服务化:热修改配置;服务化,开放 Admin接口
- 持续化:重启,像NG一样,发一个新进程,新请求分配到新进程,老请求处理完已有的请求退出
- 高扩展:承接业务流量和请求,包含业务逻辑,修改业务逻辑通过 AWS Lambda FAAS 实现
运维 - 业务松耦合、协议紧耦合:
- 应用监视,提供分析数据:
- 弹力设计保护后端:
架构
安全 - 加密数据
- 校验用户请求
- 检测异常访问
部署升级策略
- 停机:事前挂公告,选一个用户访问少的时间段来做
- 蓝绿:部署好新版本后,把流量从老服务切过来;分预发和生产环境,需要双倍资源;
- 不停机
- 灰度:一部分用户切到新版本,没问题后继续升级
- 功能
- 切网关流量:90%的请求流向老版本,10%的请求流向新版本
- 用户:内部用户、一般用户、大客户
- AB测试:同时上线两个版本,做相关的比较,用于测试应用功能表现,如可用性、受欢迎程度、可见性
- 质量
- 网站UI大改版、推荐算法更新、流程改变
如果服务有状态,比如缓存,停机和蓝绿部署都有问题
灰度、AB测试 选择用户
- 浏览器Cookie
- 查询参数
- 地理位置
- 技术支持 如浏览器版本、屏幕尺寸、操作系统
- 客户端语言
性能设计
缓存
使用原因:加速数据访问
数据库四种操作
- insert, update, delete:索引太多数据太多,操作才会变慢
- select:join、group、order、like 语义耗性能,大多应用读多写少
分布式系统中的远程调用会消耗资源,网络开销导致整体响应时间下降
网络质量不总是好的
缓存模式
- Cache Aside更新;
- 失效:Cache - DB - Cache
- 调用方把数据载入缓存
- 命中:Cache
- 更新:DB- Cache Expire
- 脏数据:A:读-DB 、B:写-DB-CacheExpire、A:-cache; 可能低,发生条件是 读时缓存失效+存在并发写。实际数据库的写操作比读操作慢的多,而且要锁表
- 通过 Paxos 协议保持一致性
- 降低并发时脏数据的概率,FaceBook使用
- 为缓存设置过期时间
- 失效:Cache - DB - Cache
- Read/Write Through:缓存代理更新数据库的操作
- Read Through:失效时,缓存把数据库数据载入缓存
- Write Through:更新时,命中缓存,更新缓存,缓存把数据更新到数据库
- Write Behind Caching
- 更新时,只更新缓存,不更新数据库,缓存异步批量更新数据库,批量更新时可以合并对同一个数据的多次操作
- 问题:数据不是强一致的,可能会丢失;实现逻辑复杂,需要track 哪些数据被更新了,需要刷到持久层,操作系统当内存不够或进程退出时才刷缓存
缓存设计重点
- 缓存是一个内存和IO密集型的应用,内存要大,网络宽带也要够好;如果内存很多,需要用分片技术把缓存分布到不同的机器上,保证我们的缓存集群可以不断的scale下去
- 缓存的命中率高(>80%)说明缓存有效,热点数据只是少数
- 缓存会有更新延迟,通过牺牲强一致性来提高性能
- 缓存过期时间要好好设计
- 缓存一般用 LRU 策略,内存不足时淘汰数据
- LRU 读写时需要加锁,除非单线程无并发。key-value 这种非顺序的数据结构需要维护顺序,在读缓存时,改变被访问数据的排序
- 加爬虫保护机制,或者用多租户将用户和三方开发者的缓存系统分离开;避免热点数据是爬虫得到的古老数据
异步处理
异步利于统一规划,达到整体最优
- 延时允许用户反悔
- 避免被打断和驱动
异步任务处理设计
- 追加记录请求,响应 ”收到请求,正在处理中“
- 任务派发
- Push:适合做调度,它需要知道下游工作节点的情况
- Pull:不关心下游,减少复杂度
- Push 可以像物流那样把相同商品的订单合并起来,打包让下游一次处理掉;也可以把同一个用户订单中的不同商品拆成多个订单
事件溯源
数据库中一个数据的值不知道是怎么得出来的,可以像银行存折一样,追加记录操作内容,也能看到每笔记录后的余额
- 可以提高性能、响应时间、事务一致性,保留了启动补偿操作的完整记录和历史记录
- 当代码中出现BUG需要做数据订正,只需要把所有事件重新播放一遍就好了
- 事件不可变,执行追加写操作,可以异步进行事件处理
- 事件只记录不会直接更新存储,简化实施和管理
- 事件溯源不需要直接更新数据存储中的对象,防止并发更新冲突
分布式事务
强一致的场景不多,可以使用异步达到一致性
- 先付钱,拿小票去领货
- 先消费,拿账单去付钱
这时需要一个交易凭证 - 凭证需要很好的保存下来,不然事务做不下去
- 凭证处理幂等性,不然重试会多次交易
- 事务无法完成,需要做事务补偿
设计要点 - 事件驱动
- 可能因为故障导致任务没有被处理,如消息丢失、没有通知到、通知到了没有处理;异步通知需要处理完成后,给任务发起方发回传状态
- 发起方需要定时任务,把超时没有回传状态的任务重做一遍,可以认为是异步系统的对账功能。处理方要支持幂等性
- 异步处理时,需要一步步处理,不到最后一步,都需要回滚
- 运维时需要监控队列任务积压,快速扩容或对前端限流
- 异步任务的本质是被动任务变主动任务,对任务进行调度和统筹管理
- 事件溯源
数据库扩展
读写分离
一个写库,两个读库:所有服务写一个数据库;服务A、B走从库A,服务D、E走从库B,服务C在从库A和从库B中做轮询
优势
- 易实现:数据库的master-slave 配置和服务框架中的读写分离都很成熟
- 业务隔离:一个业务把数据库拖死不会影响所有业务
- 分担读负载,读操作最耗CPU
劣势 - 写库单点故障,不适用于交易型业务
- 数据库同步不实时,强一致性的读写操作需要用写库
CQRS Command and Query Responsibility Regregation 命令与查询职责分离
- 写 Command:业务逻辑更重;返回执行状态
- 读 Query:基本做数据整合显现;返回结果数据
优势 - 分工明确
- 提高系统性能、可扩展性、安全性,在系统演化中保持灵活性
- 逻辑清晰,能看到系统中哪些行为导致了状态变化
- 从数据驱动,转到 任务驱动 / 事件驱动
把 Command 改成 Event Sourcing,只需要记录不可修改的事件,通过回溯事件得到数据状态,可以把写操作完全简化掉,也变成无状态的,可以降低整个系统的副作用,得到更大的并发和性能
分库分表 Sharding
影响数据库最大的两个问题
- 操作数据库:业务优化 1. 简化业务,不要做太多的关联查 2. 做报表或搜索的操作,将其移到更合适的地方,ElasticSearch 来查询,Hadoop /数据分析软件做报表分析
- 库中数据太大
分库
- 分库策略:地理位置、日期、范围、Hash算法,把数据分三个库中
- 数据访问层问题产生分片策略
- 多租户:电商平台的商家中心可以按商家ID来分
- 按数据种类分:电商平台的商品库按类目来分,商家按地域来分
- 范围,如时间;分页查快:电商平台的订单中心按月份分表
还应考虑 应用程序的业务要求及数据使用模式
- 分片必须只考虑业务,不要做Hash分片
设计重点
- 只考虑了业务层的数据扩展,没讲数据库引擎的水平扩展
- 一个服务一个库,服务间通过接口通信
- 分片
- 水平:Sharding
- 垂直:将经常修改和不经常修改的数据分离开修改字段时不会锁表;电商系统商品描述信息不经常修改,商品库存和价格信息经常修改,可以把描述和库存价格分成两张表
- 水平分片
- 随数据库变化,定期重新平衡分片,保证均匀分布避免热点;重新平衡:1. 确保每个分片包含足够的可用空间来处理未来一段时间的变化 2. 开发用于快速分片的工具和脚本
- 如果程序必须从多个分片检索数据,可使用并行任务从各分片提取聚合成单个结果;会增加数据访问逻辑的复杂性
- 分片后的数据很难保持引用完整性和一致性(跨分片事务),尽量减少分片操作,评估是否采用两阶段提交
- 配置和管理大量分片是一个挑战,做变更时,先从产线拉出数据,根据数据计划好新的分片方式,并做好测试工作
秒杀
商家低价促销
流程
- 秒杀页倒计时
- 到时点亮按钮,可点击按钮下单
- 下单填写校验码,防止机器抢
挑战:倒计时按钮和按钮可以被点击的时间需要后端校准,一旦后端表示OK,会返回一个URL,可以点击
应对100w人同时下单的请求,可能宽带不够、需要很多机器、请求集中在同一条数据库记录
解决方案:CDN边缘节点抗流量,限流用户请求
- 引入CDN:百万用户能在同一时间打开同一页面,被十几个或上百个 CDN分担;把小服务部署到CDN节点上,小服务告诉前端开始时间,统计在线人数,将等待人数回传,可统计全网在线人数
- 假设大约100w用户在点下单按钮,CDN上的小服务按 0.02% 的量把用户放到数据中心,1w人放过2个,剩下的9998直接返回秒杀结束,放过的200个人抢100个商品,也就是200TPS
12306 无法预知用户买的票,不好过滤用户,而且用户在买票前有很多查询操作,它的方案是:分批在不同的时间段放票/ 预售,先把购票输入系统中,做统筹安排/ 实在不行,抽签
双十一,想尽可能地卖出商品,要多收订单、不超库存、银行支付、库存查询和分配,就需要做高并发的架构和测试,分布式弹力设计做好
有时,边缘化方案更好,尤其是地域特征的业务,如 外卖、共享单车、打车
边缘计算
数据中心:把所有服务放在一个机房中集中处理用户请求
- 便于管理和运维
- 便于服务间通信,有很好的网络保障
CDN是边缘式的内容发布网络,把静态内容推到离用户更近的地方,获得更好性能;让CDN有可定制的计算能力,可以为数据中心带来更好的性能
边缘计算产生
- 趋势
- MB:新闻资讯
- GB:用户产生数据,写博客、发帖子、拍照片视频
- TB:线下服务走到线上,如外卖、叫车;APP收集你的行为
- PB:摄像头识别线下活动,如车牌;传感器收集线下数据,如农业、水利
- 成本
- 几十万用户,百级 QPS
- 上百万用户,千级 QPS
- 上千万用户,十万级 QPS
- 上亿用户,百万级 QPS
运维100个 50台的小数据中心,难度低于 5000台服务器的数据中心,适用于地域性业务,用边缘节点处理高峰流量
业务场景
- 实时响应业务:人脸门禁系统,共享单车的开锁
- 处理简单业务逻辑:秒杀、抢红包
- 实时监控设备:线下数据采集和监控
- 收集并结构化:把视频中的车牌信息抠出来,转成文字,回传数据中心
- P2P去中心化的应用:作为服务发现服务器,让本地设备进行 P2P 通信
- 云资源调度:允许用户使用不同生产商的云存储服务,使用不同生产商单功能相同的API服务,流量接入方可以调度流量
- 云资源整合:把语音转文字的API和语义识别的API整合,简化开发成本
关键技术 - API Gateway:
- Serverless / Faas:服务函数化,提供比微服务更细小的程序单元
编程范式
通过了解主流编程语言的特性,总结编程语言的本质,知道什么样的代码编写方法可以写出更通用,可重用,便于组合扩展的代码。
C语言是最长久的语言,几乎现在看到的语言都是以C语言为基础扩展来的,它的优点是:
- 静态弱类型语言,先声明变量类型再使用变量,类型间可以隐式转换
- 变量类型的组合,使用结构体 struct,声明成新的数据类型
- 变量类型的抽象,用 typeof 关键字定义类型的别名
- 流程语言,结构化程序设计,有变量作用域和递归功能
- 参数传递一般是值传递,也可以指针传递
- 通过指针,可直接操作内存,这引入了非常大的编程复杂度
- 编译预处理让C语言可以跨平台
缺点是:不利于代码组织和功能编程,而我们需要业务抽象型的语言,花更多时间解决业务问题而不是计算机问题。算法要通用,即实现抽象隔离,让世界变得简单
一个通用算法要适配所有数据类型+数据结构,但C语言:
- C语言不做类型检查,没有泛型这样的类型抽象支持,在进行数组传递时需要添加元素个数和数组大小参数;
- 不同数据结构的内存分配和释放不一致,对象复制涉及深拷贝和浅拷贝;
- 实现泛型算法时,不好确认哪些东西封装或抛给调用者
适用于:开发运行快,对资源利用率高的程序,底层灵活(直接操作内存)且高效
类型和泛型
《C++语言的设计和演化》
C++对C的改进
- 使用引用解决指针问题
- namespace 解决命名空间冲突问题
- try-catch 解决返回值编程文图
- class 解决对象的创建、复制和销毁问题,继而解决结构体嵌套时可以深度复制的内存安全问题
- 重载操作符实现操作上的泛型
- 模版 template、虚函数多态和运行时识别,实现泛型和多态
- RAII、智能指针,简化资源释放
- STL 实现数据结构和算法
C++ 泛型实现:
泛型的抽象:数据类型要符合通用算法,最小需求是什么?
- 类型泛型:
- 引入类,实现用户自定义数据类型和内建数据类型一致
- 重载操作符:>、<;
- 数据结构泛型:
- 模板:根据实际使用的类型在编译期生成模板代码。通过一个虚拟类型做类型绑定,避免类型转换,写出类型无关的数据结构;
- 迭代器操作数据结构内的元素
- 算法泛型(遍历只适合顺序结构,其返回值不一样):虚函数和运行时类型识别
- 虚函数多态,支持同一类的类型泛型;运行时识别可对具体类型进行特殊处理
类型系统和泛型
类型:将 数值和表达式 归类,定义操作方式
编程语言一般两种类型
- 内建类型:int、float、char
- 抽象类型:struct、class、function;抽象类型在程序运行中,可能不表示值
类型系统间的差异在于编译期的语法,运行期的操作实现方式
编程语言类型可以保障
- 语言安全
- 利于编译器优化:编译器使用值的静态类型,决定存储区和最佳算法
- 代码可读
- 抽象化:类型让接口更有语义,更直观易懂
语言都有类型系统,动态语言使用额外字段标记类型,在编写时也需要脑补变量运行时的类型,所以每个语言都需要一个类型检查系统。
静态类型在编译期进行语义分析;
动态类型在运行时做类型标记和检查
泛型的本质
- 类型是对内存的抽象,不同类型有不同的内存布局和内存分配策略
- 特定类型有特定操作
达到泛型,需要 - 标准化内存的分配、释放和访问
- 类的构造,析构,拷贝构造,重载赋值操作符,标准化内存的分配,释放和复制
- 标准化类型的特有操作,标准化接口回调特定操作
- 重载操作符,标准化类型的比较等操作
- iostream, 标准化类型的输入输出控制
- 模版,为不同类型生成类型专属代码
- 迭代器标准化容器遍历
- 面向对象的接口(虚函数),标准化特定类型在特定算法上的操作
- 函数式,标准化不同类型的特定操作
泛型的本质:屏蔽数据和操作数据的细节,让算法更为通用,让开发者更多关注算法结构,而不是类型处理
函数式
编程工作更多的是解决业务问题。函数式编程使用函数拼接业务逻辑
函数式编程
函数:定义输入数据和输出数据的关系,即映射 mapping
特征
- stateless:不改变输入数据
- immtable:返回新数据集
优势 - 没有状态,适用于并行执行、重构代码、没有执行顺序问题
- 惰性求值:需要编译器支持。延迟计算,stream流(操作链)在调用终止操作时才触发计算
- 确定性:因为数据状态不会更改,则对于相同输入,重复执行可得到相同输出
实现函数式的技术 - first class function 头等函数:函数像变量一样被创建修改传递返回,或是函数中嵌套函数
- tail recursion optimization 尾递归优化:每次递归重用 stack
- pipeline 管道:每个功能只做一件事,则程序的拼装会变得简单和直观
- recursing 递归:简化代码,通过描述问题实现
- currying 柯里化:减少函数参数个数,将一个函数的多个参数封装成多个函数,每个函数只传少量参数
- higher order function 高阶函数:适用于装饰器,输入函数,返回函数的封装
编程理念:函数当变量来用,关注问题描述-表达式-干什么,不关注如何实现,代码更易读;声明式函数;灵活组合
编程思维:what(业务) than how(控制)
函数式编程
# 3辆车有70%的概率移动一步,一共5次机会
# 普通编程
from random import random
time = 5
car_positions = [1, 1, 1]
while time:
# decrease time
time -= 1
print ''
for i in range(len(car_positions)):
# move car
if random() > 0.3:
car_positions[i] += 1
# draw car
print '-' * car_positions[i]
# 函数式:没有临时变量,没有共享变量,通过参数和返回值来传递函数
from random import random
def move_cars(car_positions):
# `car_positions` 数组中的每个元素 `x`,如果随机生成的一个小数大于 0.3,则将 `x` 加一;否则保持不变
return map(lambda x: x + 1 if random() > 0.3 else x,
car_positions)
def output_car(car_position):
return '-' * car_position
def run_step_of_race(state):
return {'time': state['time'] - 1,
'car_positions': move_cars(state['car_positions'])}
# 每辆车的位置信息转成字符串,用换行符连接后打印
def draw(state):
print ''
print '\n'.join(map(output_car, state['car_positions']))
def race(state):
draw(state)
if state['time']:
race(run_step_of_race(state))
race({'time': 5,
'car_positions': [1, 1, 1]})
# 管道,平铺函数调用,避免嵌套函数
# 数组中N个数字:filter 偶数,每个数字*3,打印
pipeline_func(nums, [even_filter,
multiply_by_three,
convert_to_string])
# pipeline 函数可以实现成
def pipeline_func(data, fns):
return reduce(lambda a, x: x(a), fns, data)
函数三件套
- Map
- Reduce
- Filter
修饰器
和Java Annotation 很像,扩展现有函数功能,内部函数作为参数传递给外部函数,由外部函数来调用,实现函数的组合拼接,适用于分离非业务,控制型(如 for-loop、函数路由、日志打印、求函数运行时间)的代码
def hello(fn):
def wrapper():
print "hi,%s" % fn._name_
fn()
print "bye,%s" % fn._name_
return wrapper
@hello
def A():
print "I am A"
A()
# 输出
# hi,A
# I am A
# bye,A
面向对象
函数式编程不处理状态,面对对象来接收、处理、转发数据
面向对象三大特性
- 封装
- 继承
- 多态
对象(类实例)含数据,属性,方法。关注接口间关系,通过多态提高软件的重用性和扩展性
23种设计模式,表达了两个核心理念: - 面向接口,而不是实现
- 组合优先于继承
桥接模式:四个物体:木桌子,木椅子,塑料桌子,塑料椅子;四个属性:燃点,密度,价格,重量
Material材质类(Wood木,Plastic塑料):燃点、密度;Furniture家具类(Table桌子,Desk椅子):价格、体积;Furniture构造函数中定义具体的Material是Wood还是Plastic;于是,根据密度和体积,可以计算出价格
策略模式(最经典的设计模式):电商订单打折HappyHourStrategy还是不打折NormalStrategy
interface BillingStrategy { double getActPrice( double rowPrice ); } class NormalStrategy/HappyHourStrategy impl BillingStrategy
class OrderItem{ billStrategy; }
class Class { orderItems; add() payBill( orderItems.for.billStrategy.getActPrice ) }
分离定价策略和订单处理流程,配置不同商品使用不同的价格计算策略
现实中还会有会员价,打折卡,商品打包价,可以使用函数式pipeline来实现
代理模式:try finally 的资源释放
IOC控制反转:通过一种标准,让业务更规范
- Switch开关{ Light }控制Light灯
- 开关声明电源接口,灯和电视适配电源接口 ISwitchable { turnOn{} turnOff{} } Switch{ ISwitchable } Light/TV impl ISwitchable
优点
- 面向对象和数据库映射,更多关注模型设计、关注对象间的交互
- 根据业务特征,形成高内聚对象,有效分离抽象和实现,增加可重用性和扩展性
- 大量设计模式和原则
缺点 - 鼓励封装和状态,屏蔽底层细节,易出现并发问题
- 通过对象达到抽象结果,代码分散在不同的类中,需要代码粘合层;
基于原型编程
直接使用对象,主流语言是 JavaScript
基于类的语言关注类和类间关系,基于原型的语言关注对象实例的行为之后才关注如何将对象划分到最近的使用方式相似的原型对象
基于原型的对象提倡运行时修改原型
构造对象:1. 复制已有的对象 2. 扩展空对象
JavaScript的原型
Go语言的委托模式
编程本质和逻辑
Programs=Algorithms ↑ (Logic + Control ↑)+ Data Structure
- 数据结构设计的好,算法会变得简单,好的通用算法可以用在不同的数据结构上
- 数据结构不复杂,复杂的是算法(业务逻辑处理)
- 控制部分描述如何使用逻辑,是解决问题的策略,可以标准化,如 遍历、查找、多线程、并发、异步;
- Logic 才是有意义的(What),Control 只能影响 Logic 的效率(How)
- Control 需要处理数据,则通过泛型标准化 Data Structure;Control 需要处理业务逻辑,通过标准化接口/协议来标准化 Logic
- 通过逻辑分析,提高算法效率,如使用 自上而下的控制、并行执行
编程范式的本质就是:有效的分离 Logic(接口)、Control(函数式)、Data(泛型)
Logic 和 Control 分离,代码会更易改进和维护
分离 Logic 和 Control :
- State Machine:状态定义,状态变迁条件,状态的Action
- DSL(Domain Special Language):HTML,SQL,Shell,AWK,正则
- 编程范式:面向对象,函数式编程
编程范式分类:声明式what,面向对象how
人类左脑
- 理性分析
- 数据证据
- 线性思维
- 陷入细节
- 具体化的
人类右脑(抽象):哲学家,艺术家,创造理论知识的人 - 直觉型
- 想象力
- 非线性
- 宏观思维
区块链
机器学习
分类
- 监督学习:样本数据有标签,需剔除噪音
- 从历史数据预测未来,如股票、垃圾邮件
- 非监督学习:找关系
基本方法
- 找规律(特征点)
- 将特征点抽象成数学中的向量,通过数学公式表达关系(数学建模)
算法
监督学习
- 决策树:自动放贷、风控
- 朴素贝叶斯分类:
- 最小二乘法,线性回归
- 逻辑回归,用变量表示二项式结果。可用于信用评分、计算营销活动的成功率、预测产品收入
- 支持向量机。用于图像性别检测、图像分类
- 集成方法。构建分类器,纠错输出编码、Bagging、Boosting
非监督学习 - 聚类
- 主成分分析,压缩、简化数据
- 奇异值分解。面部匹配身份
- 独立成分分析。解释随机变量、测量值或信号集中的隐藏因素
课程、图书
- 网易公开课
数据安全
- 跟踪依赖包的安全声明
- 建立流程,快速更新漏洞版本
- 自动化测试 Metersphere Selenium
- 建立多个安全层
- 关键数据隔离,做安全审计、监控、访问限制(访问用户、访问次数)
- 敏感词过滤、传输加密、传输时通知用户和管理员
- 设立风控基金
故障处理
故障发生时:定位、恢复
- 重启、限流
- 回滚
- 降级
- 紧急更新
故障前
- 自动化监控用户功能,指定故障检测、处理、恢复手册和工具
- 设立故障等级
- 故障演练
- 灰度发布
复盘
- 处理过程
- 原因分析
- Why?
- 故障监控方式,钉钉短信
- 为什么花15min,才知道是慢SQL的问题?
- 为什么监控系统没监控到NG异常
- 为什么要重启数据库
- 为什么故障之前没有发生
- 为什么上首页时没有做性能测试
- 为什么使用这个高危的SQL语句
- 上线过程为什么没有DBA评审
- 改进计划
- 优化故障日志、定位时间(自动化)、处理方式(信息透明、处理人员得当)、开发中的问题(CR 和测试、软件架构和设计、记录隐患、风险计划)、团队能力(技术能力,严谨的工程意识)