Feign负载均衡 Hystrix熔断器

feign负载均衡--------------

feign 是基于Ribbon负载均衡的实现,Feign对Ribbon进行了封装。

Feign执行原理

在这里插入图片描述

集成fegin

1)导入依赖

<dependencies>
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
  </dependency>
</dependencies>

2)创建Feign的客户端 接口 也就是类 (看自己业务需求是否要单独提取出来做一个工程)

@FeignClient("user-service")         //声明这是一个Feign客户端,   Eureka中的名称
public interface UserFeignApi {

    @GetMapping("/user/{id}")               //  括号里面是service的地址  从Eureka里面获得url和port
    public User findById(@PathVariable("id") String username);
}

那么如果消费者不止一个,每个消费者都需要去编写FeignClient接口,显然是对人力资源的浪费。

3)在consumer工程中新建FeignController,使用userFeignApi访问:

@RestController
@RequestMapping("feign")
public class FeignController {
    @Autowired
    UserFeignApi userFeignApi;
    @GetMapping("{id}")
    public User queryById(@PathVariable("id") String id){
        return userFeignApi.findById(id);
    }
}

4)在consumer启动类开启Feign功能

@SpringCloudApplication
@EnableFeignClients(basePackages = "com.itheima.sh.client")// 开启Feign功能
public class ConsumerDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserConsumerDemoApplication.class, args);
    }
}
访问  localhost:8081/feign/id

正确的做法应该是:
定义一个独立的jar工程
在该工程中,编写FeignClient接口及所需要的实体类
任何消费者,只需要引入这个jar包就能直接使用。
接口中的定义方法,完全采用SpringMVC的注解,Feign会根据注解帮我们生成URL,并访问获取结果
注意:Feign中已经自动集成了Ribbon负载均衡,因此我们不需要自己定义RestTemplate了
后面开启@EnableFeginClients会用另一种方式书写

请求时长和请求机制 -yml

因为是封装了ribbon所以他有负载均衡的能力 他的依赖里面也有ribbon

Fegin内置的ribbon默认设置了请求超时时长,默认是1000ms,我们可以通过手动配置来修改这个超时时长

ribbon:
  ReadTimeout: 2000 # 读取超时时长
  ConnectTimeout: 1000 # 建立链接的超时时长

因为ribbon内部有重试机制,一旦超时,会自动重新发起请求。如果不希望重试,可以添加配置:

ribbon:
  ConnectTimeout: 500 # 连接超时时长
  ReadTimeout: 1000 # 数据通信超时时长
  MaxAutoRetriesNextServer: 2 # 切换重试多少次服务
  MaxAutoRetries: 1 # 当前服务器的重试次数
  OkToRetryOnAllOperations: false # 是否对所有的请求方式都重试

配置fegin日志级别

前面讲过,通过logging.level.xx=debug来设置日志级别。然而这个对Fegin客户端而言不会产生效果。因为@FeignClient注解修饰的客户端在被代理时,都会创建一个新的Fegin.Logger实例。我们需要额外指定这个日志的级别才可以。

1)设置com.itheima.sh包下的日志级别都为debug 谁调用的它在睡得下面配置yml文件

logging:
  level:
    com.itheima.sh: debug

2)编写配置类,定义日志级别 --fegin配置日志是需要写一个类

@Configuration
public class FeignConfig {
    @Bean
    Logger.Level feignLoggerLevel(){
        return Logger.Level.FULL;
    }
}

在这里插入图片描述

Hystix熔断器--------------

分布式系统面临的问题 :扇出、服务雪崩

扇出

多个微服务互相调用的时候,如果A调用B、C,而B、C又继续调用其他微服务,这就是扇出(像一把扇子一样慢慢打开)。

服务雪崩

例如微服务I发生异常,请求阻塞,用户不会得到响应,则tomcat的这个线程不会释放,于是越来越多的用户请求到来,越来越多的线程会阻塞:服务器支持的线程和并发数有限,请求一直阻塞,会导致服务器资源耗尽,从而导致所有其它服务都不可用,形成雪崩效应。

雪崩效应产生的几种场景:

流量激增:比如异常流量、用户重试导致系统负载升高;
缓存刷新:假设A为client端,B为Server端,假设A系统请求都流向B系统,请求超出了B系统的承载能力,就会造成B系统崩溃;
程序有Bug:代码循环调用的逻辑问题,资源未释放引起的内存泄漏等问题;
硬件故障:比如宕机,机房断电,光纤被挖断等。
数据库严重瓶颈,比如:长事务、sql超时等。
线程同步等待:系统间经常采用同步服务调用模式,核心服务和非核心服务共用一个线程池和消息队列。如果一个核心业务线程调用非核心线程,这个非核心线程交由第三方系统完成,当第三方系统本身出现问题,导致核心线程阻塞,一直处于等待状态,而进程间的调用是有超时限制的,最终这条线程将断掉,也可能引发雪崩;

雪崩解决方案

超时机制

通过网络请求其他服务时,都必须设置超时。正常情况下,一个远程调用一般在几十毫秒内就返回了。当依赖的服务不可用,或者因为网络问题,响应时间将会变得很长(几十秒)。而通常情况下,一次远程调用对应了一个线程/进程,如果响应太慢,那这个线程/进程就会得不到释放。而线程/进程都对应了系统资源,如果大量的线程/进程得不到释放,并且越积越多,服务资源就会被耗尽,从而导致资深服务不可用。所以必须为每个请求设置超时。

断路器模式

试想一下,家庭里如果没有断路器,电流过载了(例如功率过大、短路等),电路不断开,电路就会升温,甚至是烧断电路、起火。有了断路器之后,当电流过载时,会自动切断电路(跳闸),从而保护了整条电路与家庭的安全。当电流过载的问题被解决后,只要将关闭断路器,电路就又可以工作了。

同样的道理,当依赖的服务有大量超时时,再让新的请求去访问已经没有太大意义,只会无谓的消耗现有资源。譬如我们设置了超时时间为1秒,如果短时间内有大量的请求(譬如50个)在1秒内都得不到响应,就往往意味着异常。此时就没有必要让更多的请求去访问这个依赖了,我们应该使用断路器避免资源浪费。

断路器可以实现快速失败,如果它在一段时间内侦测到许多类似的错误(譬如超时),就会强迫其以后的多个调用快速失败,不再请求所依赖的服务,从而防止应用程序不断地尝试执行可能会失败的操作,这样应用程序可以继续执行而不用等待修正错误,或者浪费CPU时间去等待长时间的超时。断路器也可以使应用程序能够诊断错误是否已经修正,如果已经修正,应用程序会再次尝试调用操作。

断路器模式就像是那些容易导致错误的操作的一种代理。这种代理能够记录最近调用发生错误的次数,然后决定使用允许操作继续,或者立即返回错误。

Hystix 解决雪崩问题(概念)

Hystrix是一个用于处理分布式系统延迟和容错的开源库。分布式系统中,依赖避免不了调用失败,比如超时,异常等。Hystrix能保证在出现问题的时候,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性

hystix作用

Hystrix主要的作用就是:服务的熔断、服务降级、服务限流、近实时监控。
Hystix是Netflix开源的一个延迟和容错库,用于隔离访问远程服务、第三方库,防止出现级联失败。

服务的熔断(保险丝)
  • 熔断机制的注解是==@HystrixCommand==
  • 熔断机制是应对雪崩效应的一种==链路保护机制==,一般存在于服务端
  • 当扇出链路的某个服务出现故障或响应超时,会进行==服务降级,进而熔断该节点的服务调用==,快速返回“错误”的相应信息。

Hystrix的熔断存在阈值,缺省是5秒内20次调用失败就会触发

线程隔离,服务降级

线程隔离:

Hystrix为每个服务调用的功能分配一个小的线程池,如果线程池已满调用将被立即拒绝,默认不采用排队.加速失败判定时间。

用户的请求将不再直接访问服务,而是通过线程池中的空闲线程来访问服务,如果线程池已满,或者请求超时,则会进行降级处理:返回给用户一个错误提示或备选结果

用户的请求故障时,不会被阻塞,更不会无休止的等待或者看到系统崩溃,至少可以看到一个执行结果(例如返回友好的提示信息) 。

服务降级

服务降级**虽然会导致请求失败,但是不会导致阻塞,而且最多占用该服务的线程资源,不会导致整个容器资源耗尽,把故障的影响隔离在线程池内。

Hystrix降级demo实现

线程隔离的完整步骤包括:

  1. 引入Hystrix的依赖
  2. 添加注解,开启Hystix功能
  3. 给业务编写降级的备用处理逻辑
  4. 给业务添加注解,开启线程隔离功能
  5. 设置触发降级的最长等待时间
    现在,consumer-demo是服务的调用者,user-service是服务的提供者,因此consumer-demo需要把调用user-service的业务隔离,避免级联失败。

因此我们接下来的操作都是在consumer-demo中添加的。

1)在consumer-demo添加Hystix的依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

2)开启熔断 在consumer-demo的启动类上添加注解:@EnableCircuitBreaker

@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public class ConsumerApplication {
	// ...
}

我们这里可以使用@SpringCloudApplication这个注解代替上面三个注解以为它包含了上面三个注解在这里插入图片描述
3)编写降级业务(这个demo只适用于只有一个方法,大批量的话每个方法都要指定太麻烦,后面另一种Hystrix结合Feign ,工作中会用到第二种)

当目标服务的调用出现故障,我们希望快速失败,给用户一个友好提示。因此需要提前编写好失败时的降级处理逻辑,然后使用HystixCommond来指定降级的方法。

consumer-demo中ConsumerController在queryById方法上添加 @HystrixCommand注解用来声明一个降级逻辑的方法

@GetMapping("{id}")
@HystrixCommand(fallbackMethod = "queryByIdFallBack")
public User queryById(@PathVariable("id") String id){
  User user = restTemplate.getForObject("http://user-service/user/"+id, User.class);
  return user;
}

// 降级方法
public User queryByIdFallBack(String id) {
  User user = new User();
  user.setName("暂停服务");
  return user;
}

注意:相同的参数列表和返回值声明**。失败逻辑中返回User对象没有太大意义,一般会返回友好提示。所以我们把queryById的方法改造为返回String,反正也是Json数据。这样失败逻辑中返回一个错误说明,会比较方便。

超时设置 --yml

这里的超时时间要大于Fegin的请求时长和建立连接时常的总和

hystrix:
  command:
    default:
      execution.isolation.thread.timeoutInMilliseconds: 2000

熔断器触发的阀值 – yml

熔断器的默认触发阈值是20次请求,不好触发。休眠时间时5秒,时间太短,不易观察,为了测试方便,我们可以通过配置修改熔断策略:

hystrix:
  command:
    default:
      execution.isolation.thread.timeoutInMilliseconds: 2000
      circuitBreaker:
        errorThresholdPercentage: 50 # 触发熔断错误比例阈值,默认值50%
        sleepWindowInMilliseconds: 10000 # 熔断后休眠时长,默认值5秒
        requestVolumeThreshold: 10 # 触发熔断的最小请求次数,默认20

服务熔断 状态机模型

Hystrix熔断状态机的模型
在这里插入图片描述

状态机有3个状态:

  • Closed:关闭状态(断路器关闭),所有请求都正常访问。
  • Open:打开状态(断路器打开),所有请求都会被降级。Hystix会对请求情况计数,当一定时间内失败请求百分比达到阈值,则触发熔断,断路器打开。默认失败比例的阈值是50%,请求次数最少不低于20次
  • Half Open:半开状态,open状态不是永久的,打开后会进入休眠时间(默认是5S)。随后断路器会自动进入半开状态。此时会释放1次请求通过,若这个请求是健康的,则会关闭断路器,否则继续保持打开,再次进行5秒休眠计时。

Feign集成Hystix

Fe0gn默认也有对Hystix的集成,只不过,默认情况下是关闭的。我们需要通过下面的参数来开启
步骤:
1.在Fegin 的yml文件中开启 熔断功能
2.定义一个类返回一个降级之后传给前端的友好页面 UserFeignApiFallBack(随意定义)
3.然后在Fegin(FeignController)的客户端中,指定刚才编写的实现类

1)在Fegin 的yml文件中开启 熔断功能

feign:
  hystrix:
    enabled: true # 开启Feign的熔断功能

2)首先,我们要定义一个类,实现刚才编写的UserFeignClient,作为fallback的处理类

package com.itheima.sh.clients.fallback;

import com.itheima.sh.clients.UserFeignApi;
import com.itheima.sh.pojo.User;
import org.springframework.stereotype.Component;

@Component
public class UserFeignApiFallBack implements UserFeignApi {
    @Override
    public User findById(String username) {
        User user = new User();
        user.setName(username);
        user.setNickName("UserFeignApiFallBack");
        return user;
    }
}

3)然后在Fegin(FeignController)的客户端中,指定刚才编写的实现类
fallback = UserFeignApiFallBack.class 这个就是用来指定的

package com.itheima.sh.client;

import com.itheima.sh.client.fallback.UserFeignApiFallBack;
import com.itheima.sh.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/**
 * @program: Fegin的客户端
 * @description:
 * @author: Mr.Wang
 * @create: 2020-11-27 13:04
 **/

@FeignClient(value = "USER-SERVICE",fallback = UserFeignApiFallBack.class)     //指定刚刚编写的实现类
public interface FeignController {
    @GetMapping("/user/{username}")
    public User findById(@PathVariable("username")String name);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值