Java 八股文:微服务篇
2 - 微服务篇
2.1 Spring Cloud
2.1.1 服务注册
2.1.1.1 微服务常用框架?Spring Cloud 五大组件?
1. 微服务常用框架:
- Spring Cloud:基于Spring Boot的一系列框架的集合,用于简化分布式系统的开发,如配置管理、服务发现、断路器等。
- Dubbo:由阿里巴巴开源的分布式服务化治理框架,通过RPC请求方式访问。
2. Spring Cloud 五大组件
- 注册中心 / 配置中心:
Nacos
。就像是服务的黄页,服务们都在这里登记,方便互相发现和联系,还能统一管理配置信息。- 负载均衡:
Ribbon
。就像流量分配器,能让请求均匀地分散到不同的服务器上,防止某个服务器因为请求太多而崩溃。- 服务调用:
Feign
。这个组件让服务之间的调用变得简单,就像打电话一样,直接就能联系到其他服务。- 服务保护:
sentinel
。它是一个保护机制,当服务接收到的请求太多时,它会介入,防止服务因为过载而崩溃。- 服务网关:
Gateway
。作为服务的入口,它控制着哪些请求可以进入,就像门卫,确保只有合法的请求才能访问到服务。
2.1.1.2 服务注册和发现?服务调用?
1. 注册和发现:
- 服务注册:服务提供者需要把自己的信息注册到 nacos ,由 nacos 来保存这些信息,比如服务名称、ip、端口等。
- 服务发现:消费者向 nacos 拉取服务列表信息,如果服务提供者有集群,则消费者会用负载均衡算法,选择一个发起调用。
- 服务监控:服务提供者会每隔 30 秒向 nacos 发送心跳,报告健康状态,如果 90 秒没接收到心跳,从 nacos 中剔除。
2. 服务调用:
- 添加依赖:在项目的
pom.xml
文件中添加 OpenFeign 的依赖。- 启用 OpenFeign:在服务消费者的启动类上添加
@EnableFeignClients
注解,开启 OpenFeign 功能。- 定义 Feign 接口:创建一个接口,并使用
@FeignClient
注解指定服务名,服务名与Nacos中注册的服务名一致。- 使用 Feign 客户端:在需要调用远程服务的类中,直接注入上面定义的Feign客户端接口,并调用其方法即可。
2.1.1.3 谈谈你对 RPC 框架的理解?
RPC(远程过程调用)框架允许程序像调用本地函数一样调用远程服务器上的方法。
它通过序列化和网络传输隐藏了网络通信的复杂性,使得开发者可以跨语言、跨平台进行服务调用。
常见的 RPC 框架有 Dubbo。在微服务架构中,RPC 框架用于服务间的通信,支持服务的动态发现和负载均衡。
在 Spring Cloud 中,服务间的通信通常是基于 HTTP 协议的 RESTful API,而不是传统的 RPC 机制。
2.1.2 负载均衡
2.1.2.1 你项目的负载均衡如何实现的?
负载均衡用于在多个服务器之间分配网络或请求负载,以确保没有单个服务器因过载而成为性能瓶颈。
负载均衡的实现:
- Ribbon: 客户端负载均衡器,它提供了一系列策略(如轮询、随机、响应时间加权等)来控制服务之间的请求路由。
- Feign: 声明式的Web服务客户端,简化了Web服务客户端的编写,并内置了对Ribbon的支持,可以自动进行负载均衡。
- Zuul: Netflix提供的API网关服务,它提供了动态路由、监控、弹性和安全的功能,也可以用来实现负载均衡。
实现步骤:
- 在服务消费者中引入Ribbon或Feign客户端。
- 配置负载均衡策略,如在
application.yml
中设置。- 使用
@LoadBalanced
注解启用Ribbon的负载均衡功能。
2.1.2.2 Ribbon 负载均衡策略有哪些 ?
RandomRule
:随机 选择一个可用的服务器
RoundRobinRule
:简单 轮询 服务列表来选择服务器
WeightedResponseTimeRule
:按照 权重 来选择服务器,响应时间越长,权重越小
ZoneAvoidanceRule
:区域 敏感策略,以区域可用的服务器为基础进行服务器的选择
2.1.2.3 自定义负载均衡策略如何实现 ?
- 扩展Ribbon: 创建一个继承
com.netflix.loadbalancer.AbstractLoadBalancingRule
的类,并重写choose
方法。- 注册自定义策略: 将自定义策略注册到Spring容器中,通常是通过
@Bean
注解。- 指定使用自定义策略: 在配置文件中指定使用你的自定义策略。
public class CustomRule extends AbstractLoadBalancingRule { @Override public Server choose(Object key) { ... } }
@Configuration public class RibbonConfig { @Bean public IRule customRule() { return new CustomRule(); } }
2.1.3 服务雪崩、服务降级、服务熔断
- 服务雪崩: 指一个服务的故障导致依赖它的服务也发生故障,从而引发连锁反应,最终导致系统崩溃。
- 服务降级: 指在系统负载过高或服务不可用时,临时关闭一些服务功能,以释放资源保证核心服务的正常运行。
- 服务熔断: 是一种保护机制,当服务故障达到一定阈值时,自动“断开”服务调用,避免服务雪崩。
2.1.4 服务监控
服务监控的目的:监控服务的健康状况、性能指标等,及时发现并处理问题。
监控工具:
- Spring Boot Actuator: 提供了一系列监控和管理的端点。
- Prometheus: 一个开源监控系统,可以与Spring Boot Actuator集成。
- Grafana: 一个开源的数据可视化和监控平台,可以展示Prometheus的数据。
2.2 业务相关
2.2.1 你项目中做过限流吗?怎么做的?
限流的目的: 防止系统过载,保证服务的稳定性。
先介绍业务:项目中有一个活动,到了假期就会抢购优惠券,QPS 最高可以达到 2000,平时在 10-50 之间。
- nginx 限流:使用漏桶算法,让请求以固定的速率处理请求,应对突发流量控制并发数,限制单个 ip 的链接数和并发总数。
- 网关限流:使用令牌桶算法,gateway 中支持局部过滤器。根据 ip 或路径进行限流,设置每秒填充平均速率和令牌桶总容量。
2.2.2 什么是 CAP 和 BASE?
1. CAP 定理:
一致性(Consistency):在分布式系统中的所有数据副本中,如果在某个节点上更新了一个数据项,
那么在其他节点上读取时,应该能够看到最新的数据项,即所有节点的数据保持一致。
可用性(Availability):系统在任何时候都能够响应用户的请求,即使在出现网络分区的情况下,系统仍然能够提供服务。
分区容错性(Partition tolerance):分布式系统在遇到网络分区(网络中某些节点之间的连接断开)时,仍然能够继续运行。
CAP 定理指出,在设计分布式系统时,只能同时满足其中的两个属性,而不可能同时满足所有三个属性。
2. BASE 理论:
- 基本可用(Basically Available):分布式系统在出现故障时,允许损失部分可用性,但系统仍然能够继续运行。
- 软状态(Soft state):系统的数据状态不需要严格一致,可以容忍中间状态的存在,这种状态称为软状态。
- 最终一致(Eventual consistency):系统不保证数据的即时一致性,但保证经过一段时间后,数据最终会达到一致状态。
BASE 理论是相对于传统的 ACID 事务模型提出的,它更适用于分布式系统,强调系统的可用性和容错性,而不是严格的一致性。
2.2.3 分布式事务的解决方案?
在微服务架构中,分布式事务是一个常见的问题,因为一个业务操作可能需要跨越多个服务。
两阶段提交(2PC):强一致性的解决方案,它通过准备和提交阶段来确保所有参与事务的服务要么全部提交,要么全部回滚。
三阶段提交(3PC):这是2PC的改进版本,增加了一个超时机制,以减少事务管理器在网络分区时的阻塞。
消息队列(MQ):这是一种最终一致性的解决方案,通过消息队列来异步处理事务。
当一个服务完成本地事务后,它会发送一个消息到消息队列,然后由消息队列确保消息被消费并触发另一个服务的事务。
2.2.4 分布式服务的接口幂等性如何设计?
幂等性是指执行多次和执行一次的效果相同。幂等性的实现:
- 乐观锁: 通过版本号或时间戳来控制更新操作的幂等性。
- 业务规则: 确保业务逻辑的幂等性,如检查操作是否已经执行。
- 分布式锁: 使用Redis、Zookeeper等实现分布式锁,保证操作的原子性。
2.2.5 分布式任务调度
2.2.5.1 XXL-JOB 路由策略有哪些?
- 轮询:任务按照顺序轮流分配给每个可用的执行器。
- 随机:任务随机分配给任意一个可用的执行器。
- 故障转移:任务首先尝试分配给第一个可用的执行器,如果失败,则自动转移到下一个。
- 分片广播:任务被分成多个片段,每个片段都广播到所有可用的执行器,由所有执行器并行处理。
2.2.5.2 XXL-JOB 任务执行失败怎么解决?
- 重试机制: XXL-JOB支持失败重试,可以配置重试次数和重试间隔。
- 报警通知: 配置失败报警,如邮件、短信等。
- 日志分析: 查看任务执行日志,分析失败原因。
2.2.5.3 如果有大数据量的任务同时都需要执行,怎么解决?
- 任务分片: 将大任务拆分成多个小任务并行执行。
- 资源隔离: 为不同任务分配不同的执行器,避免资源竞争。
- 限流控制: 控制任务的提交速率,避免瞬间高并发导致系统压力过大。
2.2.6 分布式锁的三种实现方式?
#java##后端开发##八股文#
基于 MySQL 的分布式锁
创建锁表: 在MySQL中创建一个表,通常包含一个唯一的主键ID或唯一索引字段。
获取锁: 当需要获取锁时,尝试插入一条记录,如果插入成功(即没有违反唯一性约束),则认为获取了锁。
释放锁: 完成操作后,删除对应的记录以释放锁。
基于 Redis 的分布式锁
SET NX: 使用
SET NX
命令尝试设置一个键,如果键不存在,则设置成功,获取锁;如果键已存在,则获取失败。Lua脚本: 为了确保原子性,通常使用Lua脚本来实现锁的获取和释放,这样可以在一个原子操作中完成检查和设置。
设置过期时间: 为了避免死锁,设置键的过期时间,即使持有锁的进程崩溃,锁也会在一定时间后自动释放。
基于 Zookeeper 的分布式锁
创建临时节点: 客户端在Zookeeper中创建一个临时节点来表示它想要获取锁。
创建顺序节点: 如果需要多个客户端按顺序获取锁,可以创建顺序节点,Zookeeper会为每个节点分配一个唯一的序号。
获取最小序号: 客户端检查自己创建的节点是否是序号最小的,如果是,则认为获取了锁。
监听节点变化: 客户端监听比自己序号小的前一个节点的删除事件,一旦前一个节点被删除,当前客户端就获取了锁。