目录
Spring Cloud
Spring Cloud 集成了各种微服务组件,基于 Spring Boot 实现了这些组件的自动装配,从而提供了开箱即用的效果。
Spring Cloud 与 Spring Boot 版本对应:
cloud | boot |
2020.0.x aka Ilford | 2.4.x, 2.5.x (Starting with 2020.0.3) |
Hoxton | 2.2.x, 2.3.x (Starting with SR5) |
Greenwich 2.1.x | 2.1.x |
Finchley 2.0.x | 2.0.x |
Edgware 1.5.x | 1.5.x |
Dalston 1.5.x | 1.5.x |
Dalston
、Edgware
、Finchley
、Greenwich
等四个版本已经到达生命的最后阶段不再长期支持。
一、核心组件
Spring Cloud Netfix 系列
组件名称 | 作用 |
Eureka | 服务注册、服务发现,一个基于 REST 的服务,用于定位服务,以实现云端中间层服务发现和故障转移。 |
Ribbon | 提供云端负载均衡,有多种负载均衡策略可供选择,可配合服务发现和断路器使用。 |
Feign | Feign是一种声明式、模板化的 HTTP 客户端,用于远程服务调用 |
Hystrix | 熔断器,容错管理工具,旨在通过熔断机制控制服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。 |
Zuul | API 服务网关,提供动态路由,监控,弹性,安全等边缘服务的框架 |
Spring Cloud Alibaba 系列
组件名称 | 作用 |
Nacos | 服务注册、服务发现、服务配置 |
Sentinel | 客户端容错保护,以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。 |
Spring Cloud 原生及其他核心组件
组件名称 | 作用 |
Consul | 服务注册中心,是一个服务发现与配置工具,与Docker容器可以无缝集成。 |
Config | 分布式配置中心,可以把配置放到远程服务器,集中化管理集群配置,目前支持本地存储、Git以及Subversion。 |
Gateway | API 服务网关,旨在提供一种简单而有效的方式来路由到 API 并为它们提供交叉关注点,例如:安全性、监控/指标和弹性。 |
Sleuth/Zipkin | 提供分布式链路追踪功能,可以监控一条访问链中各个节点的状态 |
二、入门案例
以商品订单系统为例:
- 系统中包含两个服务:订单(order-service)服务和商品服务(product-service)。
- 在订单服务中会调用商品服务的接口。
- 然后在订单服务端配置调用商品服务的负载均衡和断路器。
- 最后配置一个统一的 API 网关,供客户调用。
2.1 创建父工程及公共api
1、创建父工程
创建一个普通的 Maven 项目springcoudbase
,修改 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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.hwadee.springcloud2022</groupId>
<artifactId>springcloudbase</artifactId>
<version>0.0.1-SNAPSHOT</version>
<modules>
<module>springcloud-api</module>
<module>eurekaServer8080</module>
<module>orderServer9000</module>
<module>productServer9001</module>
</modules>
<!--父工程-->
<packaging>pom</packaging>
<!--统一版本管理-->
<properties>
<java.version>1.8</java.version>
<!--和编码相关的-->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!--和springcloud相关的-->
<spring-boot.version>2.3.12.RELEASE</spring-boot.version>
<spring-cloud.version>Hoxton.SR12</spring-cloud.version>
<!--和持久层相关的-->
<mysql.version>8.0.15</mysql.version>
<druid.version>1.0.31</druid.version>
<mybatis.version>1.3.0</mybatis.version>
<!--和测试及属性注解相关的-->
<junit.version>4.12</junit.version>
<lombok.version>1.18.24</lombok.version>
<!--java版本和日志-->
<java.version>1.8</java.version>
<log4j.version>1.2.17</log4j.version>
<logback.version>1.2.3</logback.version>
</properties>
<!--继承 Spring Boot,后续子模块都是基于 sprinig boot 进行开发-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.12.RELEASE</version>
</parent>
<dependencies>
<!-- 方便创建类的gettter setter -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- 管理公共api -->
<dependency>
<groupId>com.hwadee.springcloud</groupId>
<artifactId>springcloud-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!--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 could alibaba,后续用到的 sentinel 属于 spring cloud alibaba -->
<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>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2、创建公共API
创建项目springcoudbase的公共api子模块springcloud-api
,修改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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.hwadee.springcloud2022</groupId> <artifactId>springcloudbase</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <groupId>com.hwadee.springcloud</groupId> <artifactId>springcloud-api</artifactId> <version>0.0.1-SNAPSHOT</version> <build> <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> <plugins> <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle --> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.1.0</version> </plugin> <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging --> <plugin> <artifactId>maven-resources-plugin</artifactId> <version>3.0.2</version> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.1</version> </plugin> <plugin> <artifactId>maven-jar-plugin</artifactId> <version>3.0.2</version> </plugin> <plugin> <artifactId>maven-install-plugin</artifactId> <version>2.5.2</version> </plugin> <plugin> <artifactId>maven-deploy-plugin</artifactId> <version>2.8.2</version> </plugin> <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle --> <plugin> <artifactId>maven-site-plugin</artifactId> <version>3.7.1</version> </plugin> <plugin> <artifactId>maven-project-info-reports-plugin</artifactId> <version>3.0.0</version> </plugin> </plugins> </pluginManagement> </build> </project>
3、完善父工程 pom
修改父工程 springcoudbase
的 pom 增加内容如下:
<modules> <module>springcloud-api</module> </modules>
<!--管理 spring cloud 版本--> <dependencyManagement> <dependencies> <!-- 管理公共api --> <dependency> <groupId>com.hwadee.springcloud</groupId> <artifactId>springcloud-api</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <!--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 could alibaba,后续用到的 sentinel 属于 spring cloud alibaba --> <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> </dependencies> </dependencyManagement>
4、创建实体类及打包
创建公共api中的实体类Product,并进行打包
// 商品实体类
@Data
public class Product {
private Long id;
private String name;
private BigDecimal price;
}
2.2 创建 eureka 注册中心
1、创建服务注册中心
在父工程下创建子模块:eurekaServer8080
,pom 文件引入 eureka
如下:
<?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>springcloudbase</artifactId> <groupId>com.hwadee.springcloud2022</groupId> <version>0.0.1-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <groupId>com.hwadee.springcloud</groupId> <artifactId>eurekaServer80</artifactId> <dependencies> <!--引入 eureka 服务端依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> </dependencies> <build> <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> <plugins> <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle --> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.1.0</version> </plugin> <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging --> <plugin> <artifactId>maven-resources-plugin</artifactId> <version>3.0.2</version> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.1</version> </plugin> <plugin> <artifactId>maven-jar-plugin</artifactId> <version>3.0.2</version> </plugin> <plugin> <artifactId>maven-install-plugin</artifactId> <version>2.5.2</version> </plugin> <plugin> <artifactId>maven-deploy-plugin</artifactId> <version>2.8.2</version> </plugin> <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle --> <plugin> <artifactId>maven-site-plugin</artifactId> <version>3.7.1</version> </plugin> <plugin> <artifactId>maven-project-info-reports-plugin</artifactId> <version>3.0.0</version> </plugin> </plugins> </pluginManagement> </build> </project>
2、创建eureka 服务启动类
创建eureka 服务启动类 EurekaServerApplication
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer // 启动 eureka 服务端
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
3、配置 eureka 服务端配置文件
配置 eureka 服务端配置文件 application.yml 信息:
server:
port: 8000
spring:
application:
name: eureka-server # 配置 eureka server 的服务名称
# 配置 eureka server 使用信息
eureka:
instance: # eureka server 实例主机名 localhost /自定义名字:eureka_localhost
hostname: localhost
client:
register-with-eureka: false # 是否将当前eureka server 务注册到eureka server 服务中心以供其他服务发现,默认 true
fetch-registry: false # eureka server 是否从 eureka server 服务中获取注册信息
service-url: # 配置要暴露给 eureka client 的请求的地址
defaultZone: "https://${eureka.instance.hostname}:${server.port}/eureka/"
2.3 创建 订单(order-service )服务
1、创建订单服务
创建一个子项目orderServer9000
,pom 文件中要依赖 eureka 客户端如下:
<?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>springcloudbase</artifactId>
<groupId>com.hwadee.springcloud2022</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.hwadee.springcloud</groupId>
<artifactId>orderServer9000</artifactId>
<dependencies>
<!--web依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 管理公共api -->
<dependency>
<groupId>com.hwadee.springcloud</groupId>
<artifactId>springcloud-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!--Eureka Client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
</project>
2、创建 orderServer服务的主启动类
创建 orderServer服务的主启动类 ,并使用注解 @SpringBootApplication 和@EnableEurekaClient// 启动 eureka 客户端
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient// 启动 eureka 客户端
public class OrderServerApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServerApplication.class, args);
}
}
3、配置 orderServer 配置文件
配置 orderServer 配置文件 application.yml 信息,如下:
server:
port: 9000
spring:
application:
name: order-service # 为当前订单服务命名为 order-service
# 配置eureka客户端信息
eureka:
client:
service-url:
# 配置eureka客户端服务order-service的注册地址,与 eureka-server 中暴露地址要保持一致
defaultZone: http://localhost:8000/eureka/
instance:
prefer-ip-address: true # 是否使用 IP 地址注册,默认 false
# instance-id: order-service # 实例 id,服务的唯一标识,会自动的找到order-service的ip和端口
instance-id: ${spring.cloud.client.ip-address}:${server.port} # 如果想在控制页面看到服务地址与端口,可以将 instance-id 这样配置
2.4 创建 产品(product )服务
1、 创建 产品服务
创建一个子项目:productServer9001
,修改 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">
<parent>
<artifactId>springcloudbase</artifactId>
<groupId>com.hwadee.springcloud2022</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.hwadee.springcloud</groupId>
<artifactId>productServer9001</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<!--web依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 管理公共api -->
<dependency>
<groupId>com.hwadee.springcloud</groupId>
<artifactId>springcloud-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!--Eureka Client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
</project>
2、 创建productServer服务的主启动类
创建productServer服务的主启动类,并使用注解 @SpringBootApplication 和@EnableEurekaClient// 启动 eureka 客户端
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient// 启动 eureka 客户端
public class ProductServerApplication {
public static void main(String[] args) {
SpringApplication.run(ProductServerApplication.class, args);
}
}
3、配置 productServer 配置文件
配置 productServer 配置文件 application.yml 信息:
server:
port: 9001
spring:
application:
name: product-service # 为当前商品服务命名
eureka:
client:
service-url: # 配置服务注册地址,与 eureka-server 中暴露地址保持一致
defaultZone: http://localhost:8000/eureka
instance:
prefer-ip-address: true # 是否使用 IP 地址注册,默认 false
# instance-id: product-service # 实例 id,服务的唯一标识
instance-id: ${spring.cloud.client.ip-address}:${server.port} # 如果想在控制页面看到服务地址与端口,可以将 instance-id 这样配置
lease-renewal-interval-in-seconds: 5 # 发送心跳的间隔,单位秒,默认 30
lease-expiration-duration-in-seconds: 10 # 续约到期时间,单位秒,默认90
2.5 服务注册测试
- 首先启动注册中心服务:
eurekaServer 主启动类 EurekaServerApplication
- 其次启动订单服务
order-service 主启动类 OrderServerApplication
- 最后启动商品服务
product-servie 主启动类 ProductServerApplication
启动成功后,访问注册中心地址:localhost:8000
,可以看到两个服务都注册到了注册中心:
2.7 创建远程服务调用RestTemplate
RestTemplate提供了多种便捷访问远程Http服务的方法,是一种简单便捷的访问restful服务模板类,是Spring提供的用于访问Rest服务的客户端模板工具集。相当于之前使用httpClient。简单来说就是把rest调用又封装了一层。
RestTemplate的三个参数:(url, requestMap, ResponseBean.class)
- url: REST请求地址
- requestMap:请求参数
- ResponseBean.class:HTTP响应转换被转换成的对象类型
因为消费者无需提供服务,只是去消费生产者提供的服务,因此,这个controller中不去调用service,而是通过restful接口方式远程调用提供者的controller,使用提供者的service。
在订单服务中创建 ConfigBean ,在 ConfigBean 中创建 RestTemplate 组件。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration//此类相当于spring中的容器对bean的管理生成。
public class ConfigBean {
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
2.8 完善订单服务
在订单服务中创建 OrderController ,在OrderController 中通过 RestTemplate 远程服务对象进行远程服务调用,调用的服务为商品服务。调用的方式是
* 使用restTemplate访问restfu1接口非常的简单粗暴无脑。
* (url, requestMap, ResponseBean.class)这三个参 数分别代表: REST请求地址、请求参数、HTTP响应转换被转换成的对象类型。
import com.hwadee.springcloud.entity.Product;
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;
import org.springframework.web.client.RestTemplate;
@RestController
@RequestMapping("/order")
public class OrderController {
// 订单服务要调用远端的商品服务的ip地址和端口号
private static final String REST_URL_PREFIX = "http://localhost:9001";
/**
* 使用
* 使用restTemplate访问restfu1接口非常的简单粗暴无脑。
* (url, requestMap, ResponseBean.class)这三个参 数分别代表
* REST请求地址、请求参数、HTTP响应转换被转换成的对象类型。
**/
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/buy/{id}")
public Product buy(@PathVariable Long id) {
System.out.println("进入OrderController的buy() id:"+id);
return restTemplate.getForObject(REST_URL_PREFIX + "/product/buy/"+id,Product.class);
}
}
2.9 完善商品服务
在订单服务中创建 ProductController,在ProductController 中模拟进行数据查询,并返回结果,因注解中使用 @RestController ,则可以直接返回对象。
为了方便后面的负载均衡中方便查看信息,可以获取到当前的 服务 ip 和端口。封装到Product中返回。
import com.hwadee.springcloud.entity.Product;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;
@RestController
@RequestMapping("/product")
public class ProductController {
//方便后面讲负载均衡,查看ip,此处获取配置中的端口号和ip
@Value("${server.port}")
private String port;
@Value("${spring.cloud.client.ip-address}")
private String ip;
@RequestMapping("/buy/{id}")
public Product findById(@PathVariable Long id) {
Product product = new Product();
product.setId(id);
// 后面需要测试负载均衡,所以返回 ip 地址及端口号
product.setName("当前访问服务地址:" + ip + ":" + port);
product.setPrice(new BigDecimal(10000.0));
System.out.println(product);
return product;
}
}
2.10 进行测试
- 首先启动注册中心服务:
eurekaServer 主启动类 EurekaServerApplication
- 其次启动订单服务
order-service 主启动类 OrderServerApplication
- 最后启动商品服务
product-servie 主启动类 ProductServerApplication
访问订单服务:
http://localhost:9000/order/buy/1 订单服务会自动进行调用商品服务http://localhost:9001/product/buy/1
第二章:
第四章:Ribbon负载均衡