当前位置: 首页 > web >正文

基于黑马教程——微服务架构解析(一)

本篇文章基于黑马程序员的微服务课程内容,结合个人学习过程中的理解与思考进行整理。本节将围绕以下几个问题展开:什么是微服务?如何将一个单体项目拆分为微服务架构?以及微服务之间是如何进行协同与关联的?

1.认识微服务

首先我们对单体架构的优缺点进行分析,看看开发大型项目采用单体架构存在哪些问题,而微服务架构又是如何解决这些问题的。

1.1 单体项目

我们将从单体项目的架构入手,探讨其在实际开发过程中的局限性,并进一步说明为何需要引入微服务架构来应对这些问题。

在这里插入图片描述

但随着项目的业务规模越来越大,团队开发人员也不断增加,单体架构就呈现出越来越多的问题:

  • 团队协作成本高:试想一下,你们团队数十个人同时协作开发同一个项目,由于所有模块都在一个项目中,不同模块的代码之间物理边界越来越模糊。最终要把功能合并到一个分支,你绝对会陷入到解决冲突的泥潭之中。
  • 系统发布效率低:任何模块变更都需要发布整个系统,而系统发布过程中需要多个模块之间制约较多,需要对比各种文件,任何一处出现问题都会导致发布失败,往往一次发布需要数十分钟甚至数小时。
  • 系统可用性差:单体架构各个功能模块是作为一个服务部署,相互之间会互相影响,一些热点功能会耗尽系统资源,导致其它服务低可用。

1.2 微服务

微服务架构,首先是服务化,就是将单体架构中的功能模块从单体应用中拆分出来,独立部署为多个服务。同时要满足下面的一些特点:

  • 单一职责:一个微服务负责一部分业务功能,并且其核心数据不依赖于其它模块。
  • 团队自治:每个微服务都有自己独立的开发、测试、发布、运维人员,团队人员规模不超过10人(2张披萨能喂饱)
  • 服务自治:每个微服务都独立打包部署,访问自己独立的数据库。并且要做好服务隔离,避免对其它服务产生影响

例如,黑马商城项目,我们就可以把商品、用户、购物车、交易等模块拆分,交给不同的团队去开发,并独立部署:

在这里插入图片描述

综上所述,微服务架构解决了单体架构存在的问题,特别适合大型互联网项目的开发,因此被各大互联网公司普遍采用。大家以前可能听说过分布式架构,分布式就是服务拆分的过程,其实微服务架构正式分布式架构的一种最佳实践的方案

当然,微服务架构虽然能解决单体架构的各种问题,但在拆分的过程中,还会面临很多其它问题。比如:

  • 如果出现跨服务的业务该如何处理?
  • 页面请求到底该访问哪个服务?
  • 如何实现各个服务之间的服务隔离?

1.3 SpringCloud

将服务进行拆分以后将会碰到很多问题,当然这些问题都有相应的解决方法和组件。SPringCloud就是集成目前Java领域最全面微服务组件的框架。

在这里插入图片描述

2.服务的拆分

这里分服务拆分是以黑马商城为例:

在这里插入图片描述

2.1 服务拆分的原则

在拆分为微服务架构时,通常遵循以下原则:

  1. 按业务功能划分服务(面向领域):每个服务聚焦于一个明确的业务边界;
  2. 高内聚、低耦合:服务内部功能紧密相关,服务之间通过接口通信;
  3. 独立部署、独立数据库:每个服务可独立升级,数据相互隔离;
  4. 服务自治:服务内部业务逻辑不依赖其他服务的内部实

2.2 拆分一个商品服务和购物车服务

2.2.1 商品服务

主要流程为:

  • 创建子模块(最好是Maven空模块),命名为item-service
  • 引入该服务所需要的依赖
  • 编写一个基本SpringBoot文件架构(SSM架构、一些工具类和配置类等)和编写启动类
  • 构建配置文件,数据库、swagger配置等
  • 然后一些细节的全限定类名之类细节需要根据新项目文件进行修改
2.2.2 购物车

也是同理,如果需要看详细教程看黑马的笔记,这篇文章主要是回顾和复习

3.服务的调用

在之前拆分的时候,我们发现购物车的服务需要去调用商品信息的服务。那我怎么去实现服务之间的调用。这里可以使用RestTemplate。

3.1 RestTemplate

