组件
服务注册和发现
微服务模块中,一般是以集群的方式进行部署的,如果我们调用的时候以硬编码的方式,那么当服务出现问题、服务扩缩容等就需要对代码进行修改,这是非常不好的。所以微服务模块中就出现了服务注册和发现组件,比较常用的组件有Eureka、Consul、Nacos,在微服务系列中,也是针对这三个组件来进行介绍。
负载均衡
由于以集群的方式部署,因此我们就要考虑调用哪台机子的问题,所以微服务模块中又出现了负载均衡组件, 在微服务系列中,针对LoadBalancer组件进行介绍。
分布式配置管理
微服务导致会出现大量的服务,而每个服务都需要必要的配置,此时就迫切需要一套集中式的、动态的配置管理设施,在微服务系列中,针对Consul、Nacos组件进行介绍。
网关
微服务的出现导致大量的服务,从而导致大量的IP地址,这对前端是不友好的。但是,本着脏话累活后端来干的优良品质,因此又出现了一系列的组件来解决这个问题,在微服务系列中,针对Gateway组件进行介绍。
服务调用
单体项目中,想要调用别的业务只需要注入即可,但是在微服务中,业务进行拆分,因此无法通过注入的方式来进行调用。不过,例如RestTemplate、HttpClient等都是可以进行URL发送的,但是这种方式也会造成硬编码的出现。于是,微服务又出现了一系列组件来解决这个问题, 在微服务系列中,针对OpenFeign组件进行介绍。
服务链路追踪
微服务应用中,可能一个业务的完成就需要调用N个服务,如果该业务效率低下、响应结果错误。我们就需要去定位是哪里的响应慢、哪里的服务出现错误,但是靠肉眼是没法看出来的。因此,微服务又出现了组件,来对一次调用的链路进行记录,从而可以在出现问题后快速解决问题。 在微服务系列中,针对Micrometer Tracing组件进行介绍。
服务熔断和降级
微服务的出现本来就是为了解决单体项目资源不足的情况,因此当流量过多或者服务出错时,我们就要采取降低流量等措施,对于这种问题, 在微服务系列中,针对Circuit Breaker和Sentinel组件进行介绍。
分布式事务
单体应用中我们经常要用到事务,微服务应用也不例外,因此对于这种应用场景。 在微服务系列中,针对Seata组件进行介绍。
在微服务系列中,基本上就是对上述组件的介绍,不会涉及太多的底层原因,主要是介绍如何进行操作,后续有实力之后再对其源码进行分析介绍。
基础工程构建
业务场景
以订单服务调用商品服务为例,来对SpringCloud的一系列组件进行介绍。
数据准备
-- 建库,商品库
create database if not exists cloud_product charset utf8mb4;
-- 使用库
use cloud_product;
-- 产品表
DROP TABLE IF EXISTS product_detail;
CREATE TABLE product_detail (
`id` BIGINT 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);
-- 建库,订单库
create database if not exists cloud_order charset utf8mb4;
-- 使用库
use cloud_order;
-- 订单表
DROP TABLE IF EXISTS order_detail;
CREATE TABLE order_detail (
`id` BIGINT 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);
工程搭建
搭建父工程
<?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.wbz</groupId>
<artifactId>spring-cloud-test</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>cloud-provider-payment8001</module>
<module>cloud-consumer-order80</module>
</modules>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-cloud.version>2023.0.0</spring-cloud.version>
<spring-cloud-alibaba.version>2022.0.0.0-RC2</spring-cloud-alibaba.version>
<mysql.version>8.0.31</mysql.version>
<druid.version>1.2.18</druid.version>
<mybatis-plus.version>3.5.7</mybatis-plus.version>
<lombok.version>1.18.28</lombok.version>
</properties>
<!--SpringBoot-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencyManagement>
<dependencies>
<!--SpringCloud-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--SpringCloudAlibaba-->
<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>
<!--MySQL驱动-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>${mysql.version}</version>
</dependency>
<!--Druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!--MP-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!--Lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
搭建子工程 - 商品服务
在该业务背景下,商品服务作为服务提供方出现。
搭建一个模块,一般分为:建模块、写pom文件、写yml文件、改主启动类、写业务类。
建模块
写pom文件
<?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.wbz</groupId>
<artifactId>spring-cloud-test</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>cloud-provider-product8001</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>
<!--SpringBoot通用模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--Druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
<!--MySQL驱动-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<!--MP-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
写yml文件
server:
port: 8001
spring:
application:
name: cloud-provider-payment8001
mvc:
pathmatch:
matching-strategy: ant_path_matcher # 路径匹配策略
datasource:
url: jdbc:mysql://127.0.0.1:3306/cloud_product?characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true&allowMultiQueries=true
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
profiles:
active: dev
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath:mapper/**Mapper.xml
type-aliases-package: com.wbz.domain
改主启动类
/**
* 微服务生产者模块,表示被调用的一方
*/
@MapperScan("com.wbz.mapper")
@SpringBootApplication
public class ProductProviderApplication8001 {
public static void main(String[] args) {
SpringApplication.run(ProductProviderApplication8001.class, args);
}
}
写业务类
// model
/**
* 产品表
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("product_detail")
public class Product {
@TableId
private Long id;
@TableField
private String productName;
@TableField
private Long productPrice;
@TableField
private Integer state;
@TableField
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime createTime;
@TableField
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime updateTime;
}
// 持久层接口
public interface ProduceMapper extends BaseMapper<Product> {
}
// 服务层接口
public interface ProductService extends IService<Product> {
Product getProductById(Long productId);
}
// 服务层实现类
@Service
public class ProductServiceImpl extends ServiceImpl<ProduceMapper, Product> implements ProductService {
@Override
public Product getProductById(Long productId) {
return this.getById(productId);
}
}
// 控制层类
@RestController
@RequestMapping("/product")
public class ProductController {
@Resource
private ProductService productService;
@GetMapping("/query/{productId}")
public Product getProductById(@PathVariable Long productId) {
return this.productService.getProductById(productId);
}
}
启动项目之后,访问url:127.0.0.1:8001/product/query/1001,返回如下页面就算调用成功:
整体代码结构如下:
搭建子工程 - 订单服务
在该业务背景下,订单服务作为服务调用方出现。
建模块
写pom文件
<?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.wbz</groupId>
<artifactId>spring-cloud-test</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>cloud-consumer-order80</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>
<!--SpringBoot通用模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--Druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
<!--MySQL驱动-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<!--MP-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
写yml文件
server:
port: 80
spring:
application:
name: cloud-consumer-order80
mvc:
pathmatch:
matching-strategy: ant_path_matcher # 路径匹配策略
datasource:
url: jdbc:mysql://127.0.0.1:3306/cloud_order?characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true&allowMultiQueries=true
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
profiles:
active: dev
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath:mapper/**Mapper.xml
type-aliases-package: com.wbz.domain
改主启动类
/**
* 微服务消费者模块,调用的一方
*/
@SpringBootApplication
@MapperScan("com.wbz.mapper")
public class OrderConsumerApplication80 {
public static void main(String[] args) {
SpringApplication.run(OrderConsumerApplication80.class, args);
}
}
写业务类
// 实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("order_detail")
public class Order {
@TableId
private Long id;
@TableField
private Long userId;
@TableField
private Long productId;
@TableField
private Integer num;
@TableField
private Long price;
@TableField
private Integer deleteFlag;
@TableField
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime createTime;
@TableField
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime updateTime;
@TableField(exist = false)
private Product product;
}
// mapper接口
public interface OrderMapper extends BaseMapper<Order> {
}
// service接口
public interface OrderService extends IService<Order> {
Order getOrderById(Integer id);
}
// RestTemplateConfig实现类
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
// service实现类
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
@Resource
private RestTemplate restTemplate;
@Override
public Order getOrderById(Integer id) {
Order order = this.getById(id);
String url = "http://127.0.0.1:8001/product/query/" + order.getProductId();
Product product = this.restTemplate.getForObject(url, Product.class);
order.setProduct(product);
return order;
}
}
// controller实现类
@RestController
@RequestMapping("/order")
public class OrderController {
@Resource
private OrderService orderService;
@GetMapping("/query/{id}")
public Order getOrderById(@PathVariable Integer id) {
return this.orderService.getOrderById(id);
}
}
启动项目之后,访问url:127.0.0.1/order/query/1,出现如下页面就算调用成功: