今天继续学习SpringCloud。
上篇我们讲了[服务网关Zuul和负载均衡Ribbon]
这一篇针对Feign和Hystrix详细说说
以下代码皆用最简单的代码示例,并非真正的业务代码
学习中用到的学习资料如下:
文章:SpringCloud极简入门
视频:Spring Cloud从入门到实战
在实际开发中,还有另外一种更加便捷的方式来实现相同的功能,这就是Feign,现在就来使用Feign实现服务消费的负载均衡。
与Ribbon一样,Feign也是由Netflix提供的,Feign是一个提供模版的声明式Web服务客户端,使用Feign可以简化Web Service客户端的编写,开发者可以通过简单的接口和注解来调用HTTP API,进行开发Spring Cloud也提供了Feign的集成组件:Spring Cloud Feign,它整合了Ribbon和Hystrix,具有可插拔,基于注解,负载均衡,服务熔断等多种便捷功能。
在Spring Cloud中使用Feign非常简单,Feign是一个声明式的Web服务客户端,所以只需要创建一个接口,同时在接口上添加相关注解即可完成服务提供方的接口绑定,相比较于Ribbon + RestTemplate的方式,Feign大大简化了代码的开发,Feign支持多种注释解,包括Feign注解,JAX-RS注解,Spring MVC注解等。Spring Cloud对Feign进行了优化,整合了Ribbon和Eureka,从而让Feign的使用更加方便。
Feign是一种比Ribbon更加方便好用的Web服务客户端,那么两者有什么具体区别呢?Feign好用在哪里?
关于Ribbon和Feign的区别可以简单地理解为Ribbon是个通用的HTTP客户端工具,而Feign则是基于Ribbon来实现的,同时它更加灵活,使用起来也更加简单,上节课中我们通过Ribbon + RestTemplate实现了服务调用的负载均衡,相比较于这种方式,使用Feign可以直接通过声明式接口的形式来调用服务,非常方便,比Ribbon使用起来要更加完善,只需要创建接口并添加相关注解配置,即可实现服务消费的负载均衡。
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
<version>2.0.2.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
<version>2.0.2.RELEASEversion>
dependency>
dependencies>
server:
port: 8050
spring:
application:
name: feign
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
package com.janeroad;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
public class FeignApplication {
public static void main(String[] args) {
SpringApplication.run(FeignApplication.class,args);
}
}
package com.janeroad.feign;
import com.janeroad.entity.Student;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.Collection;
@FeignClient(value = "provider")
public interface FeignProviderClient {
@GetMapping("/student/findAll")
public Collection findAll();
@GetMapping("/student/index")
public String index();
}
package com.janeroad.controller;
import com.janeroad.entity.Student;
import com.janeroad.feign.FeignProviderClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collection;
@RestController
@RequestMapping("/feign")
public class FeignHandler {
@Autowired
private FeignProviderClient feignProviderClient;
@GetMapping("/findAll")
public Collection findAll(){
return feignProviderClient.findAll();
}
@GetMapping("/index")
public String index(){
return feignProviderClient.index();
}
}
server:
port: 8050
spring:
application:
name: feign
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
feign:
hystrix:
enabled: true
feign.hystrix.enabled
:是否开启熔断器。
@Component
注解将 FeignError 实例注入 IoC 容器中。package com.janeroad.feign.impl;
import com.janeroad.entity.Student;
import com.janeroad.feign.FeignProviderClient;
import org.springframework.stereotype.Component;
import java.util.Collection;
@Component
public class FeignError implements FeignProviderClient {
@Override
public Collection findAll() {
return null;
}
@Override
public String index() {
return "服务器维护中......";
}
}
@FeignClient
的 fallback 属性设置映射。package com.janeroad.feign;
import com.janeroad.entity.Student;
import com.janeroad.feign.impl.FeignError;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.Collection;
@FeignClient(value = "provider",fallback = FeignError.class)
public interface FeignProviderClient {
@GetMapping("/student/findAll")
public Collection findAll();
@GetMapping("/student/index")
public String index();
}
依次启动注册中心,端口为8010的Provider,端口为8011的Provider,Feign
浏览器,访问http://localhost:8761,看到如下界面
可以看到两个Provider和Feign已经在注册中心完成注册,然后使用Postman工具测试Feign相关接口,如下图所示。
Feign同时提供了容错功能,如果服务提供者Provider出现故障无法访问,直接访问Feign会报错,如下图所示。
很简单这种直接返回错误状态码的交互方式不友好,可以通过容错机制给用户相应的提示信息,而不是错误状态码,从而交互方式更加友好,容错机制非常简单,首先在application.yml中。中添加配置。
server:
port: 8050
spring:
application:
name: feign
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
feign:
hystrix:
enabled: true
feign.hystrix.enabled:是否开启熔断器。
创建FeignProviderClient接口的实现类FeignError,定义容错处理逻辑,通过@Component将FeignError实例注入IoC容器。
@Component
public class FeignError implements FeignProviderClient {
@Override
public String index() {
return "服务器维护中....";
}
@Override
public Collection findAll() {
return null;
}
@Override
public Student findById(long id) {
return null;
}
@Override
public void save(Student student) {
}
@Override
public void update(Student student) {
}
@Override
public void deleteById(long id) {
}
}
在FeignProviderClient定义处通过@FeignClient的fallback属性设置映射。
@FeignClient(value = "provider",fallback = FeignError.class)
public interface FeignProviderClient {
}
启动注册中心和Feign,此时没有服务提供者Provider被注册,直接访问Feign接口,如下图所示。
上面涉及到 Feign 提供了容错功能,该功能是结合 Hystrix 实现的。
在一个分布式系统中,各个微服务之间相互调用、彼此依赖,实际运行环境中可能会因为各种个样的原因导致某个微服务不可用,则依赖于该服务的其他微服务就会出现响应时间过长或者请求失败的情况,如果这种情况出现的次数较多,从一定程度上就可以能导致整个系统崩溃,在不改变各个微服务调用关系的前提下,可以针对这些错误情况提前设置处理方案,遇到问题时,整个系统可以自主进行调整,这就是微服务容错机制的原理,发现故障并及时处理。
Hystrix 是 Netflix 的一个开源项目,是一个熔断器模型的具体实现,熔断器就类似于电路中的保险丝,某个单元发生故障就通过烧断保险丝的方式来阻止故障蔓延,微服务架构的容错机制也是这个原理。Hystrix 的主要作用是当服务提供者发生故障无法访问时,向服务消费者返回 fallback 降级处理,从而响应时间过长或者直接抛出异常的情况。
Hystrix 设计原则:
上面演示过了 Hystrix 的熔断机制,现在重点介绍 Hystrix 的另外一个重要功能——数据监控。
Hystrix 除了可以为服务提供容错机制外,同时还提供了对请求的监控,这个功能需要结合 Spring Boot Actuator 来使用,Actuator 提供了对服务的健康监控、数据统计功能,可以通过 hystrix-stream 节点获取监控到的请求数据,Dashboard 组件则提供了数据的可视化监控功能,接下来通过代码来实现 Hystrix 的数据监控。
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
<version>2.0.2.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
<version>2.0.2.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
<version>2.0.7.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-hystrixartifactId>
<version>2.0.2.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboardartifactId>
<version>2.0.2.RELEASEversion>
dependency>
dependencies>
server:
port: 8060
spring:
application:
name: hystrix
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
feign:
hystrix:
enabled: true
management:
endpoints:
web:
exposure:
include: 'hystrix.stream'
package com.janeroad;
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;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
@EnableCircuitBreaker
@EnableHystrixDashboard
public class HystrixApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixApplication.class,args);
}
}
注解说明:
@EnableCircuitBreaker
:声明启用数据监控
@EnableHystrixDashboard
:声明启用可视化数据监控
package com.janeroad.controller;
import com.janeroad.entity.Student;
import com.janeroad.feign.FeignProviderClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collection;
@RestController
@RequestMapping("/hystrix")
public class HystrixHandler {
@Autowired
private FeignProviderClient feignProviderClient;
@GetMapping("/findAll")
public Collection findAll(){
return feignProviderClient.findAll();
}
@GetMapping("/index")
public String index(){
return feignProviderClient.index();
}
}
http://localhost:8060/actuator/hystrix.stream
可以监控到请求数据,http://localhost:8060/hystrix
,可以看到可视化的监控界面,输入要监控的地址节点即可看到该节点的可视化数据监控。依次启动注册中心、Provider、Hystrix,可以直接访问红框中的 URL 来监控数据,在浏览器地址栏输入http://localhost:8060/actuator/hystrix.stream
此时没有客户端请求,所以没有数据,访问 http://localhost:8060/hystrix/index
再次访问 http://localhost:8060/actuator/hystrix.stream
显示了监控数据情况,但是这种方式并不直观,通过可视化界面的方式能够更加直观地进行监控数据,添加 Dashboard 即可,pom.xml 添加相关依赖。
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboardartifactId>
dependency>
HystrixApplication 类定义处添加 @EnableHystrixDashboard 注解。
@SpringBootApplication
@EnableFeignClients
@EnableCircuitBreaker
@EnableHystrixDashboard
public class HystrixApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixApplication.class,args);
}
}
访问 http://localhost:8060/hystrix
在红框处输入 http://localhost:8060/actuator/hystrix.stream 以及 Tiltle,点击 Monitor Stream 按钮,如下图所示
来了到可视化监控数据界面。