微服务(五)Hystrix

前言

spring cloud 用的是 hystrix,是一个容错组件。

Hystrix实现了 超时机制和断路器模式。

Hystrix是Netflix开源的一个类库,用于隔离远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。主要有以下几点功能:

  1. 为系统提供保护机制。在依赖的服务出现高延迟或失败时,为系统提供保护和控制。
  2. 防止雪崩。
  3. 包裹请求:使用HystrixCommand(或HystrixObservableCommand)包裹对依赖的调用逻辑,每个命令在独立线程中运行。
  4. 跳闸机制:当某服务失败率达到一定的阈值时,Hystrix可以自动跳闸,停止请求该服务一段时间。
  5. 资源隔离:Hystrix为每个请求都的依赖都维护了一个小型线程池,如果该线程池已满,发往该依赖的请求就被立即拒绝,而不是排队等候,从而加速失败判定。防止级联失败。
  6. 快速失败:Fail Fast。同时能快速恢复。侧重点是:(不去真正的请求服务,发生异常再返回),而是直接失败。
  7. 监控:Hystrix可以实时监控运行指标和配置的变化,提供近实时的监控、报警、运维控制。
  8. 回退机制:fallback,当请求失败、超时、被拒绝,或当断路器被打开时,执行回退逻辑。回退逻辑我们自定义,提供优雅的服务降级。
  9. 自我修复:断路器打开一段时间后,会自动进入“半开”状态,可以进行打开,关闭,半开状态的转换。

1、模拟hystrix

其实Hystrix完全可以用try catch实现,这里解释一下逻辑。

* try{
	 * 
	 *     1.   发起向服务方的请求;
	 *     		1.1 判断连接超时
	 *     			-> 这次请求 记录到服务里
	 *     		http请求  线程消耗
	 *     
	 *     
	 *     		map(URI,线程数) 
	 *     		线程池(线程数)
	 *        阈值 阀值
	 *        
	 *        计数 连续失败次数 达到阈值 
	 *        count ++*     if(count == 10){
	 *     
	 *     new romdom  == 1  按时间
	 *       发请求
	 *     
	 *     
	 *     	throw exception;
	 *     }
	 *     
	 *     
	 *         请求/不请求/半请求
	 *         开      关         半开
	 *     
	 *     if (当前线程满了){
	 *     	throw exception
	 *     }
	 *     		
	 *     
	 *     		1.2 尝试向其他服务器发起请求
	 *     
	 *     
	 *     注解
	 *     
	 *     
	 *     2. 还是没成功
	 *     
	 *     }catch(Exception e){
	 *     
	 *     	1.	避免返回不友好的错误信息
	 *     			-> 好看点儿的页面  重试按钮 联系邮箱
	 *     
	 *     		
	 *     	2.	return 另外一个东西 写到MQ里 admin 发个邮件
	 *     
	 *     		return "客观稍后再来"*     
	 *     }
	 *     
	 *     
	 *     Hystrix 干的就是这件事儿
	 */


这里的降级就是在请求一个服务器后,没请求到,请求其他服务器,还是不行,那么返回一种比较友好的界面,比如服务器正忙,一般用兜底数据处理。
而所谓熔断,则是在请求服务的阈值达到了一个上限后,不去请求了,直接到catch里,进行降级。但是一般会用半请求的方式,隔一段时间再请求下服务。
限流就是用线程隔离或者信号量隔离的方式,固定某个服务用多少个线程,防止consumer端调用provider时,线程不够用的情况。
降级一般是再某一段时间内,流量特别大,将资源都给另外的服务做的。熔断则是防止服务断了之后会影响到其他服务做的。
降级的方案一般有两种,一种返回一个友好的界面,重试按钮。另外一种是返回另外一个页面,但是写到MQ里,发个邮件通知。

2、Hystrix独立使用

在请求的那一端,
在里面的run就是try,getfallback就是catch(运行失败)

public class HystrixTest extends HystrixCommand {

