在为微服务架构中,我们将系统拆分成多个服务单元,各个单元的应用间通过服务注册与订阅的方式互相依赖的,由于每个单元都在不同的进程中运行,依赖通过远程调用的方式执行,这样就有可能因为网络原因或是依赖服务自身问题出现调用故障或者延迟,而这些问题直接导致调用方的对外服务也出现延迟,若此时调用方的请求不断增加,最后就会因为等待出现故障依赖方的响应形成任务积压,最终导致自身服务瘫痪。
因此为了解决这个问题产生了断路器等一系列的服务保护机制。
在分布式架构中,断路器模式的作用也是类似的,当某个服务单元发生故障,通过断路器的故障监控(类似熔断保险丝), 向调用方返回一 个错误响应, 而不是长时间的等待。
针对上面的问题 Spring Cloud Hystrix实现了断路器,线程隔离等一系列服务保护功能,他也是基于Netflix的开源框架Hystrix实现的,该框架的目标在于通过控制那些访问远程系统,服务和第三方库节点,从而对延迟和故障提供更强大的容错能力。Hystrix具备服务降级、 服务熔断、 线程和信号隔离、 请求缓存、 请求合并以及服务监控等强大功能。
在使用Spring Cloud Hystrix实现断路器之前,我们先用实现的一些内容作为基础:构建如下服务调用关系:
需要启动以下工程:
1、eureka-server工程:服务注册中心,端口:1111
2、hello-service工程:HELLO-SERVICE的服务单元,两个实例启动端口分别为8081和8082
3、ribbon-consumer工程:使用Ribbon实现的消费者服务,端口9000
一、eureka-server工程:服务注册中心,端口:1111
在前几篇博文中已经创建了改工程:如下图
源代码下载地址:http://download.csdn.net/download/qq_34288630/10208119
启动后:
二、hello-service工程:HELLO-SERVICE的服务单元,两个实例启动端口分别为8081和8082
服务提供者,该工程之前的博文有创建,此时继续应用它:
源代码地址:http://download.csdn.net/download/qq_34288630/10208151
如图:分别修改server.port为8081和8082启动
eureka provider服务的端口号
server.port=8081
启动后 刷新http://localhost:2222/ 如下图 会出现两个同名 但是不同端口的实例:
三、ribbon-consumer工程:使用Ribbon实现的消费者服务,端口9000
启动消费者:
源代码:http://download.csdn.net/download/qq_34288630/10208248
在没有加入断路器之前,关闭8081的实例,发送GET请求到如上图的路径http://localhost:9000/psw
{
"timestamp": 1516088325633,
"status": 500,
"error": "Internal Server Error",
"exception": "java.lang.IllegalStateException",
"message": "No instances available for eureka.client",
"path": "/psw"
}
下面我们引入Spring Cloud Hystrix
(1)、在ribbon-consummer工程中pom.xml的dependency节点引入spring-cloud-starter-hystrix依赖:
<!--熔断器-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
完整pom.xml:
<?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.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--spring boot 单元测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--服务消费的任务有Ribbon-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<!--表明是一个web应用 -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--该模块能够自动为 Spring Boot 构建的应用提供
一 系列用千监控的端点-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--eureka客户端依赖 -->
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--熔断器-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
</dependencies>
<!--服务注册中心-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Camden.SR6</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
(2)、在ribbon-consumer工程的主类RibbonConsumerApplication中使@EnableCircuitBreaker注解开启断路器功能:
package com.example.ribbonconsumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@EnableCircuitBreaker
@EnableEurekaClient
@SpringBootApplication
public class RibbonConsumerApplication {
@Bean
@LoadBalanced
RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(RibbonConsumerApplication.class, args);
}
}
注意:::::::::::
这里还可以使用Spring Cloud应用中的@SpringCloudApplication注解来修饰应用主类,该注解具体定义如下,可以看到此注解中包含了上述所引用的三个注解,意味着一个Spring Cloud标准应用包含服务发现以及断路器。
(3)、改造服务消费方式
1、pom.xml增加依赖:
<!-- hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
2、,新增HelloService类,注入RestTemplate实例。然后将HelloController中对RestTemplate的使用迁移l到helloService函数中,最后在helloService函数上增加@HystrixCommand注解来指定回调方法:
package com.example.ribbonconsumer.service;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
/**
* @Author:peishunwu
* @Description:
* @Date:Created in ${Time} 2018/1/16
*/
@Service
public class HelloService {
@Autowired
RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "helloFallback")
public String helloService(){
return restTemplate.getForEntity("http://eureka.client/hello",String.class).getBody();
}
public String helloFallback (){
return "error";
}
}
3、修改controller,注入上面的HelloService实例,并在方法内调用如下图:
package com.example.ribbonconsumer.web;
import com.example.ribbonconsumer.service.HelloService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* @Author:peishunwu
* @Description:
* @Date:Created in ${Time} 2018/1/16
*/
@RestController
public class HelloController {
private final Logger logger = LoggerFactory.getLogger(HelloController.class);
@Autowired
HelloService helloService;
@RequestMapping(value = "/psw",method = RequestMethod.GET)
public String index(){
return helloService.helloService();
}
}
下面验证一下通过断路器实现的服务回调逻辑,重新启动之前关闭的8081端口,确保此时的服务注册中心,两个服务以及消费者均启动,访问http://localhost:9000/psw 可以轮询两个服务并返回一些文字信息,此时断开8081服务端时,输出内容error: