面试题答案
一键面试1. Node.js项目结构设计原则
- 单一职责原则:每个模块或微服务应专注于完成一项特定功能,避免功能混杂。例如,用户认证微服务仅负责处理用户认证相关逻辑,不涉及订单处理等其他功能。
- 高内聚低耦合:模块内部的代码紧密相关,相互协作完成特定功能(高内聚);微服务之间通过清晰、简洁的接口通信,尽量减少相互依赖(低耦合)。如商品微服务内部对商品的增删改查逻辑紧密结合,而与库存微服务通过RESTful API通信,仅在涉及库存变动时交互。
- 分层架构:通常可分为表现层(处理HTTP请求等)、业务逻辑层(核心业务处理)和数据访问层(与数据库交互)。以订单微服务为例,表现层接收订单创建请求,业务逻辑层处理订单计算、状态管理等,数据访问层将订单数据存储到数据库。
2. 微服务内部结构设计
- 采用模块化开发:根据功能划分模块,如在用户微服务中,可分为用户注册模块、用户登录模块、用户信息修改模块等。每个模块有清晰的接口和功能边界,方便维护和复用。
- 配置管理:将配置信息(如数据库连接字符串、第三方服务API密钥等)集中管理,可使用
.env
文件在开发和部署环境灵活切换配置。例如,开发环境使用本地数据库,生产环境使用云数据库,通过修改.env
文件的数据库配置即可。 - 日志和错误处理:建立统一的日志记录机制,记录重要的业务操作和错误信息。在微服务内部,捕获异常并进行合理处理,避免异常泄露导致服务崩溃。如使用
winston
库记录日志,在捕获到数据库连接错误时,记录详细错误信息并返回友好的错误提示给客户端。
3. 微服务之间协作机制
- RESTful API:使用HTTP协议,通过定义清晰的接口来实现微服务间通信。例如,订单微服务调用商品微服务获取商品价格信息,通过发送GET请求到商品微服务的
/products/{productId}/price
接口。 - 消息队列:用于异步通信和解耦,适合处理高并发、异步任务场景。如订单创建成功后,订单微服务发送消息到消息队列,库存微服务监听队列并处理库存扣减操作。常见的消息队列有RabbitMQ、Kafka等。
- 服务发现:在大型分布式系统中,使用服务发现工具(如Consul、Eureka等)让微服务能够动态注册和发现彼此。例如,新上线一个商品微服务实例,它向Consul注册自身信息,其他微服务(如订单微服务)可从Consul获取商品微服务的地址进行通信。
4. 服务拆分与合并
- 服务拆分:
- 基于业务功能:如果一个微服务承担过多功能,变得臃肿,可按业务功能拆分。例如,一个包含用户管理、订单管理和商品管理的单体应用,可拆分为用户微服务、订单微服务和商品微服务。拆分时,遵循单一职责原则,确保新微服务功能清晰。
- 数据驱动:若不同业务数据操作模式差异大,可按数据拆分。如电商应用中,用户数据与订单数据访问频率、存储方式不同,可将原微服务拆分为用户数据微服务和订单数据微服务。拆分过程中,梳理原微服务的数据交互逻辑,确保拆分后通过合适接口通信。
- 服务合并:
- 功能相似:当多个微服务功能相似且交互频繁时,可考虑合并。例如,有两个微服务分别处理商品的基本信息和扩展信息,由于频繁相互调用获取完整商品信息,可合并为一个商品微服务,简化通信逻辑。
- 优化资源:若某些微服务资源占用小且管理成本高,合并可提高资源利用率。合并时,整合原微服务的配置、日志等管理机制,避免重复。
5. 实际大型分布式应用场景示例
以电商平台为例,有商品展示、用户管理、订单处理、库存管理、支付等多个业务模块。
- 微服务设计:
- 商品微服务:负责商品信息的维护、查询等,内部按商品分类管理、商品详情等功能模块化设计。
- 用户微服务:处理用户注册、登录、信息管理等,通过分层架构实现业务逻辑与数据访问分离。
- 订单微服务:处理订单创建、支付、状态跟踪等,调用商品微服务获取商品价格,调用库存微服务检查库存并扣减。
- 库存微服务:管理商品库存,接收订单微服务的库存操作请求。
- 支付微服务:与第三方支付平台交互,处理支付流程,完成后通知订单微服务更新订单状态。
- 协作机制:
- 用户浏览商品时,前端调用商品微服务获取商品信息。
- 用户下单时,订单微服务调用商品微服务获取商品详情和价格,调用库存微服务检查库存,生成订单后发送消息到消息队列通知库存微服务扣减库存。同时,订单微服务调用支付微服务进行支付操作。
- 支付完成后,支付微服务通知订单微服务,订单微服务更新订单状态。
- 服务拆分与合并:
- 拆分:随着业务发展,商品微服务中商品搜索功能因算法复杂、性能要求高,可拆分为独立的商品搜索微服务,通过RESTful API与原商品微服务通信。
- 合并:如果订单微服务中订单创建和订单支付部分耦合度高,且经常一起调整,可合并为订单交易微服务,简化内部逻辑和外部接口。