SpringCloud(1)
1 什么是SpringCloud
分布式架构的一键式解决方案
Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。
2 SpringBoot与SpringCloud的版本依赖关系
https://start.spring.io/actuator/info (官方指定的版本之间的依赖,避免冲突)
{
"spring-cloud":{
"Hoxton.SR11":"Spring Boot >=2.2.0.RELEASE and <2.3.11.BUILD-SNAPSHOT",
"Hoxton.BUILD-SNAPSHOT":"Spring Boot >=2.3.11.BUILD-SNAPSHOT and <2.4.0.M1",
"2020.0.0-M3":"Spring Boot >=2.4.0.M1 and <=2.4.0.M1",
"2020.0.0-M4":"Spring Boot >=2.4.0.M2 and <=2.4.0-M3",
"2020.0.0":"Spring Boot >=2.4.0.M4 and <=2.4.0",
"2020.0.2":"Spring Boot >=2.4.1 and <2.5.0-M1",
"2020.0.3-SNAPSHOT":"Spring Boot >=2.4.6-SNAPSHOT"
},
"spring-cloud-alibaba":{
"2.2.1.RELEASE":"Spring Boot >=2.2.0.RELEASE and <2.3.0.M1"
}
}
本次springcloud项目使用的是 springboot 2.2.2 **===========**springcloud Hoxton.SR1
3 父工程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>
<groupId>com.springcloud</groupId>
<artifactId>cloud2021</artifactId>
<version>1.0-SNAPSHOT</version>
<!--父工程的作用是用作于一个统一的依赖版本管理,给子项目的依赖传递-->
<packaging>pom</packaging>
<!-- 统一管理jar包版本-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.sourse>1.8</maven.compiler.sourse>
<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>
<mysql.version>8.0.2</mysql.version>
<druid.version>1.1.16</druid.version>
<mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
</properties>
<!---子模块继承之后,提供作用:锁定版本+子module不用写groupId和version,聚合管理,统一生效-->
<!--子项目依赖不声明版本则使用父工程的依赖版本,若子项目中需要使用别的版本,那么可以额外申明单独的版本 -->
<!--dependencyManagement只是申明依赖,并不实现引入,因此子项目需要显示的申明使用的依赖及版本-->
<dependencyManagement>
<dependencies>
<!--springboot 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>
<!--springcloud Hoxton.SR1-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--springcloud alibaba 2.1.0-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
<scope>runtime</scope>
</dependency>
<!-- druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.spring.boot.version}</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<!--热部署需要安装的插件-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
<addResources>true</addResources>
</configuration>
</plugin>
</plugins>
</build>
</project>
父工程创建完成执行mvn:install将父工程发布到仓库方便子工程继承
4 Eureka(服务发现)
服务发现是微服务架构中的一项核心服务。如果没有该服务,我们就只能为每一个服务调用者手工配置可用服务的地址,这不仅繁琐而且非常容易出错。Eureka包括了服务端Server 和客户端Client两部分。服务端可以做到高可用集群部署,每一个节点可以自动同步,有相同的服务注册信息。
4.1Eureka 单机
4.1.1 构建Server ,提供服务发现的服务端
1️⃣ 构建modual
2️⃣ 导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
3️⃣ yml配置文件
server:
port: 7001
eureka:
instance:
hostname: localhost
client:
# 标明自己是注册中心,不需要检索自己服务
fetch-registry: false
# 标明eureka不需要将自己的服务注册到注册中心上
register-with-eureka: false
service-url:
# 设置与eureka server交互的地址 (查询服务和祖册服务都需要这个地址)
defaultZone: http://localhost:7001/eureka/
4️⃣启动Eureka
/**
* @EnableEurekaServer 注解标明当前类是一个eureka的服务类
*/
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerMain {
public static void main(String[] args) {
SpringApplication.run(EurekaServerMain.class,args);
}
}
4.1.2 构建Client,注册服务与发现服务
1️⃣ 构建modual
2️⃣ 导入依赖
<!--eureka客戶端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
3️⃣ yml配置文件
#端口
server:
port: 8001
#服务名称
spring:
application:
name: payment-service
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://localhost:3306/redis?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8
#mybatis数据库配置
mybatis:
# 指定Mapper存放的静态路径
mapper-locations: classpath:mapper/*.xml
# entity的类名映射包
type-aliases-package: com.pojo
logging:
level:
com.mapper: debug
eureka:
client:
service-url:
# 设置与eureka server交互的地址 (查询服务和祖册服务都需要这个地址)
# defaultZone: http://localhost:7001/eureka(单机注册)
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka(集群注册)
fetch-registry: true
register-with-eureka: true
/**
* EnableEurekaClient 标识当前类服务是eureka的客户端,需要将服务注册到注册中心上
*/
@SpringBootApplication
@EnableEurekaClient
public class PaymentMain {
public static void main(String[] args) {
SpringApplication.run(PaymentMain.class,args);
}
}
4.2 Eureka集群
为了避免出现单机故障,服务注册的方式需要采用集群的方式,提高可用性
与单机注册不同的一点。集群服务的注册是互相注册相互守望。为了避免注册中心的服务实例一致,需要在同一台服务器上采用域名的方式来区分,因此需要修改
C:\Windows\System32\drivers\etc 路径下的hosts配置文件
#######SpringCloud#############
#eureka7001.com 7001端口的域名
127.0.0.1 eureka7001.com
#eureka7002.com 7002端口的域名
127.0.0.1 eureka7002.com
1️⃣ eureka7001服务端的配置文件
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com
client:
# 标明自己是注册中心,不需要检索自己服务
fetch-registry: false
# 标明eureka不需要将自己的服务注册到注册中心上
register-with-eureka: false
service-url:
# 设置与eureka server交互的地址 (查询服务和祖册服务都需要这个地址)
defaultZone: http://eureka7002.com:7002/eureka/
1️⃣ eureka7002服务端的配置文件
server:
port: 7002
eureka:
instance:
hostname: eureka7002.com
client:
# 标明自己是注册中心,不需要检索自己服务
fetch-registry: false
# 标明eureka不需要将自己的服务注册到注册中心上
register-with-eureka: false
service-url:
# 设置与eureka server交互的地址 (查询服务和祖册服务都需要这个地址)
defaultZone: http://eureka7001.com:7001/eureka/
#eureka集群的搭建需要相互祖冊 相互守望
4.3 Eureka提供服务端集群
4.4 服务发现
eureka客户端不仅仅能提供服务注册的功能,还能进行服务发现,获得注册中心上注册服务的供能
4.5 Eureka总结
📑
1:Eureka是为了解决微服务架构中多个微服务之间的服务治理和服务发现的组件之一。
2:Eureka中分为3个角色
角色 | 角色功能 |
---|---|
注册中心 | 提供服务发现的功能,多个微服务启动之后,会向Eureka Server注册自己的信息 |
服务提供者 | EurekaClient端,服务启动后,注册自己的信息;通过心跳等机制保持与Server端的通信,实现服务的”注销“或者”续约“ |
服务消费者 | EurekaClient端,服务启动后,注册自己的信息;缓存EurekaServer上的注册表的信息,找到服务的提供者,实现Remote Call(远程调用) |
3:Eureka Server同时也是EurekaClient。多个Zone中的Eureka单机实例通过之间通过Replicate【复制】的方式来实现服务注册表之间的同步。Eureka集群的方式避免了Eureka单点故障的可能性。
5.Ribbon(负载均衡)
5.1 Ribbon负载均衡算法的替换
Ribbon默认的负载均衡算法是轮询算法(rest接口第几次请求数%服务群总数量=实际调用服务器位置下标,每次服务器启动重启后rest接口计数从1开始)
可以自定义服务调用时的轮询算法
1️⃣ 自定义配置类
/**
* * 这个自定义配置类不能放在@Component注解所扫描的包下以及子包下,
* * 否则我们自定义的配置类就会被所有的Ribbon客户端所共享,达不到特殊化定制化的目的
*/
@Configuration
public class MyIrule {
/**
* 配置类,采用随机的方式进行一个负载均衡
* @return
*/
@Bean
public IRule getMyRule(){
return new RandomRule();
}
}
2️⃣指定服务开启自定义算法规则
/**
* 标明了当消费者去消费PAYMENT_SERVICE时,负载均衡采用随机的算法
*/
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "PAYMENT-SERVICE",configuration = MyIrule.class)
public class OrderMain {
public static void main(String[] args) {
SpringApplication.run(OrderMain.class,args);
}
}
6 OpenFeign(服务调用)
OpenFeign
是一个声明式的web服务客户端
,让编写web服务客户端变的非常容易,只需要创建一个接口并在接口上添加注解即可。
OpenFeign是Spring Cloud在Feign的基础上支持了Spring MVC的注解,列如@RequestMapping
等,OpenFeign的@FeignClient
可以解析SpringMVC的@RequestMapping
注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。
6.1OpenFeign的服务调用
1️⃣ 构建Webservice类
@Component
@FeignClient("PAYMENT-SERVICE")
public interface Feign_PaymentService {
@GetMapping("/getPaymentById/{id}")
ReturnMessageModel<Payment> getPaymentById(@PathVariable("id") Long id);
}
2️⃣激活OpenFeign,调用服务
@SpringBootApplication
@EnableFeignClients
public class OpenFeignOrderMain {
public static void main(String[] args) {
SpringApplication.run(OpenFeignOrderMain.class,args);
}
}
3️⃣测试类
@RestController
public class OpenFeignController {
@Autowired
private Feign_PaymentService service;
@GetMapping("cosumer/feign/getPaymentByid/{id}")
public ReturnMessageModel<Payment>getPaymentByid(@PathVariable("id") Long id){
ReturnMessageModel<Payment> paymentById = service.getPaymentById(id);
return paymentById;
}
}
6.2OpenFeign的超时控制
用open feign的方式调用服务时,默认是1ms的时间内获得请求结果,否则会出现客户端调用服务时超时
1️⃣模拟服务端的长流程服务执行,超过默认的1Ms钟
payment8001 服务
@GetMapping("/feignTimeOut")
public String feignTimeOut() {
//模拟服务端执行长时间服务的情形
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
return port;
}
2️⃣ open feign 接口的方式接入请求
/**
* FeignClient客戶端请求的服务名称
*/
@Component
@FeignClient("PAYMENT-SERVICE")
public interface Feign_PaymentService {
@GetMapping("/getPaymentById/{id}")
ReturnMessageModel<Payment> getPaymentById(@PathVariable("id") Long id);
@GetMapping("/feignTimeOut")
String feignTimeOut();
}
3️⃣ 测试
/**
* 用open feign的方式调用服务时,默认是1ms的时间内获得请求结果,否则会出现客户端调用服务时超时
* @return
*/
@GetMapping("/consumer/feign/getFeignTimeOut")
public String getFeignTimeOut(){
return service.feignTimeOut();
}
🎫 为了避免上述情形,并且合理的拿到请求结果,需要设置feign客户端的超时控制,yml配置文件中设置
#设置feign客户端超时时间(open feign默认支持ribbon)
ribbon:
#建立连接后从服务器读取到可用资源所用的时间
ReadTimeout: 5000
# 指的是建立连接的所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
ConnectTimeout: 500
6.3 OpenFeign的日志增强
Feign提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解Feign中Http请求的细节。说白了就是对Feign接口调用情况的监控和输出
1️⃣ 添加配置文件
@Configuration
public class FeignLevelConfig {
@Bean
Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
2️⃣ 修改yml配置
logging:
level:
#feign日志以什么级别监控哪个接口
com.service.Feign_PaymentService: debug