一、环境准备
1.1 docker mysql 环境:
先创建docker中数据库容器:
# 创建网络
docker network create sc-net
docker run -d \
--name mysql \
-p 3306:3306 \
-e TZ=Asia/Shanghai \
-e MYSQL_ROOT_PASSWORD=1234 \
-v ./mysql/data:/var/lib/mysql \
-v ./mysql/conf:/etc/mysql/conf.d \
-v ./mysql/init:/docker-entrypoint-initdb.d \
--network sc-net \
mysql
挂起之后启动有问题,无法连接容器,解决方案:
# 关闭防火墙
stytemctl stop firewalld
# 禁止开机启动防火墙
stytemctl disable firewalld
# 修改ipv4转发状态
vi /usr/lib/sysctl.d/00-system.conf
# 添加信息
net.ipv4.ip_forward = 1
# 保存退出后,重启网络服务
stytemctl restrat network
# 将docker 的网络接口设置为不被NetworkManager
vi /etc/NetworkManager/conf.d/10-unmanage-docker-interfaces.conf
# 文件中添加如下内容
[keyfile]
# 重启网络
stytemctl restart NetworkManager
# 重启docker
stytemctl restart docker
# 启动相应的容器
docker restart mysql
1.2 启动后端项目
1.按下ALT + 8键打开services窗口,新增一个启动项:
找到Spring Boot:

2.在运行之前,做个简单配置:在Application上点击鼠标右键,会弹出窗口,然后选择Edit Configuration:

在弹出窗口中配置SpringBoot的启动环境为 local:

1.3 启动前端项目
# 启动nginx
start nginx.exe
# 停止
nginx.exe -s stop
# 重新加载配置
nginx.exe -s reload
# 重启
nginx.exe -s restart
注意:nginx.exe 不要双击启动,而是打开cmd窗口,通过命令行启动。停止的时候也一样要是用命令停止。如果启动失败不要重复启动,而是查看logs目录中的error.log日志,查看是否是端口冲突。如果是端口冲突则自行修改端口解决。
二、测试单体架构
重新启动项目,目前有两个接口是无需登录即可访问的:
http://localhost:8080/hihttp://localhost:8080/search/list
http://localhost:8080/search/list接口:

http://localhost:8080/hi接口:

接下来,我们假设/hi这个接口是一个并发较高的热点接口,我们通过Jmeter来模拟5000个用户不停访问。
使用JMeter打开测试文件;

@RestController
@RequestMapping("hi")
public class HelloController {
private final Map<String, AtomicInteger> countMap = new HashMap<>();
}
这个脚本会开启500个线程并发请求http://localhost/hi这个接口。由于该接口存在执行耗时,这就导致服务端每秒能处理的请求数量有限,最终会有越来越多请求积压,直至Tomcat资源耗尽。这样,其它本来正常的接口(例如/search/list)也都会被拖慢,甚至因超时而无法访问了。

我们测试一下,启动测试脚本,然后在浏览器访问http://localhost:8080/search/list这个接口,会发现响应速度非常慢:
如果进一步提高/hi这个接口的并发,最终会发现/search/list接口的请求响应速度会越来越慢。
可见,单体架构的可用性是比较差的,功能之间相互影响比较大。
单体架构的问题:
- 团队协作成本高:由于所有模块都在一个项目工程中,不同模块的代码之间物理边界越来越模糊。最终要把功能合并到一个分支,你绝对会陷入到解决冲突的泥潭之中。
- 系统发布效率低:任何模块变更都需要发布整个系统,而系统发布过程中需要多个模块之间制约较多,需要对比各种文件,任何一处出现问题都会导致发布失败,往往一次发布需要数十分钟甚至数小时。
- 系统可用性差:单体架构各个功能模块是作为一个服务部署,相互之间会互相影响,一些热点功能会耗尽系统资源,导致其它服务低可用。
三、业务分析
1.登录
登录成功后返回jwt令牌。
2.搜索商品
在首页搜索框输入关键字,点击搜索即可进入搜索列表页面:
3.购物车
在搜索到的商品列表中,点击按钮加入购物车,即可将商品加入购物车:
加入成功后即可进入购物车列表页,查看自己购物车商品列表:
同时这里还可以对购物车实现修改、删除等操作。
其中,查询购物车列表时,由于要判断商品最新的价格和状态,所以还需要查询商品信息,业务流程如下:

4.下单:
在购物车页面点击结算按钮,会进入订单结算页面:
点击提交订单,会提交请求到服务端,服务端做3件事情:
- 创建一个新的订单。
- 扣减商品库存。
- 清理购物车中商品。
5.支付
四、服务拆分原则
微服务拆分时粒度要小,这其实是拆分的目标。具体可以从两个角度来分析:
- 高内聚:每个微服务的职责要尽量单一,包含的业务相互关联度高、完整度高。
- 低耦合:每个微服务的功能要相对独立,尽量减少对其它微服务的依赖,或者依赖接口的稳定性要强。
高内聚首先是单一职责,但不能说一个微服务就一个接口,而是要保证微服务内部业务的完整性为前提。目标是当我们要修改某个业务时,最好就只修改当前微服务,这样变更的成本更低。
一旦微服务做到了高内聚,那么服务之间的耦合度自然就降低了。
当然,微服务之间不可避免的会有或多或少的业务交互,比如下单时需要查询商品数据。这个时候我们不能在订单服务直接查询商品数据库,否则就导致了数据耦合。而应该由商品服务对应暴露接口,并且一定要保证微服务对外接口的稳定性(即:尽量保证接口外观不变)。虽然出现了服务间调用,但此时无论你如何在商品服务做内部修改,都不会影响到订单微服务,服务间的耦合度就降低了。
明确了拆分目标,接下来就是拆分方式了。我们在做服务拆分时一般有两种方式:
- 纵向拆分。
- 横向拆分。
所谓纵向拆分,就是按照项目的业务功能模块来拆分。例如黑马商城中,就有用户管理功能、订单管理功能、购物车功能、商品管理功能、支付功能等。那么按照功能模块将他们拆分为一个个服务,就属于纵向拆分。这种拆分模式可以尽可能提高服务的内聚性。
横向拆分是看各个功能模块之间有没有公共的业务部分,如果有将其抽取出来作为通用服务。例如用户登录是需要发送消息通知,记录风控数据,下单时也要发送短信,记录风控数据。因此消息发送、风控数据记录就是通用的业务功能,因此可以将他们分别抽取为公共服务:消息中心服务、风控管理服务。这样可以提高业务的复用性,避免重复开发。同时通用业务一般接口稳定性较强,也不会使服务之间过分耦合。
此项目业务按照纵向拆分,可以分为以下几个微服务:
- 商品服务。
- 购物车服务。
- 用户服务。
- 订单服务。
- 支付服务。
一般微服务项目有两种不同的工程结构:
- 完全解耦:每一个微服务都创建为一个独立的工程,甚至可以使用不同的开发语言来开发,项目完全解耦。
- 优点:服务之间耦合度低。
- 缺点:每个项目都有自己的独立仓库,管理起来比较麻烦。
- Maven聚合:整个项目为一个Project,然后每个微服务是其中的一个Module。
- 优点:项目代码集中,管理和运维方便。
- 缺点:服务之间耦合,编译时间较长。
拆分商品、购物车服务
4.1 商品微服务
1)定义SpringBoot、SpringCloud的依赖。
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.it</groupId>
<artifactId>scmall</artifactId>
<packaging>pom</packaging>
<version>1.0.0</version>
<modules>
<module>sc-common</module>
<module>sc-service</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.12</version>
<relativePath/>
</parent>
<properties>
<mybatis-plus.version>3.4.3</mybatis-plus.version>
<hutool.version>5.8.11</hutool.version>
<mysql.version>8.0.23</mysql.version>
</properties>
<!-- 对依赖包进行管理 -->
<dependencyManagement>
<dependencies>
<!--spring cloud-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--spring cloud alibaba-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 数据库驱动包管理 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- mybatis plus 管理 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!--hutool工具包-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- lombok 管理 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${org.projectlombok.version}</version>
</dependency>
<!--单元测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>11</source> <!-- depending on your project -->
<target>11</target> <!-- depending on your project -->
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
2)创建模块。
3)添加依赖。
item-service的pom.xml文件参考:
<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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.it</groupId>
<artifactId>scmall</artifactId>
<version>1.0.0</version>
</parent>
<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.it</groupId>
<artifactId>sc-common</artifactId>
<version>1.0.0</version>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!--单元测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
4)创建启动引导类。
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@MapperScan("com.scmail.item.mapper")
@SpringBootApplication
public class ItemApplication {
public static void main(String[] args) {
SpringApplication.run(ItemApplication.class, args);
}
}
5)复制配置文件。
复制 sc-service 中的 application.yml、application-local.yml、application-dev.yml 文件到 item-service 项目的 resources目录如下:
修改application.yml 文件,如下:
6)复制代码。

7)创建数据库表。
先创建docker中数据库容器:
# 创建网络
docker network create hm-net
docker run -d \
--name mysql \
-p 3306:3306 \
-e TZ=Asia/Shanghai \
-e MYSQL_ROOT_PASSWORD=root \
-v ./mysql/data:/var/lib/mysql \
-v ./mysql/conf:/etc/mysql/conf.d \
-v ./mysql/init:/docker-entrypoint-initdb.d \
--network hm-net \
mysql
8)测试:
接下来,就可以启动测试了;复制一个启动项并修改如下:
Copy Configuration : project 选item-service。

然后修改:修改完成后如下:
active Profiles :Local。

访问 http://localhost:8081/search/list :

浙公网安备 33010602011771号