微服务
微服务(Microservices)是一种分布式架构风格,它将一个大型应用程序拆分为多个小型、独立的服务,每个服务专注于完成特定的业务功能,并通过定义良好的接口(如 API)进行通信。这种架构的核心目标是提高系统的灵活性、可维护性和可扩展性。
微服务的核心特点
-
小型、独立、松耦合
- 每个微服务是一个独立的进程或组件,拥有自己的代码库、数据库和配置。
- 服务之间通过轻量级通信机制(如 REST API、gRPC、消息队列)交互,彼此不直接依赖。
-
独立部署和运行
- 每个微服务可以独立开发、测试、部署和扩展,无需重新部署整个应用程序。
- 例如:支付服务升级时,不影响订单服务或用户服务。
-
技术栈灵活性
- 不同微服务可以使用不同的编程语言、框架或数据库(即“多语言编程”)。
- 例如:用户服务用 Java,支付服务用 Python,库存服务用 Go。
-
有界上下文和业务能力
- 每个微服务围绕一个明确的业务功能设计(如用户管理、订单处理、支付),并遵循领域驱动设计(DDD)的“有界上下文”原则。
-
去中心化
- 数据存储去中心化:每个服务管理自己的数据库,避免共享数据库的瓶颈。
- 治理去中心化:团队可以自主选择工具和技术,提升开发效率。
微服务的典型应用场景
- 复杂业务需求:当系统功能复杂且需要频繁迭代时(如电商、金融系统)。
- 多团队协作:不同团队可以并行开发和维护独立的微服务。
- 高可扩展性需求:某些功能(如秒杀活动)需要独立扩展,而其他模块无需扩容。
- 技术异构:需要结合不同技术栈(如遗留系统与新技术共存)。
微服务的优势
-
敏捷开发与快速交付
- 独立部署允许团队快速迭代功能,缩短发布周期。
- 例如:修复一个微服务的 bug 不需要重新部署整个应用。
-
高可扩展性
- 按需扩展特定服务(如双十一期间扩容支付服务),节省资源成本。
-
故障隔离
- 单个服务故障不会导致整个系统崩溃(通过断路器、熔断机制等实现)。
-
技术灵活性
- 团队可根据业务需求选择最适合的技术栈,避免“一刀切”的技术约束。
-
持续集成/持续交付(CI/CD)支持
- 微服务天然适合自动化部署流程,便于实现 DevOps 实践。
微服务的挑战
-
分布式系统复杂性
- 需要处理服务发现、负载均衡、分布式事务、日志聚合等问题。
- 例如:跨服务调用时如何保证一致性(可采用 Saga 模式或事件溯源)。
-
运维成本增加
- 服务数量多,部署、监控和调试难度上升(需借助 Kubernetes、Service Mesh 等工具)。
-
网络延迟与可靠性
- 服务间通信依赖网络,可能引入延迟和故障(需通过重试、超时机制优化)。
-
接口管理
- 不同版本的服务接口需要兼容性设计(如 OpenAPI 规范)。
微服务 vs 单体架构
| 特性 | 单体架构 | 微服务架构 |
|---|---|---|
| 部署方式 | 整体部署(一次发布整个应用) | 独立部署(每个服务单独发布) |
| 技术栈 | 通常使用单一技术栈 | 支持多语言、多框架 |
| 可扩展性 | 只能整体扩展 | 按需扩展特定服务 |
| 故障影响范围 | 单点故障可能导致整个系统崩溃 | 故障隔离,影响局部服务 |
| 开发团队协作 | 单一团队维护整个代码库 | 多个团队并行开发独立服务 |
| 适用场景 | 小型项目或简单业务 | 复杂业务、高并发场景 |
微服务的实现技术
- 框架与工具:
- Spring Cloud(Java 生态,提供服务注册、配置管理、网关等功能)
- Dubbo(高性能 RPC 框架)
- Service Mesh(如 Istio、Linkerd,用于服务治理)
- 基础设施:
- 容器化(Docker、Kubernetes)
- API 网关(如 Kong、Zuul)
- 服务注册与发现(Consul、Eureka)
总结
微服务架构是云原生时代的核心实践,适合需要高灵活性、可扩展性和快速迭代的复杂系统。然而,它并非万能方案,需权衡业务需求与团队能力。对于小型项目或初期阶段,单体架构可能更简单高效;而当业务规模扩大、团队分工细化时,微服务则能显著提升系统的可靠性和开发效率。
认识微服务
单体架构(monolithic structure):顾名思义,整个项目中所有功能模块都在一个工程中开发;项目部署时需要对所有模块一起编译、打包;项目的架构设计、开发模式都非常简单。
当项目规模较小时,这种模式上手快,部署、运维也都很方便,因此早期很多小型项目都采用这种模式。
但随着项目的业务规模越来越大,团队开发人员也不断增加,单体架构就呈现出越来越多的问题:
- 团队协作成本高:试想一下,你们团队数十个人同时协作开发同一个项目,由于所有模块都在一个项目中,不同模块的代码之间物理边界越来越模糊。最终要把功能合并到一个分支,你绝对会陷入到解决冲突的泥潭之中。
- 系统发布效率低:任何模块变更都需要发布整个系统,而系统发布过程中需要多个模块之间制约较多,需要对比各种文件,任何一处出现问题都会导致发布失败,往往一次发布需要数十分钟甚至数小时。
- 系统可用性差:单体架构各个功能模块是作为一个服务部署,相互之间会互相影响,一些热点功能会耗尽系统资源,导致其它服务低可用。
在上述问题中,前两点相信大家在实战过程中应该深有体会。对于第三点系统可用性问题,很多同学可能感触不深。接下来我们就通过黑马商城这个项目,给大家做一个简单演示。
首先,我们修改hm-service模块下的com.hmall.controller.HelloController中的hello方法,模拟方法执行时的耗时:

接下来,启动项目,目前有两个接口是无需登录即可访问的:
- http://localhost:8080/hi
- http://localhost:8080/search/list
经过测试,目前/search/list 是比较正常的,访问耗时在30毫秒左右。
接下来,我们假设/hi这个接口是一个并发较高的热点接口,我们通过Jemeter来模拟500个用户不停访问。在课前资料中已经提供了Jemeter的测试脚本:

这个脚本会开启500个线程并发请求http://localhost/hi这个接口。由于该接口存在执行耗时(500毫秒),这就服务端导致每秒能处理的请求数量有限,最终会有越来越多请求积压,直至Tomcat资源耗尽。这样,其它本来正常的接口(例如/search/list)也都会被拖慢,甚至因超时而无法访问了。
我们测试一下,启动测试脚本,然后在浏览器访问http://localhost:8080/search/list这个接口,会发现响应速度非常慢:

如果进一步提高/hi这个接口的并发,最终会发现/search/list接口的请求响应速度会越来越慢。
可见,单体架构的可用性是比较差的,功能之间相互影响比较大。
当然,有同学会说我们可以做水平扩展。
此时如果我们对系统做水平扩展,增加更多机器,资源还是会被这样的热点接口占用,从而影响到其它接口,并不能从根本上解决问题。这也就是单体架构的扩展性差的一个原因。
微服务
微服务架构,首先是服务化,就是将单体架构中的功能模块从单体应用中拆分出来,独立部署为多个服务。同时要满足下面的一些特点:
- 单一职责:一个微服务负责一部分业务功能,并且其核心数据不依赖于其它模块。
- 团队自治:每个微服务都有自己独立的开发、测试、发布、运维人员,团队人员规模不超过10人(2张披萨能喂饱)
- 服务自治:每个微服务都独立打包部署,访问自己独立的数据库。并且要做好服务隔离,避免对其它服务产生影响
例如,黑马商城项目,我们就可以把商品、用户、购物车、交易等模块拆分,交给不同的团队去开发,并独立部署:

微服务架构解决了单体架构存在的问题,特别适合大型互联网项目的开发,因此被各大互联网公司普遍采用。大家以前可能听说过分布式架构,分布式就是服务拆分的过程,其实微服务架构正式分布式架构的一种最佳实践的方案。
当然,微服务架构虽然能解决单体架构的各种问题,但在拆分的过程中,还会面临很多其它问题。比如:
- 如果出现跨服务的业务该如何处理?
- 页面请求到底该访问哪个服务?
- 如何实现各个服务之间的服务隔离?
目前SpringCloud最新版本为2022.0.x版本,对应的SpringBoot版本为3.x版本,但它们全部依赖于JDK17,目前在企业中使用相对较少。
推荐使用次新版本:Spring Cloud 2021.0.x以及Spring Boot 2.7.x版本。
另外,Alibaba的微服务产品SpringCloudAlibaba目前也成为了SpringCloud组件中的一员,我们课堂中也会使用其中的部分组件。
在我们的父工程hmall中已经配置了SpringCloud以及SpringCloudAlibaba的依赖:

服务拆分
熟悉项目

记得修改数据库连接信息

配置启动项激活的是local环境:

登录
登录过程的时序图

登录接口在com.hmall.controller.UserController中的login方法。
搜索商品
在首页搜索框输入关键字,点击搜索即可进入搜索列表页面:

该页面会调用接口:/search/list,对应的服务端入口在com.hmall.controller.SearchController中的search方法.
购物车
在搜索到的商品列表中,点击按钮加入购物车,即可将商品加入购物车:

加入购物车后可查询购物车列表信息

同时这里还可以对购物车实现修改、删除等操作。
相关功能全部在com.hmall.controller.CartController中。
其中,查询购物车列表时,由于要判断商品最新的价格和状态,所以还需要查询商品信息,业务流程如下:

下单
在购物车页面点击结算按钮,会进入订单结算页面:

点击提交订单,会提交请求到服务端,服务端做3件事情:
- 创建一个新的订单
- 扣减商品库存
- 清理购物车中商品
业务入口在com.hmall.controller.OrderController中的createOrder方法。
支付
下单完成后会跳转到支付页面,目前只支持余额支付:

在选择余额支付这种方式后,会发起请求到服务端,服务端会立刻创建一个支付流水单,并返回支付流水单号到前端。
当用户输入用户密码,然后点击确认支付时,页面会发送请求到服务端,而服务端会做几件事情:
- 校验用户密码
- 扣减余额
- 修改支付流水状态
- 修改交易订单状态
请求入口在com.hmall.controller.PayController中。
服务拆分原则
服务拆分一定要考虑几个问题:
- 什么时候拆?
- 如何拆?
什么时候拆
一般情况下,对于一个初创的项目,首先要做的是验证项目的可行性。因此这一阶段的首要任务是敏捷开发,快速产出生产可用的产品,投入市场做验证。为了达成这一目的,该阶段项目架构往往会比较简单,很多情况下会直接采用单体架构,这样开发成本比较低,可以快速产出结果,一旦发现项目不符合市场,损失较小。
如果这一阶段采用复杂的微服务架构,投入大量的人力和时间成本用于架构设计,最终发现产品不符合市场需求,等于全部做了无用功。
所以,对于大多数小型项目来说,一般是先采用单体架构,随着用户规模扩大、业务复杂后再逐渐拆分为微服务架构。这样初期成本会比较低,可以快速试错。但是,这么做的问题就在于后期做服务拆分时,可能会遇到很多代码耦合带来的问题,拆分比较困难(前易后难)。
而对于一些大型项目,在立项之初目的就很明确,为了长远考虑,在架构设计时就直接选择微服务架构。虽然前期投入较多,但后期就少了拆分服务的烦恼(前难后易)。
怎么拆
之前我们说过,微服务拆分时粒度要小,这其实是拆分的目标。具体可以从两个角度来分析:
- 高内聚:每个微服务的职责要尽量单一,包含的业务相互关联度高、完整度高。
- 低耦合:每个微服务的功能要相对独立,尽量减少对其它微服务的依赖,或者依赖接口的稳定性要强。
高内聚首先是单一职责,但不能说一个微服务就一个接口,而是要保证微服务内部业务的完整性为前提。目标是当我们要修改某个业务时,最好就只修改当前微服务,这样变更的成本更低。
一旦微服务做到了高内聚,那么服务之间的耦合度自然就降低了。
当然,微服务之间不可避免的会有或多或少的业务交互,比如下单时需要查询商品数据。这个时候我们不能在订单服务直接查询商品数据库,否则就导致了数据耦合。而应该由商品服务对应暴露接口,并且一定要保证微服务对外接口的稳定性(即:尽量保证接口外观不变)。虽然出现了服务间调用,但此时无论你如何在商品服务做内部修改,都不会影响到订单微服务,服务间的耦合度就降低了。
明确了拆分目标,接下来就是拆分方式了。我们在做服务拆分时一般有两种方式:
- 纵向拆分
- 横向拆分
所谓纵向拆分,就是按照项目的功能模块来拆分。例如黑马商城中,就有用户管理功能、订单管理功能、购物车功能、商品管理功能、支付功能等。那么按照功能模块将他们拆分为一个个服务,就属于纵向拆分。这种拆分模式可以尽可能提高服务的内聚性。
而横向拆分,是看各个功能模块之间有没有公共的业务部分,如果有将其抽取出来作为通用服务。例如用户登录是需要发送消息通知,记录风控数据,下单时也要发送短信,记录风控数据。因此消息发送、风控数据记录就是通用的业务功能,因此可以将他们分别抽取为公共服务:消息中心服务、风控管理服务。这样可以提高业务的复用性,避免重复开发。同时通用业务一般接口稳定性较强,也不会使服务之间过分耦合。
当然,由于黑马商城并不是一个完整的项目,其中的短信发送、风控管理并没有实现,这里就不再考虑了。而其它的业务按照纵向拆分,可以分为以下几个微服务:
- 用户服务
- 商品服务
- 订单服务
- 购物车服务
- 支付服务
拆分购物车、商品服务
接下来,我们先把商品管理功能、购物车功能抽取为两个独立服务。
一般微服务项目有两种不同的工程结构:
- 完全解耦:每一个微服务都创建为一个独立的工程,甚至可以使用不同的开发语言来开发,项目完全解耦。
- 优点:服务之间耦合度低
- 缺点:每个项目都有自己的独立仓库,管理起来比较麻烦
- Maven聚合:整个项目为一个Project,然后每个微服务是其中的一个Module
- 优点:项目代码集中,管理和运维方便(授课也方便)
- 缺点:服务之间耦合,编译时间较长
在hmall父工程之中,我已经提前定义了SpringBoot、SpringCloud的依赖版本,所以为了方便期间,我们直接在这个项目中创建微服务module.
商品服务
创建module,选择JDK11版本,商品模块,我们起名为item-service,引入基本依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>hmall</artifactId>
<groupId>com.heima</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>item-service</artifactId>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<!--common-->
<dependency>
<groupId>com.heima</groupId>
<artifactId>hm-common</artifactId>
<version>1.0.0</version>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframew


263

被折叠的 条评论
为什么被折叠?



