栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

微服务(五)Hystrix

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

微服务(五)Hystrix

微服务(五)Hystrix
  • 前言
  • 1、模拟hystrix
  • 2、Hystrix独立使用
  • 3、Hystrix整合RestTemplate
  • 3、Hystrix整合RestTemplate
  • 4、信号量隔离与线程隔离

前言

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"));
		
	//	System.out.println("result:" + hystrixTest.execute());
		
		
		Future 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 getMap(Integer id) {
        return null;
    }

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

    @Override
    public Map getMap3(Map map) {
        return null;
    }

    @Override
    public Map postMap(Map 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 {

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

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

            @Override
            public Map getMap3(Map map) {
                return null;
            }

            @Override
            public Map postMap(Map 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请求用线程池隔离。
使用,需要加依赖:

		
			org.springframework.cloud
			
				spring-cloud-starter-netflix-hystrix-dashboard
			
		
		
		    org.springframework.boot
		    spring-boot-starter-actuator
	

然后需要在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个。这是线程池做的隔离。

下面是信号量隔离的图。
没有用线程池。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1028023.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号