Hystrix 断路器模式 – SpringCloud

Hystrix 断路器模式 – SpringCloud

原文:https://howtodoinjava.com/spring-cloud/spring-hystrix-circuit-breaker-tutorial/

了解如何利用称为 HystrixSpring cloud Netflix 堆栈组件之一来实现断路器,同时调用基础微服务。 通常需要在某些基础服务永久关闭/抛出错误的应用程序中启用容错功能,我们需要自动退回到程序执行的不同路径。 这与使用大量底层微服务的生态系统的分布式计算风格有关。 这是断路器模式的帮助之处,Hystrix 是构建此断路器的工具。

Hystrix 示例

Hystrix 配置通过四个主要步骤完成。

  1. 添加 Hystrix 启动器和仪表板依赖项。

    <dependency>
    	<groupId>org.springframework.cloud</groupId>
    	<artifactId>spring-cloud-starter-hystrix</artifactId>
    </dependency>
    <dependency>
    	<groupId>org.springframework.cloud</groupId>
    	<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
    </dependency>
    
    
  2. 添加@EnableCircuitBreaker注解
  3. 添加@EnableHystrixDashboard注解
  4. 添加注解@HystrixCommand(fallbackMethod = "myFallbackMethod")

什么是断路器模式?

如果我们在基于微服务的体系结构上设计系统,则通常会开发许多微服务,并且这些微服务会在实现某些业务目标时相互影响很大。 现在,我们所有人都可以假设,如果所有服务都已启动并且正在运行,并且每个服务的响应时间令人满意,那么这将给出预期的结果。

现在,如果当前生态系统的任何服务出现问题并停止为请求提供服务,将会发生什么。 这将导致超时/异常,并且由于此单点故障,整个生态系统将变得不稳定。

在这里,断路器模式非常方便,一旦发现任何此类情况,它将流量重定向到回退路径。 它还会密切监视有缺陷的服务,并在服务恢复正常后恢复流量。

因此,断路器是进行服务调用的方法的一种包装,它监视服务的运行状况,一旦出现问题,断路器将跳闸,所有进一步的调用都将返回到断路器,最后一旦服务回来了就自动恢复! 太酷了吧?

断路器调用顺序

Hystrix 断路器示例

为了演示断路器,我们将创建以下两个微服务,其中第一个微服务依赖于另一个。

  • 学生微服务 – 将提供Student实体的一些基本功能。 这将是一个基于 REST 的服务。 我们将从School服务中调用此服务,以了解断路器。 它将在本地主机的端口8098上运行。
  • 学校微服务 – 同样是一个简单的基于 REST 的微服务,我们将使用 Hystrix 实现断路器。 Student服务将从此处调用,一旦学生服务不可用,我们将测试后备路径。 它将在本地主机的端口 9098 上运行。

技术栈和演示运行时

  • Java 1.8
  • Eclipse 作为 IDE
  • Maven 作为构建工具
  • Spring Cloud Hystrix 作为断路器框架
  • SpringBoot
  • SpringRest

创建学生服务

请按照以下步骤创建和运行学生服务-一个简单的 REST 服务,提供学生实体的一些基本功能。

创建 spring boot 项目

Spring Boot 初始化器页面创建具有三个依赖项的 Spring Boot 项目,即WebRest RepositoriesActuator。 给出其他 Maven GAV 坐标并下载项目。

学生服务生成

将项目解压缩并将其作为现有的 maven 项目导入 Eclipse。 在此步骤中,将从 maven 存储库下载所有必需的依赖项。

服务器端口设置

打开application.properties并添加端口信息。

server.port = 8098

这将使该应用程序在默认端口 8098 上运行。通过在启动服务器时提供 -Dserver.port = XXXX参数,可以轻松地覆盖此应用程序。

创建 REST API

现在添加一个称为StudentServiceController的 REST 控制器类,并公开一个其余端点,以获取特定学校的所有学生详细信息。 在这里,我们公开/getStudentDetailsForSchool/{schoolname}端点来满足业务目的。 为简单起见,我们正在对学生详细信息进行硬编码。

StudentServiceController.java

package com.example.howtodoinjava.springhystrixstudentservice.controller;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.example.howtodoinjava.springhystrixstudentservice.domain.Student;

@RestController
public class StudentServiceController {

