文章目录
- 负载均衡Ribbon
- Ribbon的作用
- 代码实现
- 生产者cloud1_provider实现
- 配置文件
- 在HiController中编写以下代码
- 启动集群
- 消费者cloud1_consumer实现
- 引入依赖
- 编写配置文件
- 编写启动类,并给RestTemplate配置@LoadBalanced注解
- 编写RestController来测试
- Feign负载均衡
- 简介Feign
- Feign集成了RIbbon
- 编码测试(基于cloud1_consumer项目修改)
- 引入依赖
- 启用FeignClient
- 编写FeignService接口
- 编写Controller测试使用
- Hystrix服务熔断
- 分布式系统面临问题——服务雪崩
- 什么是Hystrix断路器
- 作用:
- 服务熔断`@HystrixCommand`
- 服务熔断与服务降级
- 服务熔断代码案例
- 引入依赖
- 启用断路器
- 编写带有短路方法的测试Controller
- 服务降级代码案例(基于之前搭建的cloud1_consumer项目修改)
- 引入依赖
- 编写yml配置
- 编写Feign接口
- 编写降级回调工厂类 OrderServiceFallBackFactory
- 编写测试Controller
- Hystrix流监控Dashboard
- 代码案例
- 生产侧,被监测方依赖配置与代码
- 监控客户端依赖配置与代码cloud1_hystrix_dashboard
负载均衡Ribbon
SpringCloudRibbon是基于NetflixRibbon实现的一套客户端负载均衡工具。主要功能是提供客户端的负载均衡算法。Ribbon客户端提供系列完整的配置,如连接超市,重试机制。简单说就是在配置文件中列出LoadBalancer后面的所有机器。Ribbon会自动帮助基于某种规则(轮询、随机)去连接机器。
Ribbon的作用
- LB(Load Balance)负载均衡,在微服务或分布式集群中常用的一种应用
- 负载均衡简单来说就是平摊用户请求到多个赴俄乌,从而达到系统的HA(高可用)
- 常见负载均衡软件Nginx、LVS
- Dubbo、SpringCloud中均给我们提供了负载均衡。SpringCloud负载均衡算法可以自定义
- 负载均衡简单分类
- 集中式Load Balance
- 即在服务端消费方和提供方之间使用独立的LB设施,如Nginx,由该服务负责把访问请求通过负载策略转发到服务提供方
- 进程式Load Balance
- 将LB的逻辑集成到消费者,消费者从服务注册中心获取到有哪些地址可用,然后自己再根据这些地址选举出一个合适的服务器。
- Ribbon就属于进程式LB,他是一个类库。集成于消费者进程,消费者通过它来获取到服务提供者的地址。
- 集中式Load Balance
代码实现
基于前面的代码来改造服务提供者的代码,先搭建服务提供者的集群
生产者cloud1_provider实现
配置文件
spring:
profiles:
active: dev-9991
---
server:
port: 9991
spring:
config:
activate:
on-profile: dev-9991
application:
name: cloud1-provider
logging:
level:
com.hx: debug
eureka:
client:
service-url:
defaultZone: http://localhost:8881/eureka/,http://localhost:8882/eureka/
instance:
instance-id: cloud-prod-1
# 配置主机地址
hostname: localhost
---
server:
port: 9992
spring:
config:
activate:
on-profile: dev-9992
application:
name: cloud1-provider
logging:
level:
com.hx: debug
eureka:
client:
service-url:
defaultZone: http://localhost:8881/eureka/,http://localhost:8882/eureka/
instance:
instance-id: cloud-prod-2
# 配置主机地址
hostname: localhost
在HiController中编写以下代码
@Value("${server.port}")
private String port;
@RequestMapping("/")
public String hi() {
return "<h1>HI Spring Cloud 1 " + port + "</h1>";
}
启动集群
消费者cloud1_consumer实现
引入依赖
注意:这里不需要重复引入ribbon的依赖,否则无法ribbon将获取不到实例
<?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>cloud1_parent</artifactId>
<groupId>com.hx</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud1_consumer</artifactId>
<name>cloud1_consumer</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.hx</groupId>
<artifactId>cloud1_api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
编写配置文件
server:
port: 7771
spring:
application:
name: cloud1-consumer
logging:
level:
com.hx: debug
eureka:
client:
service-url:
defaultZone: http://localhost:8881/eureka/,http://localhost:8882/eureka/
register-with-eureka: false # 消费者不需要注册自己
instance:
instance-id: cloud-prod-1
# 配置主机地址
hostname: localhost
编写启动类,并给RestTemplate配置@LoadBalanced注解
@SpringBootApplication
@EnableEurekaClient
public class Cloud1ConsumerApp {
public static void main(String[] args) {
SpringApplication.run(Cloud1ConsumerApp.class,args);
}
// 配置负载均衡的restTemplate
@Bean
@LoadBalanced // ribbon
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
编写RestController来测试
@RestController
public class RestOrderController {
@Autowired
private RestTemplate restTemplate;
private static final String ORDER_APP_URI = "http://CLOUD1-PROVIDER";
@RequestMapping("/")
public String index() {
return restTemplate.getForObject(ORDER_APP_URI.concat("/"), String.class);
}
@RequestMapping("/restOrder")
public Order getOrder() {
return restTemplate.getForObject(ORDER_APP_URI.concat("/getOrder"), Order.class);
}
}
Feign负载均衡
简介Feign
Feign是声明式的web service客户端。让微服务之间的调用更简单,类似controller调用service。SpringCloud继承了Ribbon和Eureka,可以在使用Feign时提供负载均衡的http客户端。Feign旨在使得编写JavaHttp客户端简单容易。
只需要给一个接口,然后添加注解就可以。
Feign集成了RIbbon
利用Ribbon维护了微服务列表信息,通过轮询实现了客户端的负载均衡,而与Ribbon不同的是,通过Feign只需要定义服务绑定接口,且以声明式的方法,优雅简单的实现服务调用。
编码测试(基于cloud1_consumer项目修改)
引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
启用FeignClient
@SpringBootApplication
@EnableEurekaClient
//@EnableFeignClients
@EnableFeignClients(basePackages = "com.hx.service")
public class Cloud1ConsumerApp {
public static void main(String[] args) {
SpringApplication.run(Cloud1ConsumerApp.class, args);
}
// 配置负载均衡的restTemplate
@Bean
@LoadBalanced // ribbon
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
编写FeignService接口
@Service
@FeignClient(value = "CLOUD1-PROVIDER")
public interface RestOrderService {
@GetMapping("/")
String index();
@PostMapping("/getOrder")
Order restGetOrder();
}
编写Controller测试使用
@RestController
@RequestMapping("/fg")
public class RestOrderFeignController {
@Autowired
private RestOrderService restOrderService;
@RequestMapping("/")
public String index() {
return restOrderService.index();
}
@RequestMapping("/restOrder")
public Order getOrder() {
return restOrderService.restGetOrder();
}
}
Hystrix服务熔断
分布式系统面临问题——服务雪崩
复杂分布式体系结构中应用程序有数十个依赖关系,每个依赖关系在某些时刻不可避免的失败。
多个微服务之间调用的时候,假设A服务调用B、C服务,微服务B和C又调用其他微服务,这就是所谓的"扇出"。如果扇出的链路上某个微服务的调用响应超市或时间过长无响应,对微服务A的调用就会占用越来越多的系统资源,进而引发系统崩溃,即服务雪崩。
对于高流量应用,单一的后端依赖可能导致所有服务上的所有资源都在几十秒内饱和。更糟糕的是这些应用还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联腹胀。这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不取消整个应用程序系统。
什么是Hystrix断路器
Hystrix是一个应用于处理分布式系统的延迟和容错开源库,在分布式系统中,许多依赖不可避免的调用失败,eg:超时、异常等。Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,提高分布式系统的弹性。
断路器本身是一种开关,当某个服务单元发生故障后,通过断路器的故障监控,向服务调用方返回一个服务预期的、可处理的备选响应,而不是长时间的等待或者抛出无法处理的异常,这样保证了服务调用方的线程不会被长时间占用。从而避免故障在分布式系统中的蔓延乃至雪崩。
作用:
服务降级、服务熔断、服务限流、接近实时监控
服务熔断@HystrixCommand
熔断机制是应对雪崩问题的一种微服务链路保护机制。
当扇出的某个微服务不可用或者响应时间太长,会进行服务降级,进而熔断该节点微服务调用,快速返回错误的相应细腻些。当检测到该服务响应正常后,恢复链路调用。在SpringCloud熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到达一定阈值,缺省5s内20次失败就会启动熔断机制。
服务熔断与服务降级
服务熔断(服务端编写):某个服务超时或者异常,引起熔断
服务降级(客户端编写):从整体的网站请求负载考虑,当某个服务熔断或者关闭后服务将不再被调用。此时在客户端可以准备一个失败回调FallbackFactory。返回一个默认的缺省值,整体服务水平下降。
服务熔断代码案例
引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
启用断路器
@SpringBootApplication
@EnableDiscoveryClient // 服务发现,不只是eureka
//@EnableCircuitBreaker // 启用断路器Hystrix
@EnableHystrix
public class Cloud1ProdApp {
public static void main(String[] args) {
SpringApplication.run(Cloud1ProdApp.class);
}
}
编写带有短路方法的测试Controller
@RestController
@RequestMapping("hystrix")
public class Hi_HystrixController {
@Autowired
private OrderService orderService;
@HystrixCommand(fallbackMethod = "getOrderHystrix")
@RequestMapping("/order")
public Order getOrder(String id) {
if(StringUtils.isBlank(id)){
throw new RuntimeException("参数ID不可以为空!");
}
return orderService.getOrder(id);
}
/**
* 注意:这个熔断的方法,里的参数列表要和上面的方法参数列表相同
* 否则报错:com.netflix.hystrix.contrib.javanica.exception.FallbackDefinitionException: fallback method wasn't found
*/
public Order getOrderHystrix(String id) {
id = UUID.randomUUID().toString();
return orderService.getOrder(id).setId("请传入正确的ID");
}
}
服务降级代码案例(基于之前搭建的cloud1_consumer项目修改)
引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
编写yml配置
feign:
hystrix:
enable: true
编写Feign接口
@Service
@FeignClient(value = "CLOUD1-PROVIDER",fallbackFactory = OrderServiceFallBackFactory.class)
public interface RestOrderService {
@GetMapping("/")
String index();
@PostMapping("/getOrder")
Order restGetOrder();
// 我们这次测试的接口
@PostMapping("/hystrix/order")
Order restGetOrderFall();
}
编写降级回调工厂类 OrderServiceFallBackFactory
@Component
public class OrderServiceFallBackFactory implements FallbackFactory<RestOrderService> {
@Override
public RestOrderService create(Throwable cause) {
return new RestOrderService() {
@Override
public String index() {
return "降级";
}
@Override
public Order restGetOrder() {
return null;
}
@Override
public Order restGetOrderFall() {
return new Order().setId("没有找到,可能是未传入ID")
.setCreateTime(SimpleDateFormat.getDateTimeInstance().format(new Date()));
}
};
}
}
编写测试Controller
@RestController
@RequestMapping("/fgfall")
public class RestOrderFeignController2 {
@Autowired
private RestOrderService restOrderService;
@RequestMapping("/order2")
public Order getOrder() {
return restOrderService.restGetOrderFall();
}
}
注意:这里高版本的springcloud2021版本不清楚为什么无法实现
Hystrix流监控Dashboard
代码案例
生产侧,被监测方依赖配置与代码
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
#暴露全部的监控信息
management:
endpoints:
web:
exposure:
include: "*"
@Bean
public ServletRegistrationBean hystrixMetricsStreamServlet(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/actuator/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
监控客户端依赖配置与代码cloud1_hystrix_dashboard
<?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>cloud1_parent</artifactId>
<groupId>com.hx</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud1_hystrix_dashboard</artifactId>
<name>cloud1_hystrix_dashboard</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<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-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
</dependencies>
</project>
package com.hx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@SpringBootApplication
// 启用hystrix监控
@EnableHystrixDashboard
@EnableHystrix
public class DashboardApp {
public static void main(String[] args) {
SpringApplication.run(DashboardApp.class,args);
}
}