	protected HystrixTest(HystrixCommandGroupKey group) {
		super(group);
		// TODO Auto-generated constructor stub
	}

	public static void main(String[] args) {

		
	//	HystrixTest hystrixTest = new HystrixTest(HystrixCommandGroupKey.Factory.asKey("ext"));
		/**
		 * execute():以同步阻塞方式执行run()。以demo为例,调用execute()后,
		 * hystrix先创建一个新线程运行run(),
		 * 	接着调用程序要在execute()调用处一直阻塞着,直到run()运行完成 
		 */
	//	System.out.println("result:" + hystrixTest.execute());
		
		/**
		 * queue():以异步非阻塞方式执行run()。以demo为例,
		 * 	一调用queue()就直接返回一个Future对象,
		 * 	同时hystrix创建一个新线程运行run(),
		 * 	调用程序通过Future.get()拿到run()的返回结果,
		 * 	而Future.get()是阻塞执行的
		 */
		Future<String> futureResult = new HystrixTest(HystrixCommandGroupKey.Factory.asKey("ext")).queue();
		String result = "";
		try {
			result = futureResult.get();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ExecutionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		System.out.println("程序结果:"+result);
	}

	@Override
	protected Object run() throws Exception {
		// TODO Auto-generated method stub
		System.out.println("执行逻辑");
		int i = 1/0;
		return "ok";
	}

	@Override
	protected Object getFallback() {
		// TODO Auto-generated method stub
		return "getFallbackgetFallback";
	}
	
	
	
}

程序测试结果。(因为走了run,有个错误,然后返回到了getfallback中,与try,catch的逻辑一样)
在这里插入图片描述

3、Hystrix整合RestTemplate

先启动Eureka服务server。
在userconsumer方编写service:

@Service
public class RestService {
    @Autowired
    RestTemplate template;

    // 请求不同进back方法。
    @HystrixCommand(defaultFallback = "back")
    public String alive() {
        String url = "http://user-provider/User/alive";
        String str= template.getForObject(url, String.class);

        return str;
    }

    public String back(){

        return "哈哈";
    }
}

在consumer的controller层编写:

    @GetMapping("/alive2")
    public String alive2() {
        return "Consumer" + port + "->>>>>>>" +rest.alive();
    }

在provider方用一个错误测试:

    @Override
    public String alive() {
        int i = 1/0;
        return "port:" + port;
        }

效果:
在这里插入图片描述
如果去除错误的情况:
在这里插入图片描述
能够正常访问port端口。

3、Hystrix整合RestTemplate

有两种方式可以整合feign。
直接指定一个类或者拿fallbackfactory去做。
第一种方式:
在consumerApi中的@FeignClient中再加个配置属性。

@FeignClient(name = "user-provider",fallback =userback.class)

需要在properties加:(默认feign调用失败不用hystrix处理)

feign.hystrix.enabled=true

注意在整合的时候,会出现user已经被映射了的问题,注意需要在发布的api中改一下:
feign和hystrix结合使用的时候,在发布的api里,会在方法中的@RequestMapping(“/alive”)的路径中加上“/user”,此时就变成了@RequestMapping(“/user/alive”),但是这个方法在User-Provider中已经被注册过了所以会报重复“There is already ‘XXXX’ bean method”的错误!feign与hystrix会注册两次!这是个bug。只有去了才能使用。
下面写userback的代码:(实现下consumerApi,然后每个方法对应失败的措施)


@Component
public class userback implements ConsumerApi {
    @Override
    public Map<Integer, String> getMap(Integer id) {
        return null;
    }

    @Override
    public Map<Integer, String> getMap2(Integer id, String name) {
        return null;
    }

    @Override
    public Map<Integer, String> getMap3(Map<String, Object> map) {
        return null;
    }

    @Override
    public Map<Integer, String> postMap(Map<String, Object> map) {
        return null;
    }

    @Override
    public String alive() {
        return "降级了";
    }