	private static Map<String, List<Student>> schooDB = new HashMap<String, List<Student>>();

	static {
		schooDB = new HashMap<String, List<Student>>();

		List<Student> lst = new ArrayList<Student>();
		Student std = new Student("Sajal", "Class IV");
		lst.add(std);
		std = new Student("Lokesh", "Class V");
		lst.add(std);

		schooDB.put("abcschool", lst);

		lst = new ArrayList<Student>();
		std = new Student("Kajal", "Class III");
		lst.add(std);
		std = new Student("Sukesh", "Class VI");
		lst.add(std);

		schooDB.put("xyzschool", lst);

	}

	@RequestMapping(value = "/getStudentDetailsForSchool/{schoolname}", method = RequestMethod.GET)
	public List<Student> getStudents(@PathVariable String schoolname) {
		System.out.println("Getting Student details for " + schoolname);

		List<Student> studentList = schooDB.get(schoolname);
		if (studentList == null) {
			studentList = new ArrayList<Student>();
			Student std = new Student("Not Found", "N/A");
			studentList.add(std);
		}
		return studentList;
	}
}

Student.java

package com.example.howtodoinjava.springhystrixstudentservice.domain;

public class Student {

	private String name;
	private String className;

	public Student(String name, String className) {
		super();
		this.name = name;
		this.className = className;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getClassName() {
		return className;
	}

	public void setClassName(String className) {
		this.className = className;
	}
}

建立并测试学生服务

现在,使用mvn clean install进行最终构建,并使用命令java -jar target\spring-hystrix-student-service-0.0.1-SNAPSHOT.jar运行服务器。 这将在默认端口8098中启动学生服务。

打开浏览器,然后输入http://localhost:8098/getStudentDetailsForSchool/abcschool

它应在浏览器中显示以下输出:

学生服务响应

创建学校服务 - 启用 Hystrix

与学生服务类似,为学校创建另一个微服务。 它将在内部调用已开发的学生服务。

生成 Spring Boot 项目

Spring Boot 初始化器页面主要使用那些依赖项创建一个 Spring Boot 项目。

  • Web – REST 端点
  • 执行器 – 提供基本管理 URL
  • Hystrix – 启用断路器
  • Hystrix 仪表板 – 启用一个与断路器监控相关的仪表板屏幕

给出其他 Maven GAV 坐标并下载项目。

学校服务项目

将项目解压缩并作为现有 maven 项目导入到 Eclipse 中。 在此步骤中,将从 maven 存储库下载所有必需的依赖项。

服务器端口设置

打开application.properties并添加端口信息。

server.port = 9098

这将使该应用程序在默认端口 9098 上运行。通过在启动服务器时提供 -Dserver.port = XXXX参数,可以轻松地覆盖此应用程序。

启用 Hystrix 设置

打开SpringHystrixSchoolServiceApplication,即使用@SpringBootApplication生成的类,并添加@EnableHystrixDashboard@EnableCircuitBreaker注解。

这将在应用程序中启用 Hystrix 断路器,还将添加一个在 Hystrix 提供的localhost上运行的有用仪表板。

package com.example.howtodoinjava.springhystrixschoolservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;

@SpringBootApplication
@EnableHystrixDashboard
@EnableCircuitBreaker
public class SpringHystrixSchoolServiceApplication {

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

添加 REST 控制器

在我们将公开/getSchoolDetails/{schoolname}端点的位置添加SchoolServiceController Rest 控制器,该端点将仅返回学校详细信息及其学生详细信息。 对于学生详细信息,它将调用已经开发的学生服务端点。 我们将创建一个代表层StudentServiceDelegate.java来调用学生服务。 这个简单的代码看起来像

SchoolServiceController.java

package com.example.howtodoinjava.springhystrixschoolservice.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.example.howtodoinjava.springhystrixschoolservice.delegate.StudentServiceDelegate;

@RestController
public class SchoolServiceController {

	@Autowired
	StudentServiceDelegate studentServiceDelegate;

	@RequestMapping(value = "/getSchoolDetails/{schoolname}", method = RequestMethod.GET)
	public String getStudents(@PathVariable String schoolname) {
		System.out.println("Going to call student service to get data!");
		return studentServiceDelegate.callStudentServiceAndGetData(schoolname);
	}
}

StudentServiceDelegate

我们将在此处执行以下操作以启用 Hystrix 断路器。