RestTemplate 是 Spring 框架提供的一个用于 发起 HTTP 请求、访问 RESTful 服务 的客户端工具。它封装了底层的 HttpClient,让我们可以以更简洁的方式实现服务间的通信,尤其适用于微服务架构中的 服务调用

在传统的前后端分离项目中,前端通常通过 HTTP 请求与后端进行数据交互;同样,后端之间也可以通过 HTTP 接口实现服务间的通信。

3.2 案例

我们将以 cart-service 中的 com.hmall.cart.service.impl.CartServiceImplhandleCartItems 方法为例,向 item-service 发送 HTTP 请求获取商品详情。

可以看到,使用 RestTemplate 发起服务请求的方式与前端 AJAX 请求类似,都包含以下四个核心要素:

  1. 请求方式(GET / POST / PUT / DELETE)
  2. 请求路径(URL 或服务名 + 路径)
  3. 请求参数(URL 参数 / 请求体)
  4. 返回值类型(需要反序列化成的 Java 类型)
@Service
public class CartServiceImpl implements CartService {@Autowiredprivate RestTemplate restTemplate;@Overridepublic void handleCartItems(Long itemId) {// ① 请求方式:GET// ② 请求路径:调用 item-service 获取商品信息String url = "http://item-service/items/" + itemId;// ③ 请求参数:itemId 作为路径参数传递// ④ 返回值类型:Item 类对象(假设定义了 Item 实体类)Item item = restTemplate.getForObject(url, Item.class);// 使用返回的数据处理购物车逻辑if (item != null) {// ... 处理 item 信息System.out.println("获取商品信息成功:" + item.getName());}}
}

4.服务注册和发现

在前面的开发过程中,我们发现在服务之间进行调用时,每次都需要手动指定服务地址和端口号。比如:

String url = "http://localhost:8081/items/1";

这种方式存在明显的缺点:

  • 不利于维护:每次服务地址变更,都需要修改代码;
  • 无法实现负载均衡:无法同时调用多个实例;
  • 不具备服务自动发现能力:服务上线或下线,系统无法感知。

为了解决这些问题,引入了 服务注册与发现机制。这就是我们为什么要使用 Nacos

5. OpenFeign —— 更优雅的服务远程调用方式

在上一章节中,我们通过 Nacos 实现了服务的注册与发现,使用 RestTemplate 实现了服务间的远程调用。但我们也发现,RestTemplate 的调用方式存在以下问题:

  • 代码冗长,需要手动拼接 URL、设置参数、解析结果;
  • 调用方式与本地方法差异较大,影响编码体验;
  • 缺乏可复用性,重复代码多;
  • 不支持声明式、统一式的调用风格。

为了解决这些问题,我们引入了 OpenFeign —— 一种基于接口的声明式 HTTP 客户端。


5.1 核心原理

远程调用的本质无非是四个核心要素:

  1. 请求方式(GET / POST)
  2. 请求路径(URL)
  3. 请求参数(如路径参数、查询参数等)
  4. 返回值类型(响应数据结构)

OpenFeign 正是基于这些要素,通过 SpringMVC 注解(如 @GetMapping@RequestParam 等)来声明调用规则,再结合 动态代理 自动生成 HTTP 请求代码,帮助我们完成远程调用。

5.2 快速入门示例:在 cart-service 中使用 Feign 调用 item-service

5.2.1 引入依赖

cart-servicepom.xml 中添加如下依赖:

<!-- OpenFeign -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency><!-- 负载均衡器(基于服务名调用) -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
5.2.2 启用 OpenFeign 功能

cart-service 的启动类 CartApplication 上添加注解:

@EnableFeignClients
@SpringBootApplication
public class CartApplication {public static void main(String[] args) {SpringApplication.run(CartApplication.class, args);}
}
5.2.3 定义 Feign 客户端接口

cart-service 中创建接口 ItemClient,用于远程调用 item-service

@FeignClient("item-service")  // 指定服务名
public interface ItemClient {@GetMapping("/items")
List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
}
  • @FeignClient("item-service"):指定目标服务的服务名;
  • @GetMapping("/items"):请求方式和路径;
  • @RequestParam("ids"):请求参数;
  • List<ItemDTO>:返回值类型。

无需写实现类,Feign 会基于接口定义自动生成远程调用逻辑。

5.2.4 在业务中使用 Feign 调用

CartServiceImpl 中注入并使用 ItemClient

@Service
public class CartServiceImpl implements CartService {@Autowiredprivate ItemClient itemClient;@Overridepublic void handleCartItems(List<Long> itemIds) {List<ItemDTO> items = itemClient.queryItemByIds(itemIds);// 后续处理...}
}

是不是比 RestTemplate 优雅很多?这就是 Feign 的魅力!

5.3 使用连接池优化 Feign 性能

Feign 默认使用 HttpURLConnection 发送请求,它不支持连接池。为了提升性能,我们推荐使用支持连接池的客户端,如 OkHttpApache HttpClient

5.3.1 引入 OkHttp 依赖
<dependency><groupId>io.github.openfeign</groupId><artifactId>feign-okhttp</artifactId>
</dependency>
5.3.2 启用 OkHttp

application.yml 中添加配置:

feign:okhttp:enabled: true  # 开启 OkHttp 支持
5.3.3 抽取公共 Feign 客户端模块(避免重复编码)

假设 trade-service 也需要调用 item-service 的接口。如果每个服务都自己定义 ItemClient,将导致大量重复代码。

💡 解决方案:抽取通用模块 hm-api

① 创建一个新模块:hm-api

结构如下:

hm-api/
├── dto/ItemDTO.java
└── client/ItemClient.java

pom.xml

<artifactId>hm-api</artifactId>
<dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency>
</dependencies>
② 其他服务引入依赖

cart-servicetrade-servicepom.xml 中引入:

xml复制编辑<dependency><groupId>com.heima</groupId><artifactId>hm-api</artifactId><version>1.0.0</version>
</dependency>
5.3.4 扫描配置(解决找不到 FeignClient 问题)

由于 ItemClient 移到了 hm-api,如果不在当前包路径下,需要通过以下方式之一显式声明扫描路径:

方式一:配置扫描包

@EnableFeignClients(basePackages = "com.hmall.api.client")

方式二:指定具体 FeignClient 类

@EnableFeignClients(clients = ItemClient.class)

5.4 Feign 日志配置

Feign 提供了四种日志级别:

  • NONE(默认):不输出任何日志
  • BASIC:记录请求方法、URL、响应状态、耗时
  • HEADERS:记录请求和响应头信息
  • FULL:记录所有内容,包括请求体和响应体
5.4.1 配置日志等级

hm-api 中创建配置类:

@Configuration
public class DefaultFeignConfig {@Beanpublic Logger.Level feignLogLevel() {return Logger.Level.FULL;}
}
5.4.2 应用配置

方式一:局部生效

@FeignClient(name = "item-service", configuration = DefaultFeignConfig.class)

方式二:全局生效

@EnableFeignClients(defaultConfiguration = DefaultFeignConfig.class)
http://www.xdnf.cn/news/16571.html

相关文章:

  • 设计模式(十二)结构型:享元模式详解
  • Python day26
  • 无向图的连通性问题
  • 设计模式(十三)结构型:代理模式详解
  • spring gateway 配置http和websocket路由转发规则
  • NodeJs接入腾讯云存储COS
  • Ubuntu Linux 如何配置虚拟内存 —— 一步一步配置 Ubuntu Server 的 NodeJS 服务器详细实录8
  • USB设备调试
  • 全面理解JVM虚拟机
  • RK3568 Linux驱动学习——U-Boot使用
  • 六、搭建springCloudAlibaba2021.1版本分布式微服务-admin监控中心
  • Linux 基础命令大全
  • 内存泄漏问题排查
  • Context Engineering Notes
  • 【Golang】Go语言运算符
  • 迷宫生成与路径搜索(A算法可视化)
  • Triton IR
  • Libevent(4)之使用教程(3)配置
  • 如何使用ozone调试elf文件?
  • Dify 本地化部署深度解析与实战指南
  • LangChain实现RAG
  • 力扣 hot100 Day57
  • 第四科学范式(数据密集型科学):科学发现的新范式
  • Qt C++动态库SDK在Visual Studio 2022使用(C++/C#版本)
  • IIS发布.NET9 API 常见报错汇总
  • Java面试实战:从基础到架构的全方位技术交锋
  • add新增管理员功能、BaseController类的简介--------示例OJ
  • PDF转图片实用指南:如何批量高效转换?
  • AI入门学习-模型评估示例讲解
  • Deja Vu: 利用上下文稀疏性提升大语言模型推理效率