Spring Cloud 进阶--Rest微服务加入Hystrix断路器实现服务熔断与服务降级

版权声明:本文为博主原创文章,如果觉得写的不错需要转载,在转载时请注明博文出处! https://blog.csdn.net/Hello_World_QWP/article/details/88087407

                                 《 Hystrix断路器,实现服务的熔断与降级 》

前言

在上一篇文章中,主要完成了 《  Rest微服务加入Feign负载均衡客户端组件 》,并且完成了 《 Hystrix 基本理论概述 》的详细简介,本篇博客将带领读者完整在 Rest 微服务中集成 Hystrix 实现服务的熔断(服务端)与服务的降级(客户端),本篇博客主要主要涉及模块,包括:

  • 新增基于 Hystrix 的服务消费者模块,服务名称为 “ microservice-hystrix-8001 ”;
  • 修改整体微服务项目的公共子模块,供其它子模块引用,达到通用的目的,项目名为 “ microservice-api ”;
  • 新增支持 Feign 的服务消费者模块,服务名称为 “ microservice-feign-80 ”;

Hystrix断路器,实现服务的熔断与降级

1、Hystrix 服务熔断

新增 hystrix 服务提供者模块,参考一号服务提供者模块服务器 “ microservice-provider-8001 ” 新建模块 “ microservice-hystrix-8001 ” 模块,创建完成后,如下图:

POM 内容修改,新增对 Hystrix 的支持,完整内容如下:

<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.huazai.springcloud</groupId>
		<artifactId>microservice</artifactId>
		<version>${project.version}</version>
	</parent>

	<artifactId>microservice-hystrix-8001</artifactId>
	<description>服务提供者模块(包括Hystrix延迟和容错的开源库)-8001</description>

	<dependencies>
		<!-- 引入自己定义的api通用包,可以使用Dept部门Entity -->
		<dependency>
			<groupId>com.huazai.springcloud</groupId>
			<artifactId>microservice-api</artifactId>
			<version>${project.version}</version>
		</dependency>
		<!-- 单元测试相关 -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
		</dependency>
		<!-- 数据库相关 -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
		</dependency>
		<!-- 日志相关 -->
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-core</artifactId>
		</dependency>
		<!-- Spring Boot 相关 -->
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jetty</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-test</artifactId>
		</dependency>
		<!-- Eureka 相关 -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-config</artifactId>
		</dependency>
		<!-- Eureka 完善 actuator 信息相关 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka</artifactId>
		</dependency>
		<!-- Ribbon相关 -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-ribbon</artifactId>
		</dependency>
		<!-- hystrix相关 -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-hystrix</artifactId>
		</dependency>
		<!-- 修改后立即生效,热部署 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>springloaded</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
		</dependency>
	</dependencies>

</project>

yml 内容修改,将服务的实例ID修改为 “ microservice-hystrix-8001 ”,主要目的为体现出当前项目支持 Hystrix 的服务提供者,当然还可根据需求修改为其它,完整内容如下:

server:
  port: 8001
  
mybatis:
  config-location: classpath:mybatis/mybatis.cfg.xml        # mybatis配置文件所在路径
  type-aliases-package: com.huazai.springcloud.entity    # 所有Entity别名类所在包
  mapper-locations:
  - classpath:mybatis/mapper/**/*.xml                       # mapper映射文件
    
spring:
   application:
    name: microservice-provider # 该模块的服务提供者的应用名称必须一致
   datasource:
    type: com.alibaba.druid.pool.DruidDataSource            # 当前数据源操作类型
    driver-class-name: org.gjt.mm.mysql.Driver              # mysql驱动包
    url: jdbc:mysql://***.***.***.***:*****/microservice-01              # 数据库名称
    username: *****
    password: ***********
    dbcp2:
      min-idle: 5                                           # 数据库连接池的最小维持连接数
      initial-size: 5                                       # 初始化连接数
      max-total: 5                                          # 最大连接数
      max-wait-millis: 200                                  # 等待连接获取的最大超时时间
      
eureka:
  client: #客户端注册进eureka服务列表内
    service-url: 
      defaultZone: http://www.eureka7001.com:7001/eureka/,http://www.eureka7002.com:7002/eureka/,http://www.eureka7003.com:7003/eureka/
       
  instance:
    instance-id: microservice-hystrix-8001
    prefer-ip-address: true

info:
  app.name: learning-microservice-springcloud
  company.name: huazai-studio
  build.artifactId: $project.artifactId$
  build.version: $project.version$
  
 
 

修改 Controller 类

新增注解 “ @HystrixCommand ” ,其目的为:在基于微服务架构风格的业务系统中,当服务消费者模块对服务提供者模块调用失败时,不会直接卡死或者直接抛出一大坨异常,而是自动调用使用 “ @HystrixCommand ” 注解标注的指定的目标方法,返回指定的什么内容,完整内容如下:

package com.huazai.springcloud.controller;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.huazai.springcloud.entity.Department;
import com.huazai.springcloud.service.DepartmentService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;

@RestController
@RequestMapping(value = "/department")
@SuppressWarnings("unused")
public class DepartmentController
{

	@Autowired
	private DepartmentService departmentService;

	@RequestMapping(value = "/add", method = RequestMethod.POST)
	@HystrixCommand(fallbackMethod = "processHystrix_add")
	public boolean add(@RequestBody Department department)
	{
		boolean result = departmentService.add(department);
		if (!result)
		{
			throw new RuntimeException(department.toString() + "/n---添加异常");
		}
		return result;
	}

	private boolean processHystrix_add(@RequestBody Department department)
	{
		return false;
	}

	@RequestMapping(value = "/delete/{id}", method = RequestMethod.DELETE)
	@HystrixCommand(fallbackMethod = "processHystrix_delete")
	public void delete(@PathVariable Long id)
	{
		departmentService.delete(id);
	}

	private void processHystrix_delete(@PathVariable Long id)
	{
		System.out.println(id + "删除异常");
	}

	@RequestMapping(value = "/update", method = RequestMethod.PUT)
	@HystrixCommand(fallbackMethod = "processHystrix_update")
	public void update(@RequestBody Department department)
	{
		departmentService.update(department);
	}

	private void processHystrix_update(@RequestBody Department department)
	{
		System.out.println(department.toString());
	}

	@RequestMapping(value = "/get/{id}", method = RequestMethod.GET)
	@HystrixCommand(fallbackMethod = "processHystrix_get")
	public Department get(@PathVariable Long id)
	{
		Department department = departmentService.get(id);
		if (department == null)
		{
			throw new RuntimeException(id + "号的部门信息不存在");
		}
		return department;
	}

	private Department processHystrix_get(@PathVariable Long id)
	{
		// Department department = new Department(id, id + "号的部门信息不存在", "not this data in the MySQL");
		Department department = new Department(id + "号的部门信息不存在");
		return department;
	}

	@RequestMapping(value = "/list")
	@HystrixCommand(fallbackMethod = "processHystrix_list")
	public List<Department> list()
	{
		List<Department> departments = departmentService.list();
		if (departments.size() == 0)
		{
			throw new RuntimeException("部门信息获取异常");
		}
		return departments;
	}

	private List<Department> processHystrix_list()
	{
		List<Department> departments = new ArrayList<>();
		// Department department = new Department(id, id + "号的部门信息不存在", "not this data
		// in the MySQL");
		Department department = new Department("部门信息异常,无部门信息");
		departments.add(department);

		return departments;
	}

}

 

修改启动类,新增注解 “ @EnableCircuitBreaker ” ,表示开启对hystrixR熔断机制的支持,完整内容如下:

package com.huazai.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

/**
 * 
 * @author HuaZai
 * @contact [email protected]
 *          <ul>
 * @description
 *              <li>Hystrix 熔断器
 *              </ul>
 * @className MicroserviceHystrixApp_8001
 * @package com.huazai.springcloud
 * @createdTime 2018年05月06日 下午2:21:44
 *
 * @version V1.0.0
 */
@SpringBootApplication
@EnableEurekaClient // 本服务启动后会自动注册进eureka服务中
@EnableCircuitBreaker // 开启对hystrixR熔断机制的支持
public class MicroserviceHystrixApp_8001
{

	public static void main(String[] args)
	{
		SpringApplication.run(MicroserviceHystrixApp_8001.class, args);
	}
}

测试基于 Hystrix 的服务熔断机制

先启动 Eureka 集群,再启动基于 Hystrix 的服务提供者,并注册进 Eureka 注册中心,如下图:

启动消费在服务模块 “ microservice-consumer-80 ” ,并访问消费者服务器地址,当访问的数据存在时则正常显示,如下图:

当访问的数据不存在或者出现异常时,会立即返回自定义的信息,不会出现访问延迟的显现,页面访问异常的现象,如下图:

2、Hystrix 服务降级

服务降级处理是在客户端完成的,与服务端是不存在任何关系的,所以需要在公共子模块 “ microservice-api ” 中新增一个 Factory 类 “ DepartmentClientServiceFallbackFactory.java ” ,实现 “ FallbackFactory ” ,并重写 FallbackFactory 的 “ create ” 方法,当然里面的方法就需要根据实际的需求来自定义了,本次的完整类容如下:

package com.huazai.springcloud.factory;

import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Component;

import com.huazai.springcloud.entity.Department;
import com.huazai.springcloud.service.DepartmentClientService;

import feign.hystrix.FallbackFactory;

/**
 * 
 * @author HuaZai
 * @contact [email protected]
 *          <ul>
 * @description TODO
 *              </ul>
 * 
 * @className DepartmentClientServiceFallbackFactory
 * @package com.huazai.springcloud.factory
 * @createdTime 2018年05月14日 下午7:48:16
 *
 * @version V1.0.0
 */
@Component
public class DepartmentClientServiceFallbackFactory implements FallbackFactory<DepartmentClientService>
{

	@Override
	public DepartmentClientService create(Throwable cause)
	{
		return new DepartmentClientService()
		{

			@Override
			public void update(Department department)
			{
				// TODO Auto-generated method stub

			}

			@Override
			public List<Department> list()
			{
				List<Department> departments = new ArrayList<>();
				Department department = new Department("部门信息异常,无部门信息");
				departments.add(department);
				return departments;
			}

			@Override
			public Department get(Long id)
			{
				Department department = new Department(id + "号的部门信息不存在");
				return department;
			}

			@Override
			public void delete(Long id)
			{
				// TODO Auto-generated method stub

			}

			@Override
			public boolean add(Department department)
			{
				// TODO Auto-generated method stub
				return false;
			}
		};
	}

}

在接口类 “ DepartmentClientService ” 中的 “ @FeignClient ” 注解中新增 fallbackFactory 属性,并指向方才新增的 “ DepartmentClientServiceFallbackFactory ” 工厂类,完整内容如下:

package com.huazai.springcloud.service;

import java.util.List;

import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.huazai.springcloud.entity.Department;
import com.huazai.springcloud.factory.DepartmentClientServiceFallbackFactory;

/**
 * 
 * @author HuaZai
 * @contact [email protected]
 *          <ul>
 * @description TODO
 *              </ul>
 * @className DepartmentClientService
 * @package com.huazai.springcloud.service
 * @createdTime 2018年05月13日 下午8:21:01
 *
 * @version V1.0.0
 */
@FeignClient(value = "MICROSERVICE-PROVIDER/department", fallbackFactory = DepartmentClientServiceFallbackFactory.class) // 添加 Fallback,实现服务降级
public interface DepartmentClientService
{

	@RequestMapping(value = "/add", method = RequestMethod.POST)
	public boolean add(@RequestBody Department department);

	@RequestMapping(value = "/delete/{id}", method = RequestMethod.DELETE)
	public void delete(@PathVariable(value = "id") Long id);

	@RequestMapping(value = "/update", method = RequestMethod.PUT)
	public void update(@RequestBody Department department);

	@RequestMapping(value = "/get/{id}", method = RequestMethod.GET)
	public Department get(@PathVariable(value = "id") Long id);

	@RequestMapping(value = "/list")
	public List<Department> list();
}

重新安装公共模块 “ microservice-api ” ,以供其它子模块引用,达到通用的目的,点击项目右键 -> Run As -> Maven clean/Maven install。

 

修改基于 Feign 的服务消费者模块 “ microservice-feign-80 ” ,修改 YML 配置文件,新增开启 Hystrix 支持的配置,完整内容如下:

server:
  port: 80
  
feign:
  hystrix:
    enabled: true
  
eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://www.eureka7001.com:7001/eureka/,http://www.eureka7002.com:7002/eureka/,http://www.eureka7003.com:7003/eureka/
    

 

测试基于 Hystrix 的服务降级机制

先启动 Eureka 集群,再一个服务提供者模块,查看 Eureka 集群服务注册列表内容,如下图:

 

最后启动基于 Feign 的消费者服务器,正常访问消费者服务器地址,如下图:

 

模拟服务提供者出项异常宕机的情况,手动关闭提供者服务器,再次消费者服务器地址,会得出另外一个结果,如下图:

 

关于 Hystrix 的服务降级:

在上面通过手动关闭提供者服务器的形式,模拟在实际环境中,服务器宕机的情况,使消费者请求不达的情况。因为在消费者服务器客户端使用了 Hystrix 的服务降级机制,所以,当遇到服务器宕机,请求不达的情况下,消费者客户端直接返回事先预定义的响应内容,期间会停止几毫秒,一般用户根本不会察觉,客户端会在这短短的时间里裁定服务器是否死亡,之后用户再次刷新访问时,客户端则会立即返回,不会再次访问并裁定是否死亡。

GitLab 源码地址:

项目源码地址(zip格式的工程包):


好了,关于 Spring Cloud 进阶--Rest微服务加入Hystrix断路器,实现服务的熔断与降级 就写到这儿了,如果还有什么疑问或遇到什么问题欢迎扫码提问,也可以给我留言哦,我会一一详细的解答的。 
歇后语:“ 共同学习,共同进步 ”,也希望大家多多关注CSND的IT社区。


作       者: 华    仔
联系作者: [email protected]
来        源: CSDN (Chinese Software Developer Network)
原        文: https://blog.csdn.net/Hello_World_QWP/article/details/88087407
版权声明: 本文为博主原创文章,请在转载时务必注明博文出处!

猜你喜欢

转载自blog.csdn.net/Hello_World_QWP/article/details/88087407