  • 通过提供的 Spring 框架调用学生服务RestTemplate
  • 添加 Hystrix 命令以启用回退方法 – @HystrixCommand(fallbackMethod = "callStudentServiceAndGetData_Fallback") – 这意味着我们将不得不添加另一个具有相同签名的方法callStudentServiceAndGetData_Fallback,当实际的学生服务关闭时将调用该方法。
  • 添加后备方法 – callStudentServiceAndGetData_Fallback,它将仅返回一些默认值。
package com.example.howtodoinjava.springhystrixschoolservice.delegate;

import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;

@Service
public class StudentServiceDelegate {

	@Autowired
	RestTemplate restTemplate;

	@HystrixCommand(fallbackMethod = "callStudentServiceAndGetData_Fallback")
	public String callStudentServiceAndGetData(String schoolname) {

		System.out.println("Getting School details for " + schoolname);

		String response = restTemplate
				.exchange("http://localhost:8098/getStudentDetailsForSchool/{schoolname}"
				, HttpMethod.GET
				, null
				, new ParameterizedTypeReference<String>() {
			}, schoolname).getBody();

		System.out.println("Response Received as " + response + " -  " + new Date());

		return "NORMAL FLOW !!! - School Name -  " + schoolname + " :::  " +
					" Student Details " + response + " -  " + new Date();
	}

	@SuppressWarnings("unused")
	private String callStudentServiceAndGetData_Fallback(String schoolname) {

		System.out.println("Student Service is down!!! fallback route enabled...");

		return "CIRCUIT BREAKER ENABLED!!! No Response From Student Service at this moment. " +
					" Service will be back shortly - " + new Date();
	}

	@Bean
	public RestTemplate restTemplate() {
		return new RestTemplate();
	}
}

建立和测试学校服务

现在,使用mvn clean install进行最终构建,并使用命令java -jar target\spring-hystrix-school-service-0.0.1-SNAPSHOT.jar运行服务器。 这将在默认端口 9098 中启动学校服务。

如上所述启动学生服务,然后通过打开浏览器并键入http://localhost:9098/getSchoolDetails/abcschool来测试学校服务。 它应该在浏览器中显示以下输出:

学校服务响应

测试 Hystrix 断路器 - 演示

打开浏览器,然后输入http://localhost:9098/getSchoolDetails/abcschool

它应在浏览器中显示以下输出:

学校服务响应

现在我们已经知道,学校服务在内部调用学生服务,并且正在从该服务获取学生详细信息。 因此,如果两个服务都在运行,则学校服务将显示学生服务返回的数据,正如我们在上面的学校服务浏览器输出中所看到的那样。 这是电路关闭状态

现在,让我们停止学生服务,只需在学生服务服务器控制台中按CTRL + C(停止服务器),然后从浏览器再次测试学校服务。 这次它将返回回退方法响应。 Hystrix 出现在这里,它会频繁地监视学生服务,并且在停机时,Hystrix 组件已打开电路并启用了后备路径。

这是浏览器中的回退输出。

学校服务响应回退路径

再次启动学生服务,等待片刻,然后返回学校服务,它将再次开始以正常流程进行响应。

Hystrix 仪表板

当我们添加了 hystrix 仪表板依赖时,hystrix 在以下 URL 中提供了一个不错的仪表盘和 Hystrix 流:

  • http://localhost:9098/hystrix.stream – Hystrix 产生的连续流。 这只是健康检查结果以及 Hystrix 正在监视的所有服务呼叫。 示例输出在浏览器中将如下所示:

    Hystrix 流式输出

  • http://localhost:9098/hystrix – 这是可视仪表板的初始状态。

    Hystrix 初始仪表盘

  • 现在,在仪表板中添加http://localhost:9098/hystrix.stream,以获得由 Hystrix 组件监视的电路的有意义的动态视觉表示。 在主页中提供流式输入后的虚拟仪表盘:

    Hystrix 虚拟仪表盘

总结

这就是可以制造 Spring 开关 Hystrix 断路器的全部内容,我们已经测试了电路开放路径电路闭合路径。 自行进行设置,并使用不同的组合服务状态以更清楚地了解整个概念。

如果您在执行本文时遇到任何困难,请添加评论。 我们将很乐意调查这个问题。

下载源码

学习愉快!