09.SpringCloud案例
springcloud-demo (opens new window)
# 01.SpringCloud事例
# 0、服务说明
springcloud-demo:父工程,管理依赖
order-service:订单微服务,负责订单相关业务
user-service:用户微服务,负责用户相关业务
要求:
订单微服务和用户微服务都必须有各自的数据库,相互独立
订单服务和用户服务都对外暴露Restful的接口
订单服务如果需要查询用户信息,只能调用用户服务的Restful接口,不能查询用户数据库
SpringBoot
- Spring Boot 是整个项目的基础框架,用于简化 Spring 应用的开发、配置和部署
SpringCloud
- Spring Cloud 提供了一系列工具,帮助构建分布式系统和微服务架构
- 服务注册与发现:通过 Eureka、Consul 或 Nacos 实现服务注册与发现
- 配置管理:通过 Spring Cloud Config 或 Nacos 管理分布式系统的配置
- 负载均衡:通过 Ribbon 或其他负载均衡器实现客户端负载均衡
- 断路器:通过 Hystrix 或 Resilience4j 提供断路器功能,增强系统的容错性
- API 网关:通过 Zuul 或 Spring Cloud Gateway 实现 API 网关功能
nacos
- 配置中心:Nacos 提供动态配置服务,用于集中化管理应用配置,支持实时更新和推送
- 服务注册与发现:Nacos 作为服务注册中心,支持服务的注册、发现和健康检查,帮助实现微服务架构
feign
- 声明式 HTTP 客户端:Feign 提供了一个声明式的 HTTP 客户端,通过注解方式定义 HTTP 请求
- 负载均衡和断路器:与 Ribbon 和 Hystrix 集成,实现客户端负载均衡和断路器功能
gateway
- API 网关:Spring Cloud Gateway 提供了一种简单而有效的方式来路由和管理 API 请求
- 路由和过滤:通过配置路由规则和过滤器,实现请求的路由、过滤和管理
- 安全性和监控:集成安全性、监控和限流等功能,增强 API 网关的能力
# 1、导入Sql语句
# 启动MySQL
docker run --name mysql-server -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql
mysql -h 127.0.0.1 -P 3306 -u root -p123456
1
2
3
2
3
- 创建cloud_user
create database cloud_user charset utf8;
use cloud_user;
CREATE TABLE `tb_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '收件人',
`address` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `username`(`username`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 109 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
INSERT INTO `tb_user` VALUES (1, '柳岩', '湖南省衡阳市');
INSERT INTO `tb_user` VALUES (2, '文二狗', '陕西省西安市');
INSERT INTO `tb_user` VALUES (3, '华沉鱼', '湖北省十堰市');
INSERT INTO `tb_user` VALUES (4, '张必沉', '天津市');
INSERT INTO `tb_user` VALUES (5, '郑爽爽', '辽宁省沈阳市大东区');
INSERT INTO `tb_user` VALUES (6, '范兵兵', '山东省青岛市');
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- 创建cloud_order
create database cloud_order charset utf8;
use cloud_order;
CREATE TABLE `tb_order` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '订单id',
`user_id` bigint(20) NOT NULL COMMENT '用户id',
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '商品名称',
`price` bigint(20) NOT NULL COMMENT '商品价格',
`num` int(10) NULL DEFAULT 0 COMMENT '商品数量',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `username`(`name`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 109 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
INSERT INTO `tb_order` VALUES (101, 1, 'Apple 苹果 iPhone 12 ', 699900, 1);
INSERT INTO `tb_order` VALUES (102, 2, '雅迪 yadea 新国标电动车', 209900, 1);
INSERT INTO `tb_order` VALUES (103, 3, '骆驼(CAMEL)休闲运动鞋女', 43900, 1);
INSERT INTO `tb_order` VALUES (104, 4, '小米10 双模5G 骁龙865', 359900, 1);
INSERT INTO `tb_order` VALUES (105, 5, 'OPPO Reno3 Pro 双模5G 视频双防抖', 299900, 1);
INSERT INTO `tb_order` VALUES (106, 6, '美的(Midea) 新能效 冷静星II ', 544900, 1);
INSERT INTO `tb_order` VALUES (107, 2, '西昊/SIHOO 人体工学电脑椅子', 79900, 1);
INSERT INTO `tb_order` VALUES (108, 3, '梵班(FAMDBANN)休闲男鞋', 31900, 1);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 2、安装启动nacos
- https://github.com/alibaba/nacos/releases
cd /Users/tom.xiao/soft
unzip nacos-server-2.0.3.zip
cd nacos/bin
vim startup.sh # 由于电脑中安装多版本java,需要指定使用java8启动
# JAVA_HOME="/Library/Java/JavaVirtualMachines/jdk-1.8.jdk/Contents/Home"
sh startup.sh -m standalone
# 输出nacos is starting with standalone 即为成功
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
访问:http://127.0.0.1:8848/nacos/#/login (账号密码都是nacos)
管理命令
# 1)启动
cd /Users/tom.xiao/soft/nacos/bin
sh startup.sh -m standalone
# 2)关闭
lsof -i:8848
kill -9 进程号
# 3)查看日志
tail /Users/tom.xiao/soft/nacos/logs/start.out
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 3、访问接口
http://localhost:8081/user/1/
{"id":1,"username":"柳岩","address":"湖南省衡阳市"}
1
http://localhost:8088/order/101/
{"id":101,"price":699900,"name":"Apple 苹果 iPhone 12 ","num":1,"userId":1,"user":{"id":1,"username":"柳岩","address":"湖南省衡阳市"}}
1
http://localhost:10010/order/101/?authorization=admin
{"id":101,"price":699900,"name":"Apple 苹果 iPhone 12 ","num":1,"userId":1,"user":{"id":1,"username":"柳岩","address":"湖南省衡阳市"}}
1
# 4、创建项目
- 创建一个空maven项目
- 添加user-service模块
# 02.代码
# 0、项目结构
# 1) 项目结构
springcloud-demo
├── pom.xml
└── user-service # 一、用户服务模块(提供用户查询接口)
├── pom.xml
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── example
│ │ │ └── user
│ │ │ ├── UserApplication.java
│ │ │ ├── config
│ │ │ │ └── PatternProperties.java
│ │ │ ├── mapper
│ │ │ │ └── UserMapper.java
│ │ │ ├── pojo
│ │ │ │ └── User.java
│ │ │ ├── service
│ │ │ │ └── UserService.java
│ │ │ └── web
│ │ │ └── UserController.java
│ │ └── resources
│ │ ├── application.yml
│ │ └── bootstrap.yml
├── order-service # 二、订单服务模块(订单查询&调用 用户服务 查询用户信息)
│ ├── pom.xml
│ ├── src
│ │ ├── main
│ │ │ ├── java
│ │ │ │ └── com
│ │ │ │ └── example
│ │ │ │ └── order
│ │ │ │ ├── OrderApplication.java
│ │ │ │ ├── mapper
│ │ │ │ │ └── OrderMapper.java
│ │ │ │ ├── pojo
│ │ │ │ │ └── Order.java
│ │ │ │ ├── service
│ │ │ │ │ └── OrderService.java
│ │ │ │ └── web
│ │ │ │ └── OrderController.java
│ │ │ └── resources
│ │ │ └── application.yml
├── feign-api # 定义feign
│ ├── pom.xml
│ ├── src
│ │ ├── main
│ │ │ ├── java
│ │ │ │ └── com
│ │ │ │ └── example
│ │ │ │ └── feign
│ │ │ │ ├── clients
│ │ │ │ │ └── UserClient.java
│ │ │ │ ├── config
│ │ │ │ │ └── DefaultFeignConfiguration.java
│ │ │ │ └── pojo
│ │ │ │ └── User.java
├── gateway # 定义网关代理(主要在 yml中配置即可)
│ ├── pom.xml
│ ├── src
│ │ ├── main
│ │ │ ├── java
│ │ │ │ └── com
│ │ │ │ └── example
│ │ │ │ └── gateway
│ │ │ │ ├── AuthorizeFilter.java
│ │ │ │ └── GatewayApplication.java
│ │ │ └── resources
│ │ │ └── application.yml
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
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
# 2) pom.xml父级
springcloud-demo/pom.xml
项目依赖管理
<?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.example</groupId>
<artifactId>springcloud-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>user-service</module>
<module>order-service</module>
<module>feign-api</module>
<module>gateway</module>
</modules>
<!--pom用于父项目或聚合项目可以告诉Maven将这些项目打包成pom文件,而不是可执行的文件-->
<packaging>pom</packaging>
<!--当前项目将继承Spring Boot父项目的配置,包括依赖管理、插件配置等-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.9.RELEASE</version>
<relativePath/>
</parent>
<!--这些属性可以在整个项目中重用,以保持版本的一致性和简化版本管理-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR10</spring-cloud.version>
<mysql.version>5.1.47</mysql.version>
<mybatis.version>2.1.1</mybatis.version>
</properties>
<!--用于集中管理项目的所有依赖版本,这部分的依赖版本不会自动引入到子项目中,但子项目可以引用这些依赖而不需要再次指定版本 -->
<dependencyManagement>
<dependencies>
<!-- springCloud -->
<!--这个配置不仅指定了 spring-cloud-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>
<!--nacos的管理依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.5.RELEASE</version>
<type>pom</type>
<scope>import</scope>
<!--它只在依赖解析和执行过程中有效,但不会被包含到构建的结果中-->
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<!--没有指定版本,因为lombok通常由Spring Boot的父项目管理,所以它会从那里继承版本-->
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
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
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
# 1、user-service
# 0) pom.xml
<?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>springcloud-demo</artifactId>
<groupId>com.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>user-service</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</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>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!-- nacos客户端依赖包 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--nacos的配置管理依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
</dependencies>
<build>
<!--用于Spring Boot应用程序的Maven项目中,以简化Spring Boot应用程序的打包-->
<finalName>app</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
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
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
# 1) resource
- resources/application.yml
server:
port: 8081
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/cloud_user?useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
shardingsphere: # 配置项是用于配置ShardingSphere的分片策略,但在这段代码中并未具体配置
sharding:
default-database-strategy:
tables:
# discovery:
# cluster-name: HZ
mybatis:
type-aliases-package: com.example.user.pojo
configuration:
map-underscore-to-camel-case: true #表示将数据库表中的下划线命名转换为驼峰命名
logging:
level:
com.example: debug
pattern:
dateformat: MM-dd HH:mm:ss:SSS
#eureka:
# client:
# service-url: # eureka的地址信息
# defaultZone: http://127.0.0.1:10086/eureka
pattern:
name: 本地环境local # 本地环境local 配置项可能是自定义的配置,表示当前环境名称
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
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
- resources/bootstrap.yml
# 这段代码是Spring Boot应用的配置文件片段,它使用了Spring Cloud Alibaba Nacos作为配置中心和服务注册与发现的组件
spring:
application:
name: userservice #设置应用名称,这个名称在微服务架构中通常用于服务发现和负载均衡
profiles:
active: dev # 环境
cloud:
nacos:
server-addr: 127.0.0.1:8848 # nacos地址
config:
file-extension: yaml # 文件后缀名
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 2) config
- config/PatternProperties.java
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 当Spring Boot应用启动时,它会自动从配置文件中读取pattern.dateformat、pattern.envSharedValue和pattern.name的值
* 并设置到PatternProperties类的对应字段上
* 在应用的其他地方,你可以通过注入PatternProperties Bean来获取这些配置属性的值
*/
@Data
@Component
@ConfigurationProperties(prefix = "pattern")
public class PatternProperties {
private String dateformat;
private String envSharedValue;
private String name;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 3) UserApplication.java
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@MapperScan("com.example.user.mapper")
@SpringBootApplication
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 4) web
- web/UserController.java
import com.example.user.config.PatternProperties;
import com.example.user.pojo.User;
import com.example.user.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@Slf4j
@RestController
@RequestMapping("/user")
// @RefreshScope
public class UserController {
@Autowired
private UserService userService;
// @Value("${pattern.dateformat}")
// private String dateformat;
@Autowired
private PatternProperties properties;
@GetMapping("prop")
public PatternProperties properties(){
return properties;
}
@GetMapping("now")
public String now(){
return LocalDateTime.now().format(DateTimeFormatter.ofPattern(properties.getDateformat()));
}
/**
* 路径: /user/110
*
* @param id 用户id
* @return 用户
*/
@GetMapping("/{id}")
public User queryById(@PathVariable("id") Long id,
@RequestHeader(value = "Truth", required = false) String truth) {
System.out.println("truth: " + truth);
return userService.queryById(id);
}
}
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
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
# 5) service
- service/UserService.java
import com.example.user.mapper.UserMapper;
import com.example.user.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public User queryById(Long id) {
return userMapper.findById(id);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 6) mapper
- mapper/UserMapper.java
import com.example.user.pojo.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
public interface UserMapper {
@Select("select * from tb_user where id = #{id}")
User findById(@Param("id") Long id);
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 7) pojo
- pojo/User.java
import lombok.Data;
@Data
public class User {
private Long id;
private String username;
private String address;
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 2、order-service
# 0) pom.xml
<?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>com.example</groupId>
<artifactId>springcloud-demo</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.example</groupId>
<artifactId>order-service</artifactId>
<dependencies>
<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>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!-- nacos客户端依赖包 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--feign客户端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--引入HttpClient依赖-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>user-service</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>feign-api</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<!--引入feign的统一api-->
<dependency>
<groupId>com.example</groupId>
<artifactId>feign-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<finalName>app</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
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
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
# 1) resource
- resources/application.yml
server:
port: 8088
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/cloud_order?useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
application:
name: orderservice #设置应用名称,这个名称在微服务架构中通常用于服务发现和负载均衡
cloud:
nacos:
server-addr: 127.0.0.1:8848 # nacos服务地址
# discovery:
# namespace: 4d6ce343-9e1b-44df-a90f-2cf2b6b3d177 # dev环境
# ephemeral: false # 是否是临时实例
mybatis:
type-aliases-package: com.example.user.pojo
configuration:
map-underscore-to-camel-case: true
logging:
level:
com.example: debug
pattern:
dateformat: MM-dd HH:mm:ss:SSS
#eureka:
# client:
# service-url: # eureka的地址信息
# defaultZone: http://127.0.0.1:10086/eureka
userservice: #这个类是Nacos提供的一个Ribbon的负载均衡策略实现,它可以和Nacos注册中心更好地配合工作
ribbon:
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则
ribbon: # 在应用启动时,就会预先创建指定服务的LoadBalancer,而不是在第一次请求时才创建
eager-load:
enabled: true # 开启饥饿加载
clients: # 指定饥饿加载的服务名称
- userservice
feign:
httpclient:
enabled: true # 支持HttpClient的开关
max-connections: 200 # 最大连接数
max-connections-per-route: 50 # 单个路径的最大连接数
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
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
# 2) OrderApplication.java
import com.example.feign.clients.UserClient;
import com.example.feign.config.DefaultFeignConfiguration;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@MapperScan("com.example.order.mapper")
@SpringBootApplication
@EnableFeignClients(clients = UserClient.class,defaultConfiguration = DefaultFeignConfiguration.class)
// 这个注解启用Feign客户端,使得可以通过声明式的方式调用远程服务
// clients属性指定了哪些接口被注册为Feign客户端,defaultConfiguration属性指定了Feign客户端的默认配置类
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
/**
* 创建RestTemplate并注入Spring容器
* RestTemplate是Spring提供的一个用于发起HTTP请求的同步客户端
* 给RestTemplate实例添加负载均衡的能力,它会根据服务名称来选择合适的实例
* 在与Eureka或其他服务发现工具结合使用时,这允许RestTemplate通过逻辑服务名称(而不是具体的URL)来调用服务
*/
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean // 创建Ribbon的负载均衡规则, 指定使用随机选择算法
public IRule randomRule() {
return new RandomRule();
}
}
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
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
# 3) web
- web/OrderController.java
import com.example.order.pojo.Order;
import com.example.order.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("order")
public class OrderController {
@Autowired
private OrderService orderService;
@GetMapping("{orderId}")
public Order queryOrderByUserId(@PathVariable("orderId") Long orderId) {
// 根据id查询订单并返回
return orderService.queryOrderById(orderId);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 4) service
- service/OrderService.java
import com.example.feign.clients.UserClient;
import com.example.feign.pojo.User;
import com.example.order.mapper.OrderMapper;
import com.example.order.pojo.Order;
//import com.example.user.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
//import org.springframework.web.client.RestTemplate;
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private UserClient userClient;
public Order queryOrderById(Long orderId) {
// 1.查询订单
Order order = orderMapper.findById(orderId);
// 2.用Feign远程调用
User user = userClient.findById(order.getUserId());
// 3.封装user到Order
order.setUser(user);
// 4.返回
return order;
}
/*
@Autowired
private RestTemplate restTemplate;
public Order queryOrderById(Long orderId) {
// 1.查询订单
Order order = orderMapper.findById(orderId);
// 2.利用RestTemplate发起http请求,查询用户
// 2.1.url路径
String url = "http://userservice/user/" + order.getUserId();
// 2.2.框架中使用RestTemplate对象发送HTTP GET请求,将响应体转换成User对象
User user = restTemplate.getForObject(url, User.class);
// 3.封装user到Order
order.setUser(user);
// 4.返回
return order;
}
*/
}
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
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
# 5) mapper
- mapper/OrderMapper.java
import com.example.order.pojo.Order;
import org.apache.ibatis.annotations.Select;
public interface OrderMapper {
@Select("select * from tb_order where id = #{id}")
Order findById(Long id);
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 6) pojo
- pojo/Order.java
import com.example.feign.pojo.User;
import lombok.Data;
//import com.example.user.pojo.User;
@Data
public class Order {
private Long id;
private Long price;
private String name;
private Integer num;
private Long userId;
private User user;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 3、feign-api
# 0) pom.xml
<?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>com.example</groupId>
<artifactId>springcloud-demo</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.example</groupId>
<artifactId>feign-api</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
</project>
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
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
# 1) config
- config/DefaultFeignConfiguration.java
import feign.Logger;
import org.springframework.context.annotation.Bean;
/**
* 当你在Feign客户端上应用这个配置类时,Feign会按照BASIC级别来记录日志
* 这样你就可以看到Feign发送的HTTP请求的方法和URL,以及收到的HTTP响应的状态码和执行时间
*/
public class DefaultFeignConfiguration {
@Bean
public Logger.Level logLevel(){
return Logger.Level.BASIC;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 2) clients
- clients/UserClient.java
import com.example.feign.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* 这个接口的作用是,当你调用findById方法的时候,Feign会在网络上发起一个到"userservice"服务的HTTP请求
* 请求路径是"/user/"加上你传入的id值,然后等待并处理响应
*/
@FeignClient(value = "userservice") // 注解这是一个Feign客户端,"userservice"是请求服务名
public interface UserClient {
@GetMapping("/user/{id}") // 定义HTTP请求是一个GET请求,请求的路径是"/user/{id}"
User findById(@PathVariable("id") Long id);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 3) pojo
- pojo/User.java
import lombok.Data;
@Data
public class User {
private Long id;
private String username;
private String address;
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 4、gateway
# 0) pom.xml
<?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>com.example</groupId>
<artifactId>springcloud-demo</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>gateway</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--nacos服务注册发现依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--网关gateway依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
<build>
<finalName>app</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
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
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
# 1) resource
- resources/application.yml
server:
port: 10010
logging:
level:
cn.itcast: debug
pattern:
dateformat: MM-dd HH:mm:ss:SSS
spring:
application:
name: gateway
cloud:
nacos:
server-addr: 127.0.0.1:8848 # nacos地址
gateway:
routes:
- id: user-service # 路由标示,必须唯一
uri: lb://userservice # 路由的目标地址
predicates: # 路由断言,判断请求是否符合规则
- Path=/user/** # 路径断言,判断路径是否是以/user开头,如果是则符合
- id: order-service
uri: lb://orderservice
predicates:
- Path=/order/**
default-filters:
- AddRequestHeader=Truth,Itcast is freaking awesome!
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 2) gateway
- gateway/GatewayApplication.java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
- gateway/AuthorizeFilter.java
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* 这段代码是Spring Cloud Gateway中的一个全局过滤器,用于对所有经过的请求进行授权验证
* 全局过滤器是在所有路由之前执行
*/
// @Order(-1) //指定过滤器的执行顺序,数字越小,优先级越高,越早执行
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1.获取请求参数
ServerHttpRequest request = exchange.getRequest();
MultiValueMap<String, String> params = request.getQueryParams();
// 2.获取参数中的 authorization 参数
String auth = params.getFirst("authorization");
// 3.判断参数值是否等于 admin
if ("admin".equals(auth)) {
// 4.是,放行
return chain.filter(exchange);
}
// 5.否,拦截
// 5.1.设置状态码
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
// 5.2.拦截请求
return exchange.getResponse().setComplete();
}
@Override
public int getOrder() {
return -1;
}
}
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
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
上次更新: 2024/6/17 19:12:09