什么是Spring Cloud
SpringCloud 是在Spring生态环境下,用于构建可靠微服务应用的一系列组件的统称,提供的功能包括但不限于服务治理,接口式Http调用,负载均衡,服务降级以及熔断,数据健康,网关,配置中心,消息总线等。
SpringCloud中常用组件及作用
- Eureka:云端服务发现,一个基于 REST 的服务,用于定位服务,以实现云端中间层服务发现和故障转移。
- OpenFeign:Feign是一种声明式、模板化的HTTP客户端
- Ribbon:提供云端负载均衡,有多种负载均衡策略可供选择,可配合服务发现和断路器使用
- Hystrix:熔断器,容错管理工具,旨在通过熔断机制控制服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力
- Spring Cloud Config:配置管理工具包,让你可以把配置放到远程服务器,集中化管理集群配置,目前支持本地存储、Git以及Subversion
- Zuul:Zuul 是在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门
- Spring Cloud Bus:事件、消息总线,用于在集群(例如,配置变化事件)中传播状态变化,可与Spring Cloud Config联合实现热部署。
- Consul:封装了Consul操作,consul是一个服务发现与配置工具,与Docker容器可以无缝集成
Maven环境初步搭建
主项目的依赖引入
<?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>
<!--引入Springboot-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
</parent>
<groupId>org.example</groupId>
<artifactId>springcloudtest</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<!--对子项目进行管理-->
<modules>
<module>provider-8100</module>
<module>provider-8101</module>
<module>provider-8102</module>
<module>consumer-8200</module>
<module>consumer-8201</module>
<module>eureka-server-8300</module>
<module>eureka-server-8301</module>
<module>springcloud-hystrix-dashboard-8400</module>
</modules>
<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.RELEASE</spring-cloud.version>
<log4j.version>1.2.17</log4j.version>
</properties>
<dependencyManagement>
<!--对springcloud的版本进行统一管理-->
<dependencies>
<!--spring cloud Hoxton.SR1-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--spring cloud 阿里巴巴-->
<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>
<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>
Eruaka的使用
Maven引入
<!--eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!--spring boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
配置文件
server:
port: 8300
spring:
application:
name: springcloud-eureka-server-8300
eureka:
instance:
## 此处需要修改DNS 将该域名映射到本机上。不直接用localhost,一个实例只能被一个域名使用
hostname: server1.com
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/,http://server2.com:8301/eureka/
启动两个Eureka服务器实例
@SpringBootApplication
@EnableEurekaServer
public class EurekaServer8300 {
public static void main(String[] args){
SpringApplication.run(EurekaServer8300.class, args);
}
}
创建Provider
<?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>springcloudtest</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>provider-8100</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
spring:
application:
name: provider
server:
port: 8100
eureka:
instance:
instance-id: ${spring.application.name}:${server.port}
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://server1.com:8300/eureka/,http://server2.com:8301/eureka/
#Eureka地址
@SpringBootApplication
@EnableDiscoveryClient
public class Provider {
public static void main(String[] args) {
SpringApplication.run(Provider.class, args);
}
}
因此我们就完成了,eureka服务端的集群与客户端的注册
Openfeign的使用
OpenFeign是一个在集成了Ribbon的声明式,模版化客户端,我们先注册三个provider到EruekaServer中
接下来我们再启动一个接入OpenFeign的客户端:消费者
加入maven依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
给RestTemplate加上负载均衡注解
@Configuration
public class RestConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
使用@FeignClient定义http客户端
@Component
@FeignClient(name = "provider", fallback = ProductFallbackServieImpl.class)
public interface ProductService {
@GetMapping("/product/provider/serviceInfo")
String getServiceInfo();
@GetMapping("/product/provider" + "/getById?id={id}")
String selectById(@PathVariable("id") Long id);
}
定义失败回掉类用于服务降级
@Component
public class ProductFallbackServieImpl implements ProductService{
@Override
public String getServiceInfo() {
return "服务器开小差了";
}
@Override
public String selectById(Long id) {
return "服务器开小差了";
}
}
最后在启动类上加上@EnableFeignClients注解,然后启动
访问provider接口返回对应的端口信息
如图可以知道,消费者端请求到了不同端口的服务提供者。
Hystrix的使用
引入maven依赖
<!--hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
服务降级
在微服务中,为避免下游服务异常,导致上游服务异常或资源占用,通常采取特殊处理,来保证服务的正常进行,比如我们在订单服务调用库存服务发生异常时,通常不直接返回异常给用户,而是采用降级函数,替代原函数返回内容,例如上一块内容使用的异常处理函数。
在配置文件上加上:
feign:
hystrix:
enabled: true # fegin默认关闭hystrix服务
并在启动类上加上 @EnableCircuitBreaker注解。
我们访问provider一个异常接口,可以看到最终返回了降级函数的内容。
服务熔断
服务熔断发生在服务提供者,当某个接口产生了一定的异常后,将该接口的调用关闭,断路器打开,使用降级函数,一段时间后重新尝试半打开状态,就是尝试放进一些流量,如果正常,则将断路器关闭,避免不必要的资源占用。
@HystrixCommand(
// 降级函数
fallbackMethod = "selectByIdFallbackHandler", commandProperties = {
// 是否启用服务熔断
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
// 请求阈值
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
// 时间窗口
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),
// 错误比率
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50")
})
表示请求次数大于10次,且错误比率占50%以上时,打开断路器,10秒后尝试半开状态。
服务端发生的降级函数为 我失败了啊啊啊
当发生熔断之后,直接调用降级函数
Hystrix- dashboard的使用
应用暴露运行信息
Hystrix-dashboard是Hystrix-dashboard的健康仪表盘,要想被监控,我们首先需要引入一个监控信息依赖
<!-- actuator监控信息完善 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
然后添加信息暴露配置
适用于springboot 2.x以上
management:
endpoints:
web:
exposure:
include: "*"
在应用启动时,我们需要看到一行,表示成功。
2021-09-12 23:45:25.181 INFO 38634 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 17 endpoint(s) beneath base path '/actuator'
新建Hystrix Dashboard项目
添加maven依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
启动springboot应用程序
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboard8400 {
public static void main(String[] args){
SpringApplication.run(HystrixDashboard8400.class, args);
}
}
访问HystrixDashboard
如果点击进去一直在loading,需要访问一次添加@HystrixCommand注解的接口,才会开始显示,
如图当失败过多时,断路器开启
当断路器打开时,即使接口参数正确,也直接返回降级函数的内容