【分布式】面试官问微服务该如何回答?(假如你的项目涉及到)

这篇文档我主要以提出问题为引,逐步揭开大部分企业选用微服务架构的原因。

其次我会写下单体架构和微服务架构的区别,也就是我们自己的玩具单体项目(比如苍穹外卖,黑马点评)与企业里大型微服务项目的区别。

1. 什么是微服务架构?和单体架构有什么区别?

单体架构

我们以最常见的电商系统为例。假设这个系统有(用户业务,订单业务,库存业务,支付业务)。

如果是单体架构,所有的业务代码都会放到一个代码库里,打包为一个独立应用。所有代码运行在同一个进程中,共享一个数据库,部署时作为一个整体发布。本质:“all in one”,系统是一个不可分割的 “巨石”。

┌─────────────────────────────────────────────────────────┐
│                    单体应用进程                          │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐      │
│  │  用户模块    │  │  订单模块    │  │  支付模块    │     │
│  └─────────────┘  └─────────────┘  └─────────────┘      │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐      │
│  │  商品模块    │  │  搜索模块   │   │  日志模块    │     │
│  └─────────────┘  └─────────────┘  └─────────────┘      │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│                     共享数据库                           │
└─────────────────────────────────────────────────────────┘

想想你做的项目,苍穹外卖,黑马点评之类的,是不是所有的业务都在一个 Project 里?只要点一下运行,整个项目就都跑起来了。

这样的系统架构应对小型项目是没问题的,数据量不大,业务范围小。但是如果像淘宝,抖音这种大型项目呢?假如还采用单体架构,那么会面临以下几个问题:

  • 性能瓶颈:这些大型项目动辄数千万甚至上亿的 QPS 会直接将你的服务打崩溃!因为单体架构中,所有的业务全都部署在一个服务器里面,各种业务的数据也都放到同一个数据库里面,这样根本扛不住大流量。
  • 扩展难,改动难:单体架构在应对复杂系统时,也显得力不从心。拿抖音来举例,抖音应用太庞大了,里面包含 电商、社交、视频、生活服务 等等诸多模块,每个模块里又有好多个业务,比如视频的点赞、评论、转发。如果都放到一个服务里面,各种业务耦合到一起,那么每改动一点都需要将整个庞大的应用重新编译,部署。同时,各个团队之间的协作开发也是个问题,会很大程度上影响开发效率。
  • 创新难:技术的演进和迭代速度是很快的,抖音是17年兴起的,那时候大模型可还没火,如果抖音是单体项目。那么在老项目上进行 AI 时代技术的变革也是特别困难的,改动范围太大风险也过高,不好维护。

那单体架构就没有优点,没有可用之处了吗?当然也不是。

  • 开发效率高:如果项目较小,或者是试验型项目,那么团队可以基于单体架构快速开发以达到目的。
  • 部署简单:项目小,部署起来也简单,只需要考虑一个服务,不涉及多服务之间的管理与通信等等复杂问题。
  • 风险低:所有的数据都会放到一个数据库里面,不涉及到分库分表的数据一致性以及数据的分配和迁移等等。服务也不会涉及到多个实例的主备切换这些复杂问题,发生问题的风险比较小。

微服务架构

既然单体架构无法满足大型系统的各种需求,那怎么对系统进行扩展呢?那当然是 拆!!!把大的拆成小的

微服务架构就是将系统按业务领域拆分为多个独立、可自治的小型服务(如 “用户服务”“订单服务”“支付服务”),每个服务运行在独立进程中,通过 API(如 REST、gRPC)通信,拥有自己独立的数据库,可单独开发、测试、部署和扩展。本质:“split and 自治”,系统是多个 “小型应用” 的协同集合。

┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐
│ 用户服务进程 │  │ 订单服务进程 │  │ 支付服务进程  │  │ 商品服务进程 │
│(独立代码库)│  │(独立代码库)│   │(独立代码库)│  │(独立代码库) │
└──────┬──────┘  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘
       │                │                │                │
       └────────┬───────┴────────┬───────┴────────┬───────┘
                │                │                │
┌───────────────┴────────────────┴────────────────┴───────────────┐
│                       API网关(路由请求)                         │
└───────────────┬────────────────┬────────────────┬───────┬───────┘
                │                │                │       │
┌───────────────┘      ┌─────────┘        ┌───────┘       │
│                      │                  │               │
┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐
│用户服务数据库│   │订单服务数据库│  │支付服务数据库│  │商品服务数据库│
└─────────────┘  └─────────────┘  └─────────────┘  └─────────────┘

打个比方,小红书的微服务架构:

  • 认证服务:rednote_auth,负责用户角色的认证和权限校验,RBAC 模式。
  • 用户服务:rednote_user,负责用户的个人信息,点赞收藏内容等。
  • 笔记服务:rednote_note,负责笔记的发布删除、查看详情等等。
  • ......

这样做有助于:

  1. 独立自治,扩展性强:这些个服务都是独立的,解耦的,可以单独编译部署而不会影响到其他服务。这样就很易于扩展,也方便不同团队进行开发和维护。
  2. 分布式通信,支持高并发:不同的服务部署在不同的服务器上,不同服务通过网络进行通信(RPC,HTTP)。哪个服务流量大,就给哪个服务多分配实例,通过负载均衡把流量打到不同的服务器上。而且每个服务的数据库也是独立的,可以分别治理。比如哪个服务流量大,就给哪个数据库分库分表,不影响其他服务的数据库。
  3. 技术栈灵活:如果不同服务适合不同的技术栈,比如涉及大模型的服务适合用 Python,传统开发业务适合 Java,Golang 技术栈,那么他们可以独立开发互不影响,只需通过 gRPC 等中间件进行交互即可。
  4. 高可用,容错强:分布式微服务架构通过部署多个服务实例,即使其中一个服务或者某个服务的实例出现故障导致不可用,其他的服务也不会受到影响。

典型场景与实例

  1. 单体架构实例
  • 场景:一个创业公司的初期产品(如小型电商网站,仅支持商品展示、简单下单);
  • 优势:1-2 名开发者可快速开发,无需关注服务拆分,部署仅需将 JAR 包 / WAR 包上传到服务器即可;
  • 问题:当用户量增长到 10 万 +,订单模块压力增大时,无法单独扩展订单功能,只能升级整个服务器配置(垂直扩展),成本高且瓶颈明显。
  1. 微服务架构实例
  • 场景:大型电商平台(如淘宝、京东);
  • 拆分逻辑:拆分为 “用户服务”(登录、个人信息)、“商品服务”(商品管理、库存)、“订单服务”(下单、订单状态)、“支付服务”(对接支付宝 / 微信支付)、“搜索服务”(商品检索)等;
  • 优势
  • 双十一期间,仅需扩容 “订单服务” 和 “支付服务”,无需扩容整个系统;
  • 商品团队修改 “商品服务” 时,不会影响 “订单服务” 的稳定性;
  • 搜索服务可采用 Elasticsearch 技术栈,与其他服务的 Java 技术栈无关。

2. 你的项目为什么要采用微服务架构?有必要吗?

  • 如果你的项目确实很出众很高级,比如知识星球里的某个微服务项目,你就可以基于产品的设计初衷说。
  • 比如:我这个xxx产品最开始设计的时候就想支持高并发,而且微服务架构便于扩展,我以后想加其他业务的时候也便于扩展,巴拉巴拉......,照着第一个问题的框架展开。
  • 如果你的项目比较普通,或者只是个玩具项目,比如天机学堂等等,你就可以说基于学习微服务的目的才使用
  • 比如:如果只是看项目其实没必要上微服务,但是由于我之前开发过单体架构的项目,出于学习和拓展技术广度的目的想看看微服务架构是怎么设计的,和单体服务有什么区别......,这时候可能面试官就会问,那你说一下两者有什么区别吧,这样就又带入了第一个问题的框架。

这样的回答可以突出你学习的广度,和对技术的追求和热爱。面试官可能就对你高看一眼。

3. 你的微服务项目中,各个服务是如何通信的?

同步通信

同步通信是指调用方发送请求后,需等待服务方返回结果才能继续执行,适用于实时性要求高、需要立即获取结果的场景(如查询商品库存、用户登录验证)。

RESTful API

基于 HTTP/HTTPS 协议,通过 JSON/XML 格式传输数据,是微服务中最普及的通信方式。

  • 特点:简单、无状态、易调试,适合跨语言通信(任何语言都能解析 HTTP)。
  • 实现方式
  • 服务提供方暴露接口(如 GET /api/v1/products/{id} 查询商品);
  • 调用方通过 HTTP 客户端(如 Java 的 OkHttp、Python 的 Requests)发送请求并接收响应。
  • 示例:订单服务创建订单前,通过 REST API 调用商品服务查询库存:
# 订单服务 → 商品服务(查询商品ID=100的库存)
GET http://product-service:8080/api/v1/products/100/stock
Response: {"productId": 100, "stock": 50}

gRPC

基于 HTTP/2 协议的高性能 RPC(远程过程调用)框架,使用 Protocol Buffers(protobuf)序列化数据,适合服务间高频通信、对性能要求高的场景。

  • 特点:二进制传输(比 JSON 更高效)、支持双向流、强类型接口定义(通过.proto文件),性能是 REST 的 5-10 倍。
  • 实现方式
  • 定义.proto文件描述服务接口(如商品服务的库存查询方法);
  • 通过工具生成各语言的客户端 / 服务端代码;
  • 服务端实现接口,客户端直接调用生成的方法(如同调用本地函数)。
  • 示例(.proto定义):
service ProductService {
  rpc GetStock(StockRequest) returns (StockResponse);
}
message StockRequest {
  int32 product_id = 1;
}
message StockResponse {
  int32 product_id = 1;
  int32 stock = 2;
}

异步通信

异步通信是指调用方发送请求后无需等待结果,直接继续执行,适用于非实时性需求、解耦服务依赖、削峰填谷的场景(如订单创建后发送通知、日志异步写入)。核心是通过 “消息队列”(Message Queue)传递数据。

  • 核心流程
  • 发送方(生产者)将消息(如 “订单创建成功”)发送到消息队列;
  • 接收方(消费者)从队列中获取消息并处理;双方完全解耦:
  • 发送方无需知道接收方是否在线,接收方可按需消费(同步 / 异步处理)。
  • 常用消息队列
  • RabbitMQ:支持多种交换机类型(如 Direct、Topic),适合复杂路由场景;
  • Kafka:高吞吐量,适合大数据量、日志收集等场景;
  • RocketMQ:支持事务消息,适合金融级数据一致性场景。
  • 典型场景
  • 订单创建后通知用户:订单服务创建订单后,向消息队列发送 “订单创建” 消息,通知服务监听并消费该消息,发送短信 / 邮件(订单服务无需等待通知完成)。
  • 库存变更同步:商品服务扣减库存后,发送 “库存变更” 消息,搜索服务消费消息并更新商品索引,确保数据一致。

服务通信的关键支撑组件

微服务通信需解决 “服务发现” “负载均衡” “容错” 等问题,依赖以下组件:

  1. 服务注册与发现:解决 “调用方如何找到服务方地址” 的问题:
  2. 服务启动时,将自己的 IP: 端口注册到注册中心(如 Eureka、Nacos、Consul);
  3. 调用方从注册中心获取服务的可用实例列表。
  4. 负载均衡:解决 “请求如何分配到多个服务实例” 的问题:
  5. 客户端负载均衡(如 Ribbon):调用方本地计算路由(如轮询、随机);
  6. 服务端负载均衡(如 Nginx、K8s Service):统一入口分发请求。
  7. 容错与熔断:防止 “服务不可用时拖垮调用方”:
  8. 熔断(如 Sentinel、Resilience4j):当服务失败率过高时,暂时切断调用,返回默认结果;
  9. 降级:非核心服务故障时,用简化逻辑替代(如推荐服务故障时,返回热门商品列表);
  10. 超时重试:设置请求超时时间,失败后自动重试(避免网络抖动影响)。

4. 微服务架构为什么可以更好地应对高并发,实现高可用?

这个问题其实和第一个差不多,只是更侧重于说一下微服务架构的优势。

微服务架构能更好应对高并发、实现高可用,核心在于其 “拆分” 与 “去中心化” 的设计理念 —— 通过将单体应用拆解为独立运行的服务单元,从 “资源调度” “高灵活性” “故障隔离” 三个关键维度,解决了单体架构在高负载下的性能瓶颈与可用性风险。

资源调度

在单体架构中,应用是一个不可拆分的整体,即使只有某个功能(如电商的 “商品详情页”)面临高并发,也必须对整个单体应用集群扩容(增加服务器部署全量代码),导致非热点功能(如 “用户注册” “订单售后”)占用冗余资源,性价比极低。

微服务架构将应用拆分为独立服务(如 “商品服务” “订单服务” “支付服务”),每个服务可独立部署、独立扩展

  • 当 “商品服务” 因大促流量激增时,只需单独对该服务的实例数量扩容(如从 3 台服务器增加到 20 台);
  • 非热点服务(如 “售后服务”)保持原有实例规模,不浪费 CPU、内存、带宽等资源。

这种 “精准扩缩容” 能快速匹配高并发场景的资源需求,用最低成本扛住峰值流量。

高灵活性

高并发场景常需针对性技术优化(如缓存、异步、读写分离),单体架构因代码耦合,优化某功能可能影响全局;微服务则可针对单个服务的特性选择最优方案:

  • 对 “商品详情页”(读多写少):用 Redis 做缓存、MySQL 做读写分离,提升查询速度;
  • 对 “订单创建”(写操作密集):用 RabbitMQ 做异步队列,避免同步请求阻塞,提高并发处理能力;
  • 对 “大数据统计”(高计算负载):用 Spark/Flink 做离线计算,不占用核心业务服务的资源。

故障隔离

单体架构中,任何一个模块的故障(如 “支付模块” 数据库连接池耗尽)都会导致整个应用崩溃,所有功能(商品查询、下单、售后)全部不可用,即 “单点故障引发全局故障”。

微服务架构中,每个服务是独立的进程,运行在不同的服务器 / 容器中,服务间通过网络通信(如 HTTP、gRPC)交互,具备天然的故障隔离能力:

  • 若 “支付服务” 因数据库故障不可用,只需暂时关闭该服务的入口(通过网关拦截支付请求),用户仍可正常浏览商品、加入购物车、提交订单(订单服务暂存未支付订单);
  • 故障仅影响 “支付” 这一个功能,不会扩散到其他服务,大幅降低故障的影响范围。

除此之外,微服务架构还具备:“冗余部署,避免单点故障”,“熔断与降级”,“负载均衡” 等特性,我们不用全部都打上来,挑几个自己熟悉的流利地和面试官讲出来就很棒了!

#Java##后端##微服务##分布式#
后端技术学习 文章被收录于专栏

这个专栏我将对后端技术进行总结和复盘,以在字节输出文档的态度来要求自己。包括但不限于八股、算法、架构、系统设计、场景、智力题等。

全部评论

相关推荐

评论
1
6
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务