    @Override
    public String getById(Integer id) {
        return null;
    }
}

上面调用alive时,失败会显示降级了。成功则显示port

在这里插入图片描述
失败:
在这里插入图片描述在这降级页面可以做兜底数据。(很关键)
返回有点数据,但是数据不是很新。

不同的错误而导致的异常,比如连接或者服务不可用。需要粒度更细。
这里是第二种方式:
comsumerapi用这样的注解方式:

@FeignClient(name = "user-provider",fallbackFactory = userproviderBackFactory.class)
public interface ConsumerApi extends UserApi {

这里的userproviderfactort并不是实现consumerapi,而是实现一个工厂类。


@Component
public class userproviderBackFactory implements FallbackFactory<ConsumerApi> {

    //这里的异常包含了本地的异常也包含了远端的异常
    @Override
    public ConsumerApi create(Throwable cause) {
        return new ConsumerApi() {
            @Override
            public Map<Integer, String> getMap(Integer id) {
                return null;
            }

            @Override
            public Map<Integer, String> getMap2(Integer id, String name) {
                return null;
            }

            @Override
            public Map<Integer, String> getMap3(Map<String, Object> map) {
                return null;
            }

            @Override
            public Map<Integer, String> postMap(Map<String, Object> map) {
                return null;
            }

            @Override
            public String alive() {
                System.out.println(cause);
                if(cause instanceof FeignException.InternalServerError){
                    return "远程服务器500";
                }
                cause.printStackTrace();
                System.out.println(ToStringBuilder.reflectionToString(cause));
                return "呵呵";
            }

            @Override
            public String getById(Integer id) {
                return null;
            }
        };
    }

这里提前挂差到了provier关掉的时候,会出现InternalServerError错误,在alive里面捕捉到了,返回远程服务器500.
provier方和上面第一种方法类似,故不赘述。
在这里插入图片描述

4、信号量隔离与线程隔离

Hystrix支持两种隔离模式,

默认情况下hystrix使用线程池控制请求隔离

线程池隔离技术,是用 Hystrix 自己的线程去执行调用;而信号量隔离技术,是直接让 tomcat 线程去调用依赖服务。信号量隔离,只是一道关卡,信号量有多少,就允许多少个 tomcat 线程通过它,然后去执行。

信号量隔离主要维护的是Tomcat的线程,不需要内部线程池,更加轻量级。

Hystrix针对具体的服务,线程池里给他分配线程数。(线程隔离)不至于给consumer拖死。一个服务的线程池挂了,不会影响其他服务的线程池。
在这里插入图片描述

下面是信号量隔离机制。
在这里插入图片描述
一个用户一个worker线程,这种时阻塞式web。
如果100个worker线程,那么有100用户可以执行。所以hystrix的线程池不能设置太低与太高。

信号量检查当前(一个)服务的信号量是否有余额。拿不到信号量直接fallback。信号量绑定的是tomcat的worker线程池里的线程。没有维护一个线程池,比较好。
在这里插入图片描述
那为什么要用线程池隔离?好处是有失败策略,可以异步请求,解放worker的线程阻塞,可以将异常线程池隔离。
算法速度快、代码逻辑健壮用信号量。一般web请求用线程池隔离。
使用,需要加依赖:

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

然后需要在consumer启动项上加注解:

@EnableHystrixDashboard

注意,这个dashbord是actuator,所以也需要actuator的依赖。

这里还需要properties加:

management.endpoints.web.exposure.include=*
hystrix.dashboard.proxy-stream-allow-list=*

`这里默认情况下是线程隔离,所以如果想用信号量隔离,添加properties:

hystrix.command.default.execution.isolation.strategy=SEMAPHORE

访问hystrix.stream:
在这里插入图片描述最后有个threadpool,是用的线程隔离方式。
还有个图形化的。
在这里插入图片描述
url则是actuator/hystrix.stream.进入后可以看到pool size为1.
在这里插入图片描述
访问多次pool size则会不断加。
在这里插入图片描述这里poolsize最大为10个。这是线程池做的隔离。

下面是信号量隔离的图。
在这里插入图片描述没有用线程池。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值