跳转至

微服务介绍与示例引入

约 1170 个字 368 行代码 5 张图片 预计阅读时间 8 分钟

微服务介绍

微服务(Microservices)是一种将单体应用拆分为多个小型、自治服务的软件架构方式,每个服务通常围绕明确的业务能力构建,拥有相对独立的数据、代码和发布节奏,并通过HTTP、消息队列、RPC等轻量通信方式协同完成完整业务;它的核心价值在于让不同服务可以独立开发、测试、部署、扩展和演进,从而提升大型系统的灵活性与交付效率,但同时也会带来服务治理、分布式事务、链路追踪、故障隔离和运维复杂度等问题,因此微服务更适合业务边界清晰、团队协作规模较大、系统需要持续扩展和快速迭代的场景

一个单体项目拆分为微服务一般遵循如下原则:

  1. 单一职责原则:单一职责原则本是面向对象设计中的一个基本原则,它指的是一个类应该专注于单一功能。不要存在多于一个导致类变更的原因。在微服务架构中,一个微服务也应该只负责一个功能或业务领域,每个服务应该有清晰的定义和边界,只关注自己的特定业务领域
  2. 服务自治原则:每一个微服务应该有自己的存储、配置,在进行开发、构建、部署、运行和测试时,并不需要过多关注其他微服务的状态和数据

创建父工程和子工程

在新版的IDEA中,选择下面的内容:

此时生成的即为父工程

接着,在父工程中的pom文件中使用properties标签来管理依赖的版本,例如:

XML
1
2
3
4
5
6
7
8
<properties>
   <maven.compiler.source>17</maven.compiler.source> 
   <maven.compiler.target>17</maven.compiler.target> 
   <java.version>17</java.version>
   <mybatis.version>3.0.3</mybatis.version>
   <mysql.version>8.0.33</mysql.version>
   <spring-cloud.version>2022.0.3</spring-cloud.version>
 </properties>

使用<dependencyManagement></dependencyManagement>来声明依赖,但是不会引入对应的jar包,一般放在父工程的pom文件中,如果在父工程的pom中指定了依赖版本,那么子工程不显式写版本就会继承父工程指定的版本,否则使用子工程指定的版本,而使用dependencies标签直接引入依赖,会引入对应的jar

注意,父工程的packaging标签内容为pom而不是jar

XML
1
<packaging>pom</packaging>

Spring Cloud版本选择

根据官方文档的介绍进行选择:

不同的Spring Cloud第三方可能对SpringBoot最高支持版本不一致,例如Spring Cloud Alibaba:

如果使用了SpringBoot3.2.4以上的版本或者Spring Cloud 2023.0.1以上的版本都可能存在兼容性问题导致部分功能无法实现

对于最新的2025.1.x也是一样的

微服务基础示例

下面以一个电商系统的商品服务和订单服务为例

数据库搭建

使用下面的数据库和测试数据:

SQL
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
-- 建库
create database if not exists cloud_order charset utf8mb4;

-- 订单表
DROP TABLE IF EXISTS order_detail;
CREATE TABLE order_detail (
    `id` INT NOT NULL AUTO_INCREMENT COMMENT '订单id',
    `user_id` BIGINT(20) NOT NULL COMMENT '用户ID',
    `product_id` BIGINT(20) NULL COMMENT '产品id',
    `num` INT(10) NULL DEFAULT 0 COMMENT '下单数量',
    `price` BIGINT(20) NOT NULL COMMENT '实付款',
    `delete_flag` TINYINT(4) NULL DEFAULT 0,
    `create_time` DATETIME DEFAULT now(),
    `update_time` DATETIME DEFAULT now(),
PRIMARY KEY (`id`) ENGINE = INNODB DEFAULT CHARACTER
SET = utf8mb4 COMMENT = '订单表';

-- 数据初始化
insert into order_detail (user_id,product_id,num,price)
values
(2001,1001,1,99),(2002,1002,1,30),(2001,1003,1,40),
(2003,1004,3,58),(2004,1005,7,85),(2005,1006,7,94);

create database if not exists cloud_product charset utf8mb4;

-- 产品表
DROP TABLE IF EXISTS product_detail;
CREATE TABLE product_detail (
    `id` INT NOT NULL AUTO_INCREMENT COMMENT '产品id',
    `product_name` varchar(128) NULL COMMENT '产品名称',
    `product_price` BIGINT(20) NOT NULL COMMENT '产品价格',
    `state` TINYINT(4) NULL DEFAULT 0 COMMENT '产品状态 0-有效 1-下架',
    `create_time` DATETIME DEFAULT now(),
    `update_time` DATETIME DEFAULT now(),
PRIMARY KEY (`id`) ENGINE = INNODB DEFAULT CHARACTER
SET = utf8mb4 COMMENT = '产品表';

-- 数据初始化
insert into product_detail (id,product_name,product_price,state)
values
(1001,'T恤',101,0),(1002,'短袖',30,0),(1003,'短裤',44,0),
(1004,'卫衣',58,0),(1005,'马甲',98,0),(1006,'羽绒服',101,0),
(1007,'冲锋衣',30,0),(1008,'袜子',44,0),(1009,'鞋子',58,0),
(10010,'毛衣',98,0);

引入依赖搭建父子工程

参考下面的配置代码:

XML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?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>org.epsda</groupId>
    <artifactId>spring-cloud-demo01</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <modules>
        <module>order-service</module>
        <module>product-service</module>
    </modules>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.5.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <java.version>17</java.version>
        <mybatis.version>3.0.5</mybatis.version>
        <mysql.version>8.0.33</mysql.version>
        <spring-cloud.version>2025.0.0</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>${mybatis.version}</version>
            </dependency>
            <dependency>
                <groupId>com.mysql</groupId>
                <artifactId>mysql-connector-j</artifactId>
                <version>${mysql.version}</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter-test</artifactId>
                <version>${mybatis.version}</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>
XML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<?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>
    <parent>
        <groupId>org.epsda</groupId>
        <artifactId>spring-cloud-demo01</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>order-service</artifactId>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
        </dependency>
        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
                <includes>
                    <include>**/**</include>
                </includes>
            </resource>
        </resources>
    </build>

</project>
XML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<?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>
    <parent>
        <groupId>org.epsda</groupId>
        <artifactId>spring-cloud-demo01</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>product-service</artifactId>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
        </dependency>
        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
                <includes>
                    <include>**/**</include>
                </includes>
            </resource>
        </resources>
    </build>

</project>

编写商品服务基础代码

Java
1
2
3
4
5
6
7
8
9
@Data
public class Product {
    private Integer id;
    private String productName;
    private Integer productPrice;
    private Integer state;
    private Date createTime;
    private Date updateTime;
}
Java
1
2
3
4
5
6
7
@Mapper
public interface ProductMapper {

    // 根据id查询
    @Select("select * from product_detail where id = #{id}")
    Product selectProductById(Integer id);
}
Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@RequestMapping("/product")
@RestController
public class ProductController {
    @Autowired
    private ProductService productService;

    @RequestMapping("/{id}")
    public Product getProductById(@PathVariable Integer id) {
        return productService.getProductById(id);
    }
}
Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@Service
public class ProductService {
    @Autowired
    private ProductMapper productMapper;

    public Product getProductById(Integer id) {
        return productMapper.selectProductById(id);
    }

}
YAML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
server:
port: 8082

spring:
datasource:
    url: jdbc:mysql://127.0.0.1:3306/cloud_product?characterEncoding=utf8&useSSL=false
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
# 设置 Mybatis 的 xml 保存路径
mybatis:
mapper-locations: classpath:mapper/*Mapper.xml
configuration: # 配置打印 MyBatis 执行的 SQL
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: true  #自动驼峰转换

编写订单服务基础代码

先准备Controller、Order实体类、Mapper、配置文件:

Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Data // 来自父工程
public class Order {
    private Integer id;
    private Integer userId;
    private Integer productId;
    private Integer num;
    private Integer price;
    private Integer deleteFlag;
    private Date createTime;
    private Date updateTime;
}
Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@RequestMapping("/order")
@RestController
public class OrderController {
    @Autowired
    private OrderService orderService;

    @RequestMapping("/{id}")
    public Order getOrderById(@PathVariable Integer id) {
        return orderService.getOrderById(id);
    }
}
Java
1
2
3
4
5
6
7
@Mapper
public interface OrderMapper {

    // 根据id查询订单信息
    @Select("select * from order_detail where id = #{id}")
    Order selectOrderById(Integer id);
}
YAML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
server:
port: 8081

spring:
datasource:
    url: jdbc:mysql://127.0.0.1:3306/cloud_order?characterEncoding=utf8&useSSL=false
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
# 设置 Mybatis 的 xml 保存路径
mybatis:
mapper-locations: classpath:mapper/*Mapper.xml
configuration: # 配置打印 MyBatis 执行的 SQL
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: true  #自动驼峰转换

用户下订单,后台需要知道用户需要针对哪一个商品进行下单,即从商品表中获取到商品信息,所以订单服务中需要用到商品服务的查询商品接口,但是目前是微服务,订单服务模块不能直接调用商品服务内的接口,此时就需要有一个工具可以实现远程调用,本次使用HTTP请求的方式来实现,即订单服务通过发起HTTP请求给商品服务对应的接口以获取到商品信息。在Spring Cloud中内置了RestTemplate用于基础的远程调用,使用方式如下:

先拿到RestTemplate对象:

Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestTemplateConfig {

    // 注入RestTemplate对象
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

创建Product实体类信息以保存RestTemplate拿到的商品结果:

Java
1
2
3
4
5
6
7
8
9
@Data
public class Product {
    private Integer id;
    private String productName;
    private Integer productPrice;
    private Integer state;
    private Date createTime;
    private Date updateTime;
}

Product实体类添加到Order实体类中:

Java
1
2
3
4
5
6
@Data
public class Order {
    // ...
    // 保存查询Product的数据
    private Product product;
}

使用RestTemplate编写订单服务Service:

Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@Service
public class OrderService {
    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private RestTemplate restTemplate;

    public Order getOrderById(Integer id) {
        // 将从ProductServiceApplication查询到的数据保存到Order中
        // 两个服务不相互影响
        Order order = orderMapper.selectOrderById(id);
        // 通过RestTemplate发送HTTP请求从ProductServiceApplication查询到的数据保存到Order中
        Product product = restTemplate.getForObject("http://localhost:8082/product/" + order.getProductId(), Product.class);
        order.setProduct(product);
        return order;
    }
}

总结

在上面的示例中,已经基本有了微服务的思想,两个微服务分别是订单服务和商品服务,订单服务通过发起HTTP请求从商品服务中拿取到数据,从上面的示例可以看出,在开发中,负责订单服务的开发人员只需要关注订单服务的逻辑,商品服务的只需要关注商品的逻辑,进而降低了代码之间的耦合度