1. Spring Could

本章节的代码:https://github.com/wicksonZhang/Spring-Cloud

1.1. 微服务是什么?

  • 微服务是针对大型项目复杂性而设计的软件架构模式,它将整个应用拆分成小型、独立的服务单元,每个服务专注于特定的业务功能。这种拆分降低了系统之间的耦合性,是的每个微服务之间能够独立开发、部署和扩展。

  • 如下图就是微服务 (microservices) 架构示例:

    Microservices-3.png

1.2. 微服务有什么优缺点

优点:

  1. 解耦性: 微服务架构将业务模块拆分,每个模块通过 API 进行服务调用,降低了模块间的紧密耦合度。
  2. 灵活性和可扩展性:当模块进行拆分之后每个模块就变得更加独立,而且修改每个模块的内容时不会影响到其他模块。

缺点:

  1. 分布式系统复杂性: 微服务拆分后需要处理分布式通信、数据一致性等问题,可能增加项目复杂性。
  2. 部署和运维成本过高:微服务架构拆分为多个模块之后,涉及到多个模块的维护,而部署时会涉及到多个服务的管理和运维。

1.3. 微服务解决了什么问题?

上面的描述已经解释的差不多了,微服务项目主要还是解决了项目庞大之后不易维护的问题。

  • 2000 ~ 2010 年左右(服务导向架构(SOA))
    • 2000年前后至2010年左右是 SOA 概念逐渐兴起和发展的阶段。
  • 2006 年左右( 云计算和容器技术兴起 )
    • 云计算概念开始在2006年左右兴起,容器技术如Docker于2013年左右开始引起广泛关注。
  • 2010 年左右( Netflix 和互联网公司的实践 )
    • Netflix 等互联网巨头在2010年左右开始采用微服务架构,并提出了一系列开源工具和实践经验,如 Eureka(服务发现)、Hystrix(容错和降级)、Ribbon(负载均衡)等。
  • 2014 年左右( Martin Fowler 提出 “微服务” 概念 )
    • Martin Fowler等人在2014年左右正式提出“微服务”的概念。
    • 微服务的概念传入中国,2015年左右国内大厂开始进行项目升级,转战微服务
  • 2015 年左右 ( 微服务的流行和演进 )

1.4. 微服务应用场景

  • 大型复杂应用: 当项目规模过大时,采用微服务架构可以将整个系统拆分为小的、自治的服务单元。类似于商城系统中可以将支付服务和订单服务进行拆分。

    image-20231226152558752

  • 技术异构性需求: 微服务架构的宗旨在于只对外暴露 api 接口,不在乎内部是用什么语言进行开发,如果不同业务需要采用不同技术栈时可以采用微服务。

  • 弹性和可扩展性: 微服务的架构可以根据业务的发展有针对性的水平扩展具体的服务,而不需要扩展整个应用。

    image-20231226152700480

2. 微服务技术架构选型

2.1. Java 微服务技术架构选型

刚刚我们在上面已经说明了微服务架构需要使用到的一些技术架构。例如,服务注册与发现、负载均衡、网关等等。

  • 下图中说明当前依旧主流使用的微服务技术架构,其中的❌表示不再维护停止更新,✅表示还在维护。

    微服务组件选型

2.2. Spring Boot 版本选择

2.3. Spring Cloud 版本选择

Spring Cloud 官网地址:https://spring.io/projects/spring-cloud/

3. 微服务技术实践

我们本次实践的案例是以订单、支付两个模块,但是其中会有很多开发细节《尚硅谷SpringCloud框架开发教程》不太一样,因为在学习过程中和开发是有很大不一样的。

  • 需求信息

    • 我们通过访问订单微服务 02-spring-cloud-order-2000 的接口,然后在订单微服务的内部支付微服务 03-spring-cloud-payment-3000 的接口。
  • 实现步骤

    • Step-1:创建项目
    • Step-2:导入 Maven 依赖
    • Step-3:修改 yml/properties 文件
    • Step-4:开发业务逻辑
  • 项目结构如下,后续新增的模块也会以如下格式进行新增

plaintext
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
├─Spring-Cloud # 父级工程
│ ├─01-spring-cloud-common # 公共模块
│ │ ├─src
│ │ └─main
│ │ └─java
│ │ │ └─cn.wickson.cloud.common
│ │ │ └─enums # 枚举类
│ │ │ ├─handle # 全局统一处理类
│ │ │ ├─model # 实体信息
│ │ │ │ ├─dto
│ │ │ │ ├─entity
│ │ │ │ └─vo
│ │ │ └─utils # 工具类信息
│ │ └─resources
│ │
│ ├─02-spring-cloud-order-2000 # 订单模块
│ │ ├─src
│ │ ├─main
│ │ ├─java
│ │ │ └─cn.wickson.cloud.order
│ │ │ ├─config
│ │ │ ├─controller
│ │ └─resources
│ │
│ └─02-spring-cloud-payment-2100 # 支付模块
│ ├─src
│ ├─main
│ ├─java
│ │ └─cn.wickson.cloud.payment
│ │ ├─controller
│ │ ├─convert
│ │ └─service
│ │ └─impl
│ └─resources

3.1. 父级工程

  • 父级工程只提供 pom.xml 依赖信息,具体信息如下:

    image-20231228114944103

  • pom.xml 信息

    • 注意:在如下配置没有引入 mybatisdruidmysql-connector-java 这些依赖信息。
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
<?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>cn.wickson</groupId>
<artifactId>Spring-Cloud</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>

<!--子项目基本信息-->
<modules>
<module>01-spring-cloud-common</module>
<module>02-spring-cloud-order-2000</module>
<module>02-spring-cloud-payment-2100</module>
</modules>

<!-- 统一管理jar包版本 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<junit.version>4.12</junit.version>
<log4j.version>1.2.17</log4j.version>
<lombok.version>1.16.18</lombok.version>
<hutool.version>5.8.12</hutool.version>
<mapstruct.version>1.5.3.Final</mapstruct.version>
</properties>

<!--项目依赖包统一管理-->
<dependencyManagement>
<dependencies>
<!-- spring boot 2.2.2 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>

<!-- spring cloud Hoxton.SR1 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>

<!-- hutool工具依赖包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>

<!-- lombok 工具依赖包 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<optional>true</optional>
</dependency>

<!-- MapStruct 依赖包 -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
</dependency>

<!-- mapstruct-processor依赖包 -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</dependency>

</dependencies>
</dependencyManagement>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<addResources>true</addResources>
</configuration>
</plugin>
</plugins>
</build>

</project>

3.2. 公共模块

公共模块主要提供了两个服务公用的逻辑。例如,统一的数据结果返回、公共的实体类信息等等。

  • 项目具体信息如下:

    image-20231228095051904

  • 其中值得关注的类有两个 GlobalExceptionHandlerGlobalResponseHandler

    • GlobalExceptionHandler:全局异常处理器
    • GlobalResponseHandler :全局统一返回结果集处理器
  • GlobalResponseHandler.java

    • 注意:使用时需要在 @ControllerAdvice 添加相关包扫描信息。并且在相关的 @SpringBootApplication 启动类添加包扫描。
    java
    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
    /**
    * 数据结果统一格式全局处理类
    *
    * @author ZhangZiHeng
    * @date 2023-12-27
    */
    @ControllerAdvice(
    basePackages = {"cn.wickson.cloud.payment.controller", "cn.wickson.cloud.order.controller"}
    )
    public class GlobalResponseHandler implements ResponseBodyAdvice<Object> {

    /**
    * 是否支持advice功能
    *
    * @return 返回值:true=表示开启, false=表示关闭
    */
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
    return true;
    }

    /**
    * 处理response的具体业务方法
    */
    @Override
    public Object beforeBodyWrite(Object body,
    MethodParameter returnType,
    MediaType selectedContentType,
    Class<? extends HttpMessageConverter<?>> selectedConverterType,
    ServerHttpRequest request,
    ServerHttpResponse response) {

    // 校验Controller层传递过来的值是否为String类结构的数据,为真则转成Json格式,以保持统一格式返回客户端
    if (body instanceof String) {
    return JSONUtil.toJsonStr(ResultUtil.success(body));
    }

    // 防止重复包裹的问题出现
    if (body instanceof ResultUtil) {
    return body;
    }

    // 如果body实现 ResultUnpacked 接口类,则不需要返回统一结果封装
    if (body instanceof ResultUnpacked) {
    return body;
    }

    // 返回结果值给客户端
    return ResultUtil.success(body);
    }

    }
  • SpringCloudOrderApplication.java

    java
    1
    2
    3
    4
    5
    6
    7
    8
    @SpringBootApplication(scanBasePackages = {"cn.wickson.cloud"})
    public class SpringCloudOrderApplication {

    public static void main(String[] args) {
    SpringApplication.run(SpringCloudOrderApplication.class, args);
    }

    }

3.3. 订单模块

订单类主要是要调用支付类的信息,所以通过 SpringBoot 提供的 RestTemplate 进行远程调用即可。

  • 项目具体信息如下:

    image-20231228101224595

  • OrderController.java

java
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
/**
* 订单服务-控制类
*
* @author ZhangZiHeng
* @date 2023-12-27
*/
@Slf4j
@Validated
@RestController
@RequestMapping("/order")
public class OrderController {

public static final String PAYMENT_URL = "http://localhost:3000";

@Resource
private RestTemplate restTemplate;

/**
* 调用支付信息
*
* @param payment 支付信息
* @return ResultUtil
*/
@PostMapping("/create")
public ResultUtil create(@RequestBody Payment payment) {
return restTemplate.postForObject(PAYMENT_URL + "/payment/create", payment, ResultUtil.class);
}

/**
* 获取订单
*
* @param id id
* @return ResultUtil
*/
@GetMapping("/getPayment/{id}")
public ResultUtil getPayment(@PathVariable("id") Long id) {
return restTemplate.getForObject(PAYMENT_URL + "/payment/getById/" + id, ResultUtil.class);
}

}
  • RestTemplateConfig.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Configuration
public class RestTemplateConfig {

@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
return new RestTemplate(factory);
}

@Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setReadTimeout(5000);
factory.setConnectTimeout(15000);
return factory;
}
}

3.4. 支付模块

我们支付模块主要就提供了两个接口,其中一个用于生成支付订单信息,另一个用于查询订单信息。

  • 项目具体信息如下:

image-20231228115104199

  • PaymentController
java
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
/**
* 支付服务-控制类
*
* @author ZhangZiHeng
* @date 2023-12-27
*/
@Slf4j
@Validated
@RestController
@RequestMapping("/payment")
public class PaymentController {

@Resource
private IPaymentService paymentService;

/**
* 创建支付订单
*
* @param paymentVO 订单信息
*/
@PostMapping(value = "/create")
public void create(@RequestBody PaymentCreateReqVO paymentVO) {
/* Step-1:创建支付订单 */
this.paymentService.create(paymentVO);
}

/**
* 根据订单id获取支付订单
*
* @param id 订单信息
*/
@GetMapping(value = "/getById/{id}")
public PaymentRespDTO getById(@PathVariable("id") Long id) {
/* Step-1: 获取支付订单 */
return this.paymentService.getById(id);
}

}
  • PaymentServiceImpl.java
    • 在我们 PaymentServiceImpl 中并没有去操作数据库信息,但是不影响我们的核心操作。
java
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
@Service
public class PaymentServiceImpl implements IPaymentService {

private static List<Payment> list = new ArrayList<>();

/**
* 创建支付信息
*
* @param paymentVO 支付info
*/
@Override
public void create(final PaymentCreateReqVO paymentVO) {
Payment payment = PaymentConvert.INSTANCE.toEntity(paymentVO);
list.add(payment);
}

/**
* 返回支付信息
*
* @param id 订单id
* @return PaymentRespDTO
*/
@Override
public PaymentRespDTO getById(final Long id) {
if (CollUtil.isEmpty(list)) {
return new PaymentRespDTO();
}

PaymentRespDTO paymentRespDTO = new PaymentRespDTO();
for (Payment payment : list) {
Long paymentId = payment.getId();
if (paymentId.equals(id)) {
paymentRespDTO = PaymentConvert.INSTANCE.toDTO(payment);
break;
}
}
return paymentRespDTO;
}
}

3.5. 单元测试

  • 我们通过调用 订单微服务 ,然后在订单微服务调用支付微服务,如下我们通过 ApiPost 进行测试

    动画

4. 参考博文