文章目录
四、Feign
1、概述
在前面的学习中,我们使用了Ribbon的负载均衡功能,大大简化了远程调用时的代码:
而Feign可以把Rest的请求进行隐藏,伪装成类似SpringMVC的Controller一样。你不用再自己拼接url,拼接参数等等操作,一切都交给Feign去做。
Feign是一个声明式WebService客户端。使用Feign能让编写Web Service客户端更加简单, 它的使用方法是定义一个接口,然后在上面添加注解,同时也支持JAX-RS标准的注解。Feign也支持可拔插式的编码器和解码器。Spring Cloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。
Feign是一个声明式的Web服务客户端,使得编写Web服务客户端变得非常容易,
只需要创建一个接口,然后在上面添加注解即可。
Feign集成了Ribbon利用Ribbon维护了MicroServiceCloud-Dept的服务列表信息,并且通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过feign只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用
2、功能实现
<1>、远程调用
首先在父工程下创建Feign的名为miscroservicecloudconsumerdeptfeign的maven工程,在pom中添加如下依赖:
<dependencies>
<dependency>
<groupId>SpringCloud</groupId>
<artifactId>miscroserviceapi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--feign相关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--ribbon相关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!--修改后立即生效、热部署-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
<version>1.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
创建主启动类并加上@EnableFeignClients注解,具体代码如下:
package springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = {"springcloud"})
@ComponentScan("springcloud")
public class DeptConsumer80_Feign_App {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer80_Feign_App.class, args);
}
}
接下来创建Feign的客户端:
package springcloud.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import springcloud.entities.Dept;
import java.util.List;
@FeignClient(value = "MISCROSERVICECLOUD-DEPT")
public interface DeptClientService {
@RequestMapping(value = "/dept/get/{id}",method = RequestMethod.GET)
Dept get(@PathVariable("id") long id);
@RequestMapping(value = "/dept/list", method = RequestMethod.GET)
List<Dept> list();
@RequestMapping(value = "/dept/add", method = RequestMethod.POST)
boolean add(Dept dept);
}
调用Feign客户端,完成简化
package springcloud.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.RestController;
import springcloud.entities.Dept;
import springcloud.service.DeptClientService;
import java.util.List;
@RestController
public class DeptController_Consumer {
@Autowired
private DeptClientService service;
@RequestMapping(value = "/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") Long id){
return service.get(id);
}
@SuppressWarnings("unchecked")
@RequestMapping(value = "/consumer/dept/list")
public List<Dept> list(){
return service.list();
}
//测试@EnableDiscoveryClient,消费端可以调用服务发现
@RequestMapping(value = "/consumer/dept/add")
public Object add(Dept dept){
return service.add(dept);
}
}
另外,Feign内置了一个超时时常,我们也可以在配置文件中对其进行设置
ribbon:
ConnectionTimeOut: 500 #连接超时时常
ReadTimeOut: 2000 #读取超时时常
<2>、熔断机制
首先先说一下,Hystrix默认Feign在中默认是关闭的,我们需要在配置文件中进行手动开启
feign:
hystrix:
enabled: true #开启Feign的熔断功能
首先编写一个熔断接口
package springcloud.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import springcloud.entities.Dept;
import java.util.List;
//@FeignClient(value = "MISCROSERVICECLOUD-DEPT")
@FeignClient(value = "MISCROSERVICECLOUD-DEPT",fallbackFactory = DeptClientServiceFallbackFactory.class)
public interface DeptClientService {
@RequestMapping(value = "/dept/get/{id}",method = RequestMethod.GET)
Dept get(@PathVariable("id") long id);
@RequestMapping(value = "/dept/list", method = RequestMethod.GET)
List<Dept> list();
@RequestMapping(value = "/dept/add", method = RequestMethod.POST)
boolean add(Dept dept);
}
编写自己的FallbackFactory类,实现自己想要实现的功能:
package springcloud.service;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
import springcloud.entities.Dept;
import java.util.List;
@Component
public class DeptClientServiceFallbackFactory implements FallbackFactory<DeptClientService> {
@Override
public DeptClientService create(Throwable throwable) {
return new DeptClientService() {
@Override
public Dept get(long id) {
return new Dept().setDeptno(id).setDname("该ID:" + id + "没有对应信息,Consumer客户端提供的降级信息" +
",此刻服务Provider已经关闭").setDb_source("no this database in MySQL");
}
@Override
public List<Dept> list() {
return null;
}
@Override
public boolean add(Dept dept) {
return false;
}
};
}
}
测试之后显示成功。
五、Hystrix
1、简述
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
2、服务雪崩
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它的微服务,这就是所谓的“扇出”。如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的“雪崩效应”.
对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。
Hystrix解决雪崩问题的方法有两个:
1.线程隔断
2.服务熔断
3、线程隔离、服务降级
<1>、简述
使用了Hystrix之后会给每一个服务分配线程池,让每一个微服务占有的线程是有限的,因此也就不会占用到过量的服务器性能,以致出现错误的时候使服务器瘫痪。但是这个作用仅仅是不让服务器瘫痪,事实上服务本身还是导致了阻塞,为了使我们的程序正常运行,因此要让出错的服务进行降级,从而使程序正常运行,具体方式就是设置一个相应时间,由于阻塞导致无法进行相应,这时,就对相应的服务进行降级。
<2>、程序实现
首先添加依赖:
<!--hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
在启动类添加添加:
@EnableCircuitBreaker //对Hystrix熔断机制的支持
到目前为止,启动类已经添加了很多注解
这三个注解可以用@SpringCloudApplication
这个注解替换
然后对某个方式开启容错机制:
package springcloud.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import springcloud.entities.Dept;
import springcloud.service.DeptService;
@RestController
public class DeptController {
@Autowired
private DeptService service = null;
@RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)
//一旦调用服务方法失败并抛出了错误信息后,会自动调用@HystrixCommand标注好的fallbackMethod调用类中指定的方法
@HystrixCommand(fallbackMethod = "processHystrix_Get")
public Dept get(@PathVariable("id") Long id){
Dept dept = this.service.get(id);
if (null == dept){
throw new RuntimeException("该ID:" + id + "没有对应的信息");
}
return dept;
}
public Dept processHystrix_Get(@PathVariable("id") Long id){
return new Dept().setDeptno(id).setDname("该ID:" + id + "没有对应的信息,null@HystrixCommand")
.setDb_source("no this database in MySQL");
}
}
4、服务熔断
<1>、熔断原理
服务熔断
熔断机制是应对雪崩效应的一种微服务链路保护机制。
当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回"错误"的响应信息。当检测到该节点微服务调用响应正常后恢复调用链路。在SpringCloud框架里熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败就会启动熔断机制。熔断机制的注解是@HystrixCommand。
Close:关闭状态(断路器关闭),所有请求都正常访问。
Open:打开状态(断路器打开,所有请求都会被降级)。Hystrix会对请求情况计数。,当一定时间内的失败请求百分比达到阈值,或者触发熔断断路器会完全关闭。默认失败的阈值是50%,请求次数最少不低于20次。
Half Open:半开状态,Closed状态不是永久的,关闭后会进入休眠时间(默认是5s).随后断路器会自动进入半开状态。此时会释放部分请求通过,若这些请求都是健康的则会完全打开断路器,否则关闭再次进行休眠计时。
<2>、程序实现
package springcloud.service;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
import springcloud.entities.Dept;
import java.util.List;
@Component
public class DeptClientServiceFallbackFactory implements FallbackFactory<DeptClientService> {
@Override
public DeptClientService create(Throwable throwable) {
return new DeptClientService() {
@Override
public Dept get(long id) {
return new Dept().setDeptno(id).setDname("该ID:" + id + "没有对应信息,Consumer客户端提供的降级信息" +
",此刻服务Provider已经关闭").setDb_source("no this database in MySQL");
}
@Override
public List<Dept> list() {
return null;
}
@Override
public boolean add(Dept dept) {
return false;
}
};
}
}
然后重新运行程序,此时会发现频繁重启一个网站之后,提示无法继续访问。
5、服务监控HystrixDashboard
<1>、简要概述
除了隔离依赖服务的调用以外,Hystrix还提供了准实时的调用监控(Hystrix Dashboard),Hystrix会持续地记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行多少请求多少成功,多少失败等。Netflix通过hystrix-metrics-event-stream项目实现了对以上指标的监控。Spring Cloud也提供了Hystrix Dashboard的整合,对监控内容转化成可视化界面。
<2>、程序实现
首先创建一个实现HystrixDashboard功能的maven工程
添加相关依赖:
·<dependencies>
<dependency>
<groupId>SpringCloud</groupId>
<artifactId>miscroserviceapi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
<version>1.2.6.RELEASE</version>
</dependency>
<!--ribbon相关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!--fegin相关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--hystrix和hystrix-dashboard-->
<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>
编写主启动类
package springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@SpringBootApplication
@EnableHystrixDashboard
public class DeptConsumer_Dashboard_App {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_Dashboard_App.class, args);
}
}
接下来在所有的微服务提供类添加监控依赖配置
<!--actuator监控信息完善-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
当启动这个监控工程后会显示:
当观察到这个界面的时候,则证明监控类是正确的。