IOC:解耦
AOP:适用于面向对象方法无法抽象的业务逻辑,如:日志,安全、事务、应用程序性能管理(APM)等
实现方式:1.对字节码重新编译,cglib实现
2.定制类加载器,在类加载时对字节码进行补充,jvm自身提供的java agent机制
3.invocationhandler
SOA实现
1.Web Service
SP WSDL、UDDI (注册) 服务注册中心 UDDI(查询服务) consumer
SP SOAP consumer //SOAP通常是一种在http(s)通道上传输XML数据来实现的协议
缺点:
1.依赖中心化服务发现机制
2.使用SOAP,通常用XML格式来序列化通信数据,冗余大,协议太重
3.服务化管理和治理设施不完善
2.ESB 企业服务总线
通过对总线的流程编排和协议转换能力来组合实现业务处理能力
缺点:
1.虽然是SOA的一种实现方式,却更多体现了系统集成的便利性,提供组合业务流程服务
2.组合在ESB上的服务本身可能就是一个过重的整体服务
3.ESB视图通过总线来隐藏系统内部复杂性,系统内部复杂性仍然存在
4.对于总线本身的中心化的管理模型,系统变更影响的范围经常会随之扩大
微服务特点
1.每一个职责单一的功能放到一个独立的服务中
2.每个服务在一个单独的进程中
3.每个服务有多个实例在运行,每个实例可以运行在容器化平台内,达到平滑伸缩的效果
4.每个服务都有自己的数据存储,独享的数据库、缓存、消息队列等资源
5.每个服务都有自己的运营平台,团队,高度自治
6.每个服务都可根据性能需求进行水平伸缩。
传统单体架构特点
1.所有模块化组件混合后运行在一个服务JVM进程中
2.可对多个模块化组件的整体JVM进行扩展,不能对某个模块化组件进行水平扩展
3.某个模块组件发生变化的时候,整个项目需要重新编译
4.久而久之,模块化间的依赖会不清晰,互相耦合、依赖成为家常便饭
SOA强调异构服务之间的协作和契约
微服务强调有效拆分应用,实现敏捷开发和部署,减少团队沟通,缩小变更迭代影响范围
使用Docker进行容器的敏捷扩容缩容、 倡导 去中心化治理
微服务交互模式
1.读者容错模式:C对SP返回的消息进行过滤提取有效消息,多余抛弃,而不是抛错处理
只有完全不能识别接收的消息,或无法通过消息继续处理流程时才抛错
2.消费者驱动契约模式:定义服务化中服务之间交互接口改变的最佳规则。
服务契约分为:提供者契约、消费者。。、消费者驱动。。
提供者契约:最常用。以提供者为中心,提供什么功能消息格式,消费者无条件遵守。
消费者契约:对某个消费者的需求进行更精确的描述,用来表示现有的提供者契约,
也可以用来发现一个尚未明确的提供者契约(提取有效数据)。
消费者驱动契约:代表服务提供者向所有当前消费者承诺遵守的约束,提供者无论在
什么情况下都不应打破。(如重复入账拦截等)
微服务的分解和组合模式 DRY原则(don’t repeat yourself)
1.服务代理模式:根据业务需求选择调用后端的某个服务,代理可以对后端服务输出进行加工
可用于平滑的系统迁移
2.服务聚合模式:根据业务处理需要,以一定顺序调用依赖的多个服务,对返回数据进行组合、
加工、转换,最后以一定形式返回给使用方 ui(交易服务、库存服务…)
3.服务串联模式:类似于一个工作流,最前面的服务用于接收请求和响应,串联服务以后再
与一串服务进行交互,串联服务未完成之前会进行阻塞。 尽量不用,用的话不推荐层级过多
(ui 购买 交易服务 扣减库存 库存服务)
4.服务分支模式:用于组合以上三种模式 当多个服务依赖其中一个 基础服务时,基础服务的
所有机器有一台宕机了(如8台),那么影响会大于1/8. 分支模式会放大服务依赖关系
1-3-6、1-3-5-6 两层服务依赖服务6 ,尽量不要使用
5.服务异步消息模式:核心服务使用同步,非核心以外的使用异步消息队列进行异步化
6.服务共享数据模式:两个服务共享数据缓存,不推荐使用,仅在如下场合使用。
单元化架构:性能要求较高时,共享可以减少性能损耗,
遗留的整体服务:反规范化可能导致数据一致性问题时,没有完全把握,选择保持现状
微服务容错模式
1.舱壁隔离模式
①微服务容器分组
如:每个服务节点服务池分为三组:准生产环境、灰度环境(普通流量)、生产环境(VIP+大部分)
②线程池隔离
如:为了避免微服务成本过高,会出现一个线程池共用多个功能,一个功能流量过大会阻塞其它功能
2.熔断模式
当服务输入负载迅速增加时,若不熔断可能导致雪崩效应
3.限流模式 一般控制访问的并发量
①通过原子变量 计数器,超过阀值,拒绝后续请求,等到下一个单位时间重新计数
②令牌桶:一个线程在单位时间生产固定数量的令牌,每次请求从桶中拿出一个令牌,拿到令牌
才有资格执行请求调用,否则等待拿到令牌再执行,或直接丢弃。
③信号量
4.失效转移模式
快速失败 、切换备份 、 重试
微服务项目依赖关系
一方库:服务内部,JVM进程内依赖jar包
二方库:RPC、网络通信调用的服务的jar
三方库:所依赖的其它公司或者组织提供的服务或者模块。
项目层级结构:服务导出层、接口层、逻辑实现层
服务导出层(war):web.xml、Spring环境 ,最后会打包成war包,包含服务实现jar,接口jar
以及web项目导出RPC服务所需的配置文件等。
服务接口层(jar):业务接口、DTO(数据传输对象)、枚举类。最后打包jar,发布到mvean服务器
服务逻辑实现层(jar):业务实现类、外部服务包装类、DAO。
PS:在任何情况下,本地事务中都不应该调用远程服务,会拖长事务、导致占用过多数据库连接,
->本地服务层—(数据库事务、保证强一致性)–>DAO
服务逻辑实现层 业务流程层(最终一致性)|
->–外部服务层
微服务项目的持续发布 需要实现 自动化持续部署和持续集成
代码管理、自动编译、发布QA、自动化测试、性能测试、准生产部署和测试、生产环境发布等
水平拆分:单一节点无法满足性能要求,扩展为多个节点,多个节点功能一致,一个节点一部分,即服务池
垂直拆分:按照功能拆分。每个功能职责单一、简单,使维护容易、简单、安全,更易于版本迭代、敏捷开发
一致性问题:
1下单扣库存、
2同步调用超时(服务调用超时反馈给C,技术难以实现)、
3异步回调超时、
4掉单(一个系统存在请求,另外一个不存在)、
5系统间状态不一致(两系统都存在请求但是请求state不一致)
6缓存数据库不一致(强一致还是弱)、
7本地缓存节点间不一致、
8缓存数据结构不一致
解决一致性问题思路
1.ACID(酸) 原子性、一致性、隔离性、持久性
2.CAP 一致性、可用性、分区容忍性
3.BASE(碱) 基本可用性、软状态(一段时间内可以不一致)、最终一致性
PS:软状态可以用log来记录(机械硬盘追加有较好的性能)、也可以用数据库记录(通过行级锁实现)
能用向上扩展并运行关系型数据库保证强一致性的,都不是问题
若成本过高,可用开源关系型数据库进行 水平伸缩 和 分片 ,相关数据放到同一个片上
业务规则限制,无法放到一个片上,就需要实现最终一致性
分布式一致性协议
应用程序、事务管理器(全局管理者)、资源管理器(参与者)、通信资源管理器(参与者)
两阶段提交协议
1.准备阶段:协调者向参与者发起指令,参与者评估自己状态,若评估可以完成,则写repo,undo
日志,然后锁定资源,执行操作,但是不提交最终结果。
2.提交阶段:若每个参与者明确返回准备成功,则协调者向参与者发起提交指令,参与者提交资源变更
的事务,释放锁定资源,若任何一个参与者明确返回失败,则协调者发起中止命令。参与者取消事务
执行undo日志。
缺点:阻塞,对任何一次指令都必须受到明确响应;单点故障。协调者宕机,参与者会一直阻塞,尽管
通过重新选举新的替代,但是当协调者发出命令给一个协调者,此时两者都宕机,则新的协调者无
法处理这种情况;脑裂,协调者发送指令,有的受到了命令,有的没有收到,多个参与者状态不一致
三阶段提交协议
1.询问阶段:协调者询问参与者能否可以完成指令,只回复能或不能,这阶段超时会导致中止。
2.准备阶段:若询问阶段所有节点返回可以执行操作,则协调者向参与者发送执行请求,然后参与者
写repo,undo日志,执行操作不提交,若询问阶段任意参与者返回不能执行,则协调者发送中止
3.提交阶段:若准备阶段,每个参与者返回成功,则发起提交指令,释放资源。若任一返回失败,则
中止,取消事务,执行undo日志
相对而言。询问,确保更早发现无法执行操作而需要中止的行为。增加了超时,不会永远阻塞和锁定资源。
TCC协议(try-confirm-cancel) 简化版三阶段提交协议,没有解决极端情况下出现不一致和脑裂问题
保证最终一致性的模式: 操作id、查询id、批量ids
1.查询模式:任何服务操作都需要提供一个查询接口用来输出执行状态,然后根据不同状态进行不同操作。
每个服务操作都需要唯一的流水号标识,单笔查询操作必须提供,推荐使用,批量则根据需求提供,
但是必须有合理分页机制,限制分页大小,吞吐量容量评估、熔断、隔离、限流等。 (2-5案例处理)
操作id、重试操作id、取消操作id
2.补偿模式:系统处于不正常的状态需要修正有问题的子操作,可能需要重新执行子操作,或者取消
自动恢复、通知运营、技术运营(数据库变更,代码变更,最严重,尽量避免)
3.异步确保模式:最大好处是能对高并发流量进行消峰,如电商系统物流、配送,支付系统计费、入账
4.定期校对模式:实现的关键是系统中需要一个自始至终唯一id (4、5案例处理)
持久型:使用数据库表自增字段或者Sequence生成,为了提高效率,每个节点可以缓存一个批次
的id,重启可能会丢失一部分,但是并不会产生任何问题。
时间型:由 机器号、业务号、时间、单节点自增id组成,由于时间精确到s或ms,因此不需持久就能
保证id唯一,粗略递增等。
5.可靠消息模式:
①消息的可靠发送:消息发送之前,持久化到数据库,标记待发送,发送成功以后标记为已发送
这里第二种实现方式,持久消息数据库独立,发送给第三方消息管理器,
未发送消息查询业务是否需要继续发送
②消息处理的幂等性:有重试机制以后,会产生重复的问题,就需要进行处理,如:通过唯一id
6.缓存一致性模式:核心需求 亿级读 (678案例处理)
若性能需求不是非常高,尽量用分布式缓存,而不是本地缓存
写数据必须完整,若不完整则删除,然后重试
使用缓存牺牲了一致性,为了提高性能,数据库与缓存只需要保证弱一致性
读的顺序是 先缓存再数据库 写 先写数据库再写缓存
超时处理模式
微服务的交互模式
1.同步调用:服务1调用2,一直等待2完成或者超时为止
2.接口异步调用:服务1调用2,2立刻返回受理结果,1继续做其它事情,2完成回调通知1
如商品售卖成功后,商户入账入账。
3.消息队列异步处理:服务1调用2,通常不需等待2返回结果,如交易系统与物流,可以消峰
同步与异步的抉择
1.尽量用异步替换同步 业务功能角度,即用户与使用方交互模式出发
2.能用同步解决的问题不要引入异步 技术和架构角度,过于复杂
交互模式下超时问题解决方案
1.同步调用模式下的解决方案
对外接口会提供服务契约 1.成功、失败 2.成功、失败、处理中
1.两状态同步接口
①超时发生在同步接口与服务1之间 即使用方使用接口调用服务1,服务1调用2
失败,则重新调用或放弃
若查询模式返回的是未知请求,那么是服务1请求没有收到,服务使用方应该
使用同一个请求id重试。
②超时发生在服务1与2之间
应该采用快速失败策略,然后服务1调用2的 冲正接口(判定之前是否收到了请求)
收到了,第二次通过接口调用服务1,服务2的操作应该回滚。,未收到则忽略。
2.三状态同步接口
①同两状态的①
②这时,应该返回处理中,将同步调用转为异步,达到最终一致的效果,即服务1根据
服务2的查询接口状态,进行状态补偿,完成后等待使用方进行查询
2.异步调用模式下的解决方案
对外接口会提供服务契约 受理、未受理
①同上①
②服务1根据服务2的查询接口状态,进行状态补偿,完成后 回调通知服务调用者
③异步调用回调超时,一般会使用一个通知子系统,服务1需要保证通知一定可送达
是否成功以对方写回状态为准。
3.消息队列异步处理的解决方案
①消服务1消息队列生产者与消息队列间超时 通过可靠消息解决
②消息队列与服务2消费者超时 , 一般可通过消息队列提供的机制解决
自动增长消费的偏移量:消费者取走消息,偏移量++,消息彻底删除不存在于server
手工提交消费的偏移量:消费者取走消息,先存入数据库,然后通知已经取走消息,消息
服务器才会彻底删除消息。
超时补偿的原则
0.快速失败。
1.服务1调用2,2告知1已接收,然后服务2处理失败,则应该由2来进行重试
2.服务1调用2,2忙于其它任务,那么1应该持续重试
/*
第三四章pass
*/
APM(Application Performance Management)
Pinpoint、Zipkin 分布式服务调用链跟踪系统、CAT
工具 Vesa发号器
原理:全局唯一id(TraceID)、SpanID、parentspanID,在http头携带 机器号、时间、方法等制作id
消息类型
RPCPhase.P1 调用端发送请求的调用
RPCPhase.P2 被调用端接收请求
RPCPhase.P3 被调用端发送处理成功信息
RPCPhase.P4 调用端接收处理成功请求
RPCPhase.E3 被调用端发送处理失败信息
RPCPhase.E4 调用端接收处理失败请求
RPCPhase.SIB 主子线程间传递调用信息类型
TraceID、SpanID在服务间传递
1.java进程内传递 通过ThreadLocal,这样在下需要调用外部系统时们都可以得到id
2.服务间传递 使用restful风格API,在HTTP头,RPC则在序列化协议增加定制化字段
3.主子线程间传递 定制化实现线程池,用消息队列
4.消息队列的传递 ①更改消息队列底层协议(实现困难)、
②应用层增加字段,手工将id通过报文传递(侵入业务系统)、
③在二的基础上在消息队列处理库做定制化(0侵入,且不用手工加入)
5.缓存、数据库访问 ①对其服务进行二次开发,修改网络通信协议(难度大)
②封装客户端,将id访问与访问的数据关联(简单方便0侵入)
采集器实现方法
1.应用层主动推送(短期内解决方案、实现简单、侵入代码)
2.AOP推送(功能独立、可升级、方便、耦合不严重)
3.javaagent字节码增强(0侵入)
4.代理推送(Logstash)
推送的实现方法
1.kafka消息队列
2.UDP推送
//以上收集器出现问题,影响业务流程
所以一般使用异步线程池发送调用信息,且使用有界队列
处理方法 spark+hbase
调用链可视化 ECharts
#第6章
应急目标:快速恢复服务,影响最小化
应急原则:
1.第一时间恢复系统,而不是定位问题
2.有明显资金损失时,应第一时间升级
3.应急指挥要围绕目标,快速启动应急过程和快速决策止损方案
4.若短时间不能解决问题,则必须升级处理
5.应急过程在不影响用户体验的前提下,要保留部分现场和数据
Vesta 使用
git clone https://github.com/robertleepeak/vesta-id-generator.git
cd vesta-id-generator
./make-release.sh
cd ./releases/vesta-id-generator-0.0.1-release/bin
./shart.sh
测试
crul “http://localhost:8080/genid” //生成 2355484949484894654
crul http://localhost:8080/expid?id=2355484949484894654 反解成json
{“machine”:…}
//查看进程 ps -elf | grep java