1. 你对微服务的理解
微服务是一种架构思想, 将应用程序拆分为小型、独立的服务,每个服务器负责处理一项特定业务功能, 服务之间松耦合、独立部署和运行。
可以使开发人员更专注于各个服务的开发和测试,降低了 系统的复杂性,提高了服务的可维护性和可扩展性,每个服务可以根据具体的业务需求选择合适的语言和工具,
提高系统的灵活性,服务之间通过轻量级的通信方式进行通信,每个服务都可以独立扩展和升级,提高了可靠性和可用性
总体来说,微服务使得程序更加灵活、可维护和可扩展,但是同时也使得需要的开发人员变多
2. openFeign 的使用
在pom 中添加相关依赖,启动类添加 @EnableFeignClients, 用于扫描FeignClient, 通过basePackage 指定FeignClient 的路径
在对应的接口中添加FeignClient 注解, value 为服务名, path 指定前缀路径, 在具体方法添加mvc 相关注解, 例如@GetMapping
2.1 openFeign 远程调用原理
在程序运行时, openFeign 会利用java的动态代理机制,基于定义的接口生成一个代理对象,这个代理对象会拦截对接口方法的调用, 并负责后续的远程调用逻辑
构建好请求后,会借助底层的http客户端, 通常是ribbot 将请求发送到远程服务的指定地址, ribbon 可以实现负载均衡,选择合适的服务实例来发送请求
接收到远程服务返回的响应后,openFeign 会按照约定的规则对响应进行解析,如果是json 格式,会转换为对应的java 对象,并最终解析后返回给调用方
3. nacos 和 sentinel 的使用
nacos 是阿里开源的服务注册与发现,动态配置的功能组件
nacos 宕机后, 5秒之内还是可以通过缓存获取到对应的下游服务的, 因为nacos 的心跳机制是5秒, 有5秒的缓存
nacos 可以通过框架集成, 通过@EnbaleDiscoveryClient 实现服务的自动注册
可以通过sdk 手动注册、可以通过命令行工具,手动注册,
当客户端向nacos 注册成功之后,会每5秒向其发送一次心跳,当nacos过了15秒依旧没有收到心跳,则会认为当前服务处于不健康状态,30秒每条收到心跳,则会认为当前服务已经宕机,就会将其剔除
同时nacos 也会进行自主侦测服务的健康情况,每20秒发送一次侦测,当侦测不到时,nacos 会将服务标记为不健康,但是不会进行剔除
3.1 nacos 自动刷新
nacos 的自动刷新主要是通过 @RefreshScope 注解实现的
当在类上添加 @RefreshScope 注解时, 当Nacos 中的注解发生改变后, 会通过发送 /actuator/refresh 端点的http 请求来触发配置的重新加载
3.2 nacos、eureka、zk注册中心的区别
nacos、eureka 和 zk 都是常用的注册中心,只是在功能上有一些不同
nacos 除了是注册中心,还提供了配置管理、服务发现等功能,其部署可以选择AP和CP,
eureka 🈯️提供注册中心,配置中心还需要使用config,同时部署只提供了AP
zk 是注册中心+分布式协调,部署上可以采用CP,适用于需要分布式协调服务的场景
3.3 nacos 原理
当一个服务启动时,会根据配置信息向Nacos 进行注册,提供服务名,端口,权重信息等,nacos 收到这些注册请求,就会根据发送过来的信息将其注册到Nacos中,通过配置的调用信息进行调研对应的服务,例如通过openFeign + loadbalancer 进行服务调用,同时nacos 提供两种健康检测方式,同时其支持自定义的健康检测
一般使用nacos 自带的健康检测机制就可以了,其分别为客户端主动上报以及nacos 自主侦测,主动上报一般用于临时服务,客户端每5秒发送一次心跳给nacos,15秒没收到,标记不健康,30秒进行提出
针对nacos 的自主侦测,则是nacos每20秒向客户端发起一次侦测,侦测不到认为其不健康,但是不会进行剔除,因为这种一般是吃就好服务
4. sentinel
sentinel 是一款流量控制和熔断降级的开源框架,主要用于保障微服务系统的稳定性和可靠性,防止系统因流量过大或异常情况而崩溃
实现sentinel 的BlockExceptionHandler, 重写其handler 方法,返回自己需要的result, 同时重写sentinelFeign,在其内的FeignBuilder 中create 方法, 改写为通过查找
FeignClient 的形式实行降级策略, 重写SentinelInbocatHandler, 将其内部的entry 改写为项目的result, 创建一个AutoConfiguration, 将其注册为bean, 通过springboot 自动装配集成到项目中,
通过EnableAutoConfiguration 指定到sentinel 的autoConfiguration
4.1 sentinel 滑动窗口
滑动窗口,是一种用于处理数据或者控制流程的概念和技术,其原理就是存在一个固定长度的窗口,其可以在一系列数据上进行滑动
对于sentinel 来说,其采样是使用了一个数组,其长度为2,根据当前实际计算timeId, 当前实际每增长一个窗口长度,timeId 就+1, 并根据这个timeId 计算窗口索引Idx
idx 在数组中进行变换,然后根据当前实际计算一个当前窗口对于的开始时间,根据索引idx, 从采样窗口获取时间窗口,循环判断去获取时间矿口old, 然后根据这个old 与当前窗口的开始时间做判断,是否通过cas更新窗口时间
5. 分布式事务
分布式事务,可以分为刚性事务和柔性事务
刚性事务,主要就是XA、2PC、3PC之类的具有强一致性的事务
柔性事务主要就是TCC、消息事务等追求最终一致性的事务
XA: 理论上两台机器无法完成,需要引入一个节点进行调节,例如XA协议的实现2PC
2PC: 主要分为两个阶段,首先是事务准备阶段,协调者向参与者发起询问请求,由参与者判断是否可以提交事务,如果都回复y,则协调者向所有参与者发送事务提交请求,
然后参与者执行本地事务,并向协调者发送ack,如果一个回复n,或者超时回复,则发送回滚操作,由参与者回滚并发送ack,属于同步阻塞,不适合大型互联网分布式场景
3PC: 主要分为三个阶段,协调者向所有参与者发送CanCommit 请求,询问是否可以操作事务,如果都回复, 那么向所有参与者发送pre 请求,参与者执行事务操作,并记录事务日志,但是并不提交,如果协调者未在规定时间内收到所有参与者Y响应,或者有N响应,则事务中断,协调者收到所有参与者的事务执行成功响应,发送doCommi,参与者提交事务,并释放资源. 否则事务回滚
tcc事务: 主要分为三个阶段, 首先是try, 用于完成业务检查和资源预留,然后是confitm 是真正的执行事务, 最后是cancel,当try 出现问题或者事务需要回滚时,执行cancel
消息事务: 利用消息中间件实现的最终一致性全局事务,生产者将半消息发送到消息队列,消息队列持久化后返回ack确认消息,然后生产者执行本地事务,同时根据本地事务执行结果向消息队列提交二次确认,为commit 时,将半消息事务标记为可投递,同时发送消费者,否则回滚事务,不会讲消息投递给消费者。如果mq长时间未收到二次确认,则会对生产者发起消息回查,生产者收到消息回查后,检测对应消息的本地事务执行结果,同时再次提交二次确认
5.1 seata
seata 是一款开源的分布式解决方案,主要用于解决在微服务架构下分布式事务的一致性问题
支持AT模式(自动事务模式),TCC模式
在AT模式下,Seata 通过代理数据源拦截业务sql, 记录前后镜像数据,一阶段提交本地事务并向TC(事务协调器)注册分支事务,二阶段根据全局事务状态决定回滚或提交
TCC模式需要业务自己实现try、confirm、cancel 三个阶段的逻辑
5.2 stata 的AT 模式
1. 解析sql, 在业务执行sql 操作时,seata 通过对数据源的代理,拦截业务sql
2. 记录前后镜像,在执行sql之前,seata 会记录数据的前镜像,执行sql之后,记录数据的后镜像
3. 一阶段提交,本地事务提交,此时会将业务数据的修改和undo_log 一并写入数据库
4. 注册分支事务,向seata 的事务协调器注册分支事务,包括事务ID、资源id等
5. 二阶段处理,如果全局事务提交,TC通知各分支事务提交,此时只需要删除对应的undo_log 即可,因为数据已经在一阶段提交时写入
6. 如果全局事务需要回滚,TC通知分支事务回滚,seata 会根据之前记录的undo_log 中的前镜像数据,将数据恢复到事务执行前的状态
6. 分布式幂等性
分布式幂等性,就是同一个接口,多次发起同一个请求,接口需要保证所得到的数据结果是准确的,比如不能多扣款,不能多插入数据等
对于分布式幂等,可以使用全局唯一id, 或者token 机制
我在项目中采用的方案是token 机制 先发放token 令牌,将其存储在缓存中,提交请求时,需要携带toke,如果为第一次提交,则token应当存在,则放行,
同时修改token 状态, 当token 不存在时,说明请求不正确,对其进行拦截,当token 不为第一次提交时,说明当前请求已存在,对其返回请求结果
比如在叁一项目中的使用.
叁一的项目存在一个google 支付校验,而这个支付是直接调用的google 支付,我们自己的服务器实际上并没有存储对应的订单信息,而我们本地又需要对应的订单
所以当时的做法就是当用户点击商品支付时,会同时向我们的服务器后台发送一个请求,携带有订单数据,比如对应的商品、安卓id 等,服务器会返回一个订单id, 并且这个订单id 会存储到redis 中
当google 支付确认支付成功后,由用户端拿着google 返回的支付成功token + 订单id 再次向服务端发起请求,根据订单id 的缓存状态判断当前订单是否为合法订单,不合法时直接拒绝此订单请求,对于合法订单进行入库统计,同时会通过canal 修改redis 中对应的订单支付结果,将其修改为支付成功
当用户发起退款时,拿着对应的订单id, 同样判断缓存中是否存在对应的订单id, 如果不存在说明token不正确,否则就发起退款请求,调用google 的退款接口发起退款,同步落入数据库中,再次通过canal 删除缓存中对应的订单token
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!