微服务架构spring cloud - 服务容错保护 Hystrix(四)

1.什么是服务容错保护

基于Netfix的开源框架Hystrix实现的,该框架的目标在于通过控制那些远程访问系统、服务、和第三方库的节点,从而对延迟和故障提供更强大的容错能力。Hystrix具备服务降级,服务熔断、线程和信号隔离、请求缓存、请求合并以及服务监控等强大功能

2.项目实战

打上注解的时候可以用@springCloudApplication注解,,可以看出一般spring cloud客户端都需要服务容错和eureka客户端这两大自动配置服务

//@EnableDiscoveryClient
//@EnableCircuitBreaker
//@SpringBootApplication
@SpringCloudApplication
public class DemoApplication {

    @Bean
    @LoadBalanced
    RestTemplate restTemplate(){

        return new RestTemplate();

    }

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

写上@HystrixComman注解,把备用方法的方法名写上去。如果线程阻塞或者节点无法访问都会触发备用方法

@Service
public class ConsumerService {

    @Autowired
    private RestTemplate restTemplate;

    @HystrixCommand(fallbackMethod = "helloError")
    public String helloConsumer(){

        return restTemplate.getForEntity("http://HELLO-SERVICE/hello",String.class).getBody();
    }

    public String helloError(){
        return "error";
    }
}

3.原理分析

1.创建HystrixCommand 或HystrixObervableCommand 对象

HystrixCommand :用在依赖的服务返回单个操作结果的时候

HystrixObervableCommand :用在依赖的服务返回多个操作结果的时候

使用的设计模式是命令模式

命令模式:将来自客户端的请求封装成一个对象,从而让你可以使用不同的请求对客户端进行参数化。它可以被用于实现”行为请求者“与”行为实现者“的解耦,以便使两者可以适应变化

意图:将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。

(个人理解:将请求封装成对象,可以使用多种不同的请求对象进行命令的请求)

  1. Receiver:接受者,真正的处理具体的业务逻辑
  2. Command:抽象命令,里面定义了一系列命令操作。当命令对象被调用时候就会去调用接受者
  3. ConcreteCommand:命令的具体实现类,在这里就是绑定了接受者和命令对象的关系,耦合在一起
  4. Invoke:调用者,它持有命令对象,通过发送命令,让接受者执行某些命令

2.命令执行

从hystrix在执行时候会创建command对象

HystrixCommand 实现下面的执行方式

execute() 同步执行 ,从依赖的服务返回一个单一的结果对象,或是在发生错误的时候抛出异常

扫描二维码关注公众号,回复: 2688836 查看本文章

queue() 异步执行,直接返回一个Future对象,其中包含了服务执行结束时要返回的的单一结果对象

HystrixObservableCommand

observe() 返回 Observable 对象,它代表了操作的多个结果,它是一个HotObervable

toObserve() 返回Obervable对象,也代表了操作的多个结果,但它返回的是一个ColdObservable

在Hystrix底层中大量使用的是Rxjava响应式编程,主要解决异步混乱问题。

HotObervable:无论事件源是否有订阅者,都会在创建事件后进行发布,每一个订阅者都可能是从事件源的中途开始的

ColdObservable:只有有订阅者才会发布事件,不然不会发布而一直等待

public R execute() {
        try {
            return this.queue().get();
        } catch (Exception var2) {
            throw Exceptions.sneakyThrow(this.decomposeException(var2));
        }
    }

    public Future<R> queue() {
        final Future<R> delegate = this.toObservable().toBlocking().toFuture();
        Future<R> f = new Future<R>() {
            public boolean cancel(boolean mayInterruptIfRunning) {
                if (delegate.isCancelled()) {
                    return false;
                } else {
                    if ((Boolean)HystrixCommand.this.getProperties().executionIsolationThreadInterruptOnFutureCancel().get()) {
                        HystrixCommand.this.interruptOnFutureCancel.compareAndSet(false, mayInterruptIfRunning);
                    }

                    boolean res = delegate.cancel(HystrixCommand.this.interruptOnFutureCancel.get());
                    if (!HystrixCommand.this.isExecutionComplete() && HystrixCommand.this.interruptOnFutureCancel.get()) {
                        Thread t = (Thread)HystrixCommand.this.executionThread.get();
                        if (t != null && !t.equals(Thread.currentThread())) {
                            t.interrupt();
                        }
                    }

                    return res;
                }
            }

            public boolean isCancelled() {
                return delegate.isCancelled();
            }

            public boolean isDone() {
                return delegate.isDone();
            }

            public R get() throws InterruptedException, ExecutionException {
                return delegate.get();
            }

            public R get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
                return delegate.get(timeout, unit);
            }
        };
        if (f.isDone()) {
            try {
                f.get();
                return f;
            } catch (Exception var6) {
                Throwable t = this.decomposeException(var6);
                if (t instanceof HystrixBadRequestException) {
                    return f;
                } else if (t instanceof HystrixRuntimeException) {
                    HystrixRuntimeException hre = (HystrixRuntimeException)t;
                    switch(hre.getFailureType()) {
                    case COMMAND_EXCEPTION:
                    case TIMEOUT:
                        return f;
                    default:
                        throw hre;
                    }
                } else {
                    throw Exceptions.sneakyThrow(t);
                }
            }
        } else {
            return f;
        }
    }

3.结果是否被缓存

缓存功能打开并且缓存命中,那么缓存的结果会以Observable对象返回

4.断路器是否打开

如果没有缓存结果,那么就表明没有Observable对象返回,所以接着继续判断断路器是否打开,如果打开了那么hystrix不会执行相应的命令而是直接跳到fallback里面执行备用方法。如果断路器没打开接着继续判断。

5.线程池/请求队列/信号量是否占满

如果三者之一已经占满,那么直接跳到fallback里面执行备用方法

6.HystrixObservableCommand.construt()或HystrixCommand.run()

HystrixObservableCommand.construt() :返回Observable对象发射多个结果

HystrixCommand.run() : 返回单一结果或者抛出异常

如果上述方法调用超时,那么直接跳到fallback方法。同时,如果当前命令没有被取消或者中断,那么他最终会忽略返回值。

如果命令调用成功没有异常且返回了结果,那么hystrix记录日志和监控报告后将结果返回

run情况下返回observale,并调用onComplete的结束通知

construt情况下直接返回observale

7.计算断路器的健康度

Hystrix会将成功、失败、拒绝、超时等信息报告给断路器,断路器会统计这组数据。如果数据不佳则会采用短路的方式架空服务调用,维持一段时间称为恢复期。当恢复期结束后,再次判断数据,如果还是不佳,则继续短路,否则恢复。

8.fallback处理

我们通常称此操作为服务降级,总的来说超时、线程池请求队列信号量占满、断路器打开都会引起服务降级

服务降级需要实现一个通用的响应结果,切记不能是网络资源。如果一定要是网络操作,则必须是级联的操作。如果级联成功则返回observale,否则会根据执行方法的不同而返回相应的东西

4.断路器原理

断路器首先从HystrixCircuitBreaker入手


public interface HystrixCircuitBreaker {
    boolean allowRequest();

    boolean isOpen();

    void markSuccess();

    void markNonSuccess();

    boolean attemptExecution();

    public static class NoOpCircuitBreaker implements HystrixCircuitBreaker {}

    public static class Factory {}

isopen() 判断断路器是否打开

由源码可以看出来,第一个判断条件是否强制打开,第二个判断条件是否强制关闭,第三个判断条件则是判断circuitOpened的大小,如果大于等于0直接返回true代表断路器打开

public boolean isOpen() {
            if ((Boolean)this.properties.circuitBreakerForceOpen().get()) {
                return true;
            } else if ((Boolean)this.properties.circuitBreakerForceClosed().get()) {
                return false;
            } else {
                return this.circuitOpened.get() >= 0L;
            }
        }
this.circuitOpened = new AtomicLong(-1L);
private Subscription subscribeToStream() {
            return this.metrics.getHealthCountsStream().observe().subscribe(new Subscriber<HealthCounts>() {
                public void onCompleted() {
                }

                public void onError(Throwable e) {
                }

                public void onNext(HealthCounts hc) {
                    if (hc.getTotalRequests() >= (long)(Integer)HystrixCircuitBreakerImpl.this.properties.circuitBreakerRequestVolumeThreshold().get() && hc.getErrorPercentage() >= (Integer)HystrixCircuitBreakerImpl.this.properties.circuitBreakerErrorThresholdPercentage().get() && HystrixCircuitBreakerImpl.this.status.compareAndSet(HystrixCircuitBreaker.HystrixCircuitBreakerImpl.Status.CLOSED, HystrixCircuitBreaker.HystrixCircuitBreakerImpl.Status.OPEN)) {
                        HystrixCircuitBreakerImpl.this.circuitOpened.set(System.currentTimeMillis());
                    }

                }
            });
        }

allowRequest() 判断请求是否被允许

前面条件是判断属性文件,最后面则是判断断路器是否有半打开的状态,也就是说还在不在恢复期,如果有这个条件,则去判断当前时间是不是已经过了恢复期,如果是则返回true

 public boolean allowRequest() {
            if ((Boolean)this.properties.circuitBreakerForceOpen().get()) {
                return false;
            } else if ((Boolean)this.properties.circuitBreakerForceClosed().get()) {
                return true;
            } else if (this.circuitOpened.get() == -1L) {
                return true;
            } else {
                return ((HystrixCircuitBreaker.HystrixCircuitBreakerImpl.Status)this.status.get()).equals(
HystrixCircuitBreaker.HystrixCircuitBreakerImpl.Status.HALF_OPEN) ? false : 
this.isAfterSleepWindow();
            }
        }
 private boolean isAfterSleepWindow() {
            long circuitOpenTime = this.circuitOpened.get();
            long currentTime = System.currentTimeMillis();
            long sleepWindowTime = (long)(Integer)this.properties.circuitBreakerSleepWindowInMilliseconds().get();
            return currentTime > circuitOpenTime + sleepWindowTime;
        }

markSuccess() 该函数用于在半开路状态时使用,若Hystrix命令调用成功,通过调用它将打开的断路器关闭,并重置circuitOpend对象的数值

public void markNonSuccess() {
            if (this.status.compareAndSet(HystrixCircuitBreaker.HystrixCircuitBreakerImpl.Status.H
ALF_OPEN, HystrixCircuitBreaker.HystrixCircuitBreakerImpl.Status.OPEN)) {
                this.circuitOpened.set(System.currentTimeMillis());
            }

        }

大致流程:

当HystrixCommand调用isopen()的时候,通过Metrics计算出调用失败率,failure/(success+failure)= % ,如果错误率大于阈值则返回true,并记录此次数据。

当HystrixCommand调用allowRequest()的时候,判断是否open,如果否则判断是否在恢复期,如果恢复期已经过去则请求允许通过

当HystrixCommand调用markSuccess(),如果open则关闭断路器,将数据通过Metrics记录

当HystrixCommand调用markFailure(),记录失败数据

5.依赖隔离

舱壁模式:Docker使用的就是此模式,容器与容器之间的隔离

Hystrix则用此模式实现线程池的隔离,为每一个服务依赖创建一个线程池,这样子每个线程池互不影响,互不霸占所有资源,当一个服务依赖故障时候不会影响其他服务依赖。

由于使用线程池会损耗一定的性能,所以又有了信号量来控制单个依赖服务的并发量,但是它不能设置超时和实现异步访问

命令执行:execution.isloation.strategy=SEMAPHORE,Hystrix会使用信号量替代线程池来控制依赖服务的并发

降级逻辑:当Hystrix尝试逻辑时,它会在调用线程中使用信号量

信号量的默认值为10,我们可以通过动态刷新的方式控制并发数量大小,性能达到5000rps(一秒的请求数量)可以将信号量设置为1-2

6.使用详解

创建请求命令

(1) 通过继承的方式来实现HystrixCommand

通过调用execute方法是同步方法 ,返回User

通过调用queque方法是异步方法,返回的是Future<T>对象,通过get方法可以获取结果

public class UserCommand extends HystrixCommand<User> {
    
    private RestTemplate restTemplate;
    private Long id;

    protected UserCommand(Setter setter,RestTemplate restTemplate,Long id) {
        super(setter);
        this.restTemplate=restTemplate;
        this.id=id;
    }

    @Override
    protected User run() throws Exception {
        return restTemplate.getForObject("http://USER-SERVICE/user/{1}",User.class,id);
    }
}

(2) 通过注解实现Hystrix命令

同步方法

@HystrixCommand(fallbackMethod = "helloError")
    public User helloConsumer(){
        return restTemplate.getForEntity("http://HELLO-SERVICE/hello",User.class).getBody();
    }

异步方法

@HystrixCommand
    public Future<User> getUserAsync(){
        return new AsyncResult<User>(){

            @Override
            public User invoke() {
                return restTemplate.getForEntity("http://HELLO-SERVICE/hello",User.class).getBody();
            }
        };
    }

虽然HystrixCommand具备oberve()和toObervable()方法,但是只能发射一次数据,所以hystrix还提供了HystrixObservableCommand

observe()

public class UserObservableCommand extends HystrixObservableCommand<User> {


    private RestTemplate restTemplate;
    private Long id;

    protected UserObservableCommand(Setter setter,RestTemplate restTemplate, Long id) {
        super(setter);
        this.restTemplate=restTemplate;
        this.id=id;
    }

    @Override
    protected Observable<User> construct() {

        return Observable.create(new Observable.OnSubscribe<User>() {
            @Override
            public void call(Subscriber<? super User> subscriber) {
                if(!subscriber.isUnsubscribed()){
                    User user=restTemplate.getForObject("http://USER-SERVICE/users{1}",User.class,id);
                    subscriber.onNext(user);
                    subscriber.onCompleted();
                }
            }
        });
    }
    
}

用注解实现

当要实现hot observe @HystrixCommand(observableExecutionMode = ObservableExecutionMode.EAGER)

当要实现cool observe @HystrixCommand(observableExecutionMode = ObservableExecutionMode.LAZY)

@HystrixCommand(observableExecutionMode = ObservableExecutionMode.EAGER)
    protected Observable<User> getUser() {

        return Observable.create(new Observable.OnSubscribe<User>() {

            @Override
            public void call(Subscriber<? super User> subscriber) {
                try {
                    if(!subscriber.isUnsubscribed()){
                        User user=restTemplate.getForObject("http://USER-SERVICE/users{1}",User.class,id);
                        subscriber.onNext(user);
                        subscriber.onCompleted();
                    }
                }catch (Exception e){
                    subscriber.onError(e);
                }

            }
        });
    }

下面demo从别处查看得来

地址:https://blog.csdn.net/liuchuanhong1/article/details/73293318

作者:牛奋lch

大致意思是执行了多条的rest资源请求,并将结果通过onnext()方法全部返回。

有需要的话需要学习rxjava了解一下 

HystrixObservableCommand和HystrixCommand进行比较:

  1. HystrixCommand提供了同步和异步两种执行方式,而HystrixObservableCommand只有异步方式
  2. HystrixCommand的run方法是用内部线程池的线程来执行的,而HystrixObservableCommand则是由调用方(例如Tomcat容器)的线程来执行的,因为是异步,所以两种方式都能很好的起到资源隔离的效果。
  3. HystrixCommand一次只能发送单条数据返回,而HystrixObservableCommand一次可以发送多条数据返回.
if (!observer.isUnsubscribed()) {
		for (String id : ids) {
			CloseableHttpClient client = HttpClients.createDefault();
			HttpGet get = new HttpGet("http://localhost:7901/user/" + id);
			CloseableHttpResponse response = client.execute(get);
			HttpEntity entity = response.getEntity();
			String body = EntityUtils.toString(entity);
			ObjectMapper mapper = ObjectMapperInstance.getInstance();
			User u = mapper.readValue(body, User.class);
//			TimeUnit.SECONDS.sleep(3);
			observer.onNext(u);
		}
		observer.onCompleted();
	}
@RequestMapping("/users/{ids}")
	public String getAll(@PathVariable("ids") String ids){
		
		List<User> list = new ArrayList<User>();
		UserHystrixObservableCommand observableCommand = new 
UserHystrixObservableCommand(ids.split("-"));
		Observable<User> observe = observableCommand.observe();
		observe.subscribe(new Observer<User>() {
 
			@Override
			public void onCompleted() {
				System.out.println("聚合完了所有的查询请求!");
				System.out.println(list);
			}
 
			@Override
			public void onError(Throwable t) {
				t.printStackTrace();
			}
 
			@Override
			public void onNext(User user) {
				list.add(user);
			}
		});
		return "success";
	}

定义服务降级

HystrixCommand:也就是我们常说的fallback方法,通过重写getFallback() 方法来写服务降级方法

 @Override
    protected User getFallback() {
        return new User();
    }

HystrixObservableCommand:也就是我们常说的fallback方法,通过重写resumeWithFallback() 方法来写服务降级方法 

@Override
    protected Observable<User> resumeWithFallback() {
        return Observable.create(new Observable.OnSubscribe<User>() {
            @Override
            public void call(Subscriber<? super User> subscriber) {
                if(!subscriber.isUnsubscribed()){
                    User user=new User();
                    subscriber.onNext(user);
                    subscriber.onCompleted();
                }
            }
        });
    }

使用注解的方式来定义服务降级逻辑

@HystrixCommand(fallbackMethod = "helloError")
    public User helloConsumer(){
        return restTemplate.getForEntity("http://HELLO-SERVICE/hello",User.class).getBody();
    }

    public String helloError(){
        return "error";
    }

当发现备用方法也是一个不稳定的方法的时候也可以加上@HystrixCommand注解以生成Hystrix命令

 @HystrixCommand(fallbackMethod = "helloError")
    public User helloConsumer(){
        return restTemplate.getForEntity("http://HELLO-SERVICE/hello",User.class).getBody();
    }

    @HystrixCommand(fallbackMethod = "helloErrorTwo")
    public User helloError(){
        return restTemplate.getForEntity("http://HELLO-SERVICE/hello",User.class).getBody();
    }

    public String helloErrorTwo(){
        return "error";
    }

需要注意的是:在使用注解开发的时候,备用方法必须在同一个类中,又因为是同一个类,不管限定符是什么都可以调用这个备用方法

特殊情况:

1.执行文件的写操作,当写失败时候只需要通知调用者即可,因为写操作一般占据不少时间。

2.执行批处理或离线计算的命令:当失败的时候告诉调用者重试即可,不用去进行备用方法的调用

异常处理

由于除了HystrixBadRequestException之外,其他异常都会被判定为方法调用失败而执行备用方法。如果我们想指定异常不被判定为方法调用故障,那么如下所示

@HystrixCommand(ignoreExceptions = {BadRequestException.class},fallbackMethod = "helloError")
    public User helloConsumer(){
        return restTemplate.getForEntity("http://HELLO-SERVICE/hello",User.class).getBody();
    }

如果是想异常获取的话,有两种情况,一种是在继承的时候直接调用getExecutionException()方法,第二种是直接绑定到形参上就可以了。

@Override
    protected User getFallback() {
        getExecutionException();
        return new User();
    }
 @HystrixCommand(fallbackMethod = "helloErrorTwo")
    public User helloError(String id){
        return restTemplate.getForEntity("http://HELLO-SERVICE/hello",User.class).getBody();
    }

    public String helloErrorTwo(String id,Throwable throwable){
        return "error";
    }

命令名称、分组以及线程划分

GroupKey是一个必选的参数,因为Hystrix会根据组来组织和统计命令的的告警、仪表盘 等信息,且会根据相同组名来实现同一个线程池

protected UserCommand(Setter setter,RestTemplate restTemplate,Long id) {
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("myGroup"))
.andCommandKey(HystrixCommandKey.Factory.asKey("hello"))
);
        this.restTemplate=restTemplate;
        this.id=id;
    }

为了进一步的更加细粒度的划分,最好使用HystrixThreadPoolkey的方式来指定线程池的划分,而不是通过组名来划分

 protected UserCommand(Setter setter,RestTemplate restTemplate,Long id) {
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("myGroup")).andCommandKey(HystrixCommandKey.Factory.asKey("hello"))
                .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("thread")));
        this.restTemplate=restTemplate;
        this.id=id;
    }

使用注解方式实现

@HystrixCommand(commandKey = "helloError",groupKey = "UserGroup",threadPoolKey="getUserById")
    public User helloError(String id){
        return restTemplate.getForEntity("http://HELLO-SERVICE/hello",User.class).getBody();
    }

请求缓存

由于使用http调用依赖服务相比于进程调用,损耗是非常大的,所以我们可以仿照数据缓存来进行请求缓存

在使用继承HystrixCommand或者HystrixObservableCommand时,通过重载getCacheKey()方法来开启请求缓存

缓存有点:

减少了请求次数,降低了依赖服务的并发度

由于缓存,相同的依赖服务数据相同

缓存会在run()和construt()调用之前生效,所以会减少不必要的线程开销


    @Override
    protected String getCacheKey() {
        return String.valueOf(id);
    }

清理失效缓存功能

如果请求的是更新操作,那么应该及时清理缓存,防止读取到脏数据

在HystrixCommand中可以通过HystrixRequestCache.clear();

里面的构造函数有两个,第一个就是我们之前设置的命令名称,第二个就是获取并发策略实例

@Override
    protected User run() throws Exception {
        //return restTemplate.getForObject("http://USER-SERVICE/user/{1}",User.class,id);
        User u= restTemplate.postForObject("http://USER-SERVICE/users",user,User.class);
        //清理缓存
        HystrixRequestCache.getInstance(HystrixCommandKey.Factory.asKey("hello"),
                HystrixConcurrencyStrategyDefault.getInstance()).clear(String.valueOf(u.getId()));
        return u;
    }

缓存工作原理解析

主要位于AbstractCommand的toObervable()方法中

尝试获取请求缓存:判断是否开启了缓存,如果开启了则通过cachekey去获取HystrixCachedObservable对象

 boolean requestCacheEnabled = AbstractCommand.this.isRequestCachingEnabled();
                    String cacheKey = AbstractCommand.this.getCacheKey();
                    if (requestCacheEnabled) {
                        HystrixCommandResponseFromCache<R> fromCachex = (HystrixCommandResponseFromCache)AbstractCommand.this.requestCache.get(cacheKey);
                        if (fromCachex != null) {
                            AbstractCommand.this.isResponseFromCache = true;
                            return AbstractCommand.this.handleRequestCacheHitAndEmitValues(fromCachex, AbstractCommand.this);
                        }
                    }

将请求结果加入缓存:

先获得一个延迟执行的结果对象hystrixObservable,如果开启了缓存对象,继续包装成缓存对象HystrixCachedObervable,变量名为tocache,通过putifAbsent将数据放到线程安全的map中,并且如果当前的fromCache不为null,则取消订阅操作就是不发送请求。

并且需要知道putIfAbsent是如果有数据则使用原先数据,没有的话才会put新数据。所以实际上已经记录了缓存,最后如果!null则直接调用缓存返回结果,如果为null则执行toObervable方法返回Observable对象

Observable<R> hystrixObservable = Observable.defer(applyHystrixSemantics).map(wrapWithAllOnNextHooks);
                    Observable afterCache;
                    if (requestCacheEnabled && cacheKey != null) {
                        HystrixCachedObservable<R> toCache = HystrixCachedObservable.from(hystrixObservable, AbstractCommand.this);
                        HystrixCommandResponseFromCache<R> fromCache = (HystrixCommandResponseFromCache)AbstractCommand.this.requestCache.putIfAbsent(cacheKey, toCache);
                        if (fromCache != null) {
                            toCache.unsubscribe();
                            AbstractCommand.this.isResponseFromCache = true;
                            return AbstractCommand.this.handleRequestCacheHitAndEmitValues(fromCache, AbstractCommand.this);
                        }

                        afterCache = toCache.toObservable();
                    } else {
                        afterCache = hystrixObservable;
                    }
 <T> HystrixCachedObservable<T> putIfAbsent(String cacheKey, HystrixCachedObservable<T> f) {
        HystrixRequestCache.ValueCacheKey key = this.getRequestCacheKey(cacheKey);
        if (key != null) {
            ConcurrentHashMap<HystrixRequestCache.ValueCacheKey, HystrixCachedObservable<?>> cacheInstance = (ConcurrentHashMap)requestVariableForCache.get(this.concurrencyStrategy);
            if (cacheInstance == null) {
                throw new IllegalStateException("Request caching is not available.  Maybe you need to initialize the HystrixRequestContext?");
            }

            HystrixCachedObservable<T> alreadySet = (HystrixCachedObservable)cacheInstance.putIfAbsent(key, f);
            if (alreadySet != null) {
                return alreadySet;
            }
        }

        return null;
    }

使用注解实现请求缓存

@CacheResult

通过此注解将会缓存返回的对象,而key值则是方法上面的参数

@CacheResult
    @HystrixCommand(ignoreExceptions = {BadRequestException.class},fallbackMethod = "helloError")
    public User helloConsumer(Long id){
        return restTemplate.getForEntity("http://HELLO-SERVICE/hello",User.class).getBody();
    }

@CacheResult(cacheKeyMethod="xxx")

通过此注解自定义需要缓存的数据的key值,类似于在使用继承方法的时候覆盖的缓存方法,括号里面写的是方法名

@CacheResult(cacheKeyMethod = "getUserByIdCacheKey")
    @HystrixCommand(ignoreExceptions = {BadRequestException.class},fallbackMethod = "helloError")
    public User helloConsumer(Long id){
        return restTemplate.getForEntity("http://HELLO-SERVICE/hello",User.class).getBody();
    }
    
    private Long getUserByIdCacheKey(Long id){
        return id;
    }

@CacheKey

此注解的作用同上边注解类似,唯一区别就是优先级的问题,此注解优先级较低,如果两种注解同时存在,那么此注解将会被忽略.顺带说明此注解还可以拿内部属性值

 @HystrixCommand(ignoreExceptions = {BadRequestException.class},fallbackMethod = "helloError")
    public User helloConsumer(@CacheKey Long id){
        return restTemplate.getForEntity("http://HELLO-SERVICE/hello",User.class).getBody();
    }
@HystrixCommand(ignoreExceptions = {BadRequestException.class},fallbackMethod = "helloError")
    public User helloConsumer(@CacheKey("id") User user){
        return restTemplate.getForEntity("http://HELLO-SERVICE/hello",User.class).getBody();
    }

@CacheRemove

这个注解与@CacheResult注解是相对的,目的是清楚缓存,所以也同上方的继承类一样,需要指定commankey来找到正确的请求命令缓存位置,并且通过指定key删除缓存.

注意:commandkey 是你在缓存的时候的commandkey

 @CacheRemove(commandKey = "helloError")
    @HystrixCommand(ignoreExceptions = {BadRequestException.class},fallbackMethod = "helloError")
    public User helloConsumer(@CacheKey("id") User user){
        return restTemplate.getForEntity("http://HELLO-SERVICE/hello",User.class).getBody();
    }

    @CacheResult
    @HystrixCommand(commandKey = "helloError",groupKey = "UserGroup",threadPoolKey="getUserById")
    public User helloError(@Cachekey String id){
        return restTemplate.getForEntity("http://HELLO-SERVICE/hello",User.class).getBody();
    }

请求合并

目的是为了将在一定时间范围内多个相同的请求合并在一起,通过一次访问的方式来解决。可以减少并发量,但是要考虑是否需要使用请求合并,如果访问少,请求快速的,应该不使用,不然只会拖延请求时间。

合并器的三个泛型参数是分别是 

  1. 合并请求后的返回类型
  2. 单个请求返回类型
  3. 单个请求参数类型
  • 首先使用构造器初始化配置,设置时间窗,并且设置了合并器命令名称
  • 通过getRequestArgument返回请求参数
  • 然后创建命令,将所有的请求数据拿出来,通过流的操作获取所有的请求参数。
  • 最后通过mapResponse返回所有请求的对象,也就是将合并请求的所有数据拆分后分发到每个线程

大概流程

首先服务提供者

public class UserCollapseCommand extends HystrixCollapser<List<User>,User,Long> {
    private UserService userService;
    private Long userId;

    public UserCollapseCommand(UserService userService, Long userId) {
        super(Setter.withCollapserKey(HystrixCollapserKey.Factory.asKey("userCollapseCommand")).andCollapserPropertiesDefaults(
                HystrixCollapserProperties.Setter().withTimerDelayInMilliseconds(100)
        ));
        this.userService = userService;
        this.userId = userId;
    }

    @Override
    public Long getRequestArgument() {
        return userId;
    }

    @Override
    protected HystrixCommand<List<User>> createCommand(Collection<CollapsedRequest<User, Long>> collection) {
        List<Long> userIds=new ArrayList<>(collection.size());
        userIds.addAll(collection.stream().map(CollapsedRequest::getArgument).collect(Collectors.toList()));

        return new UserBatchCommand(userService,userIds);
    }

    @Override
    protected void mapResponseToRequests(List<User> users, Collection<CollapsedRequest<User, Long>> collection) {
        int count=0;
        for(CollapsedRequest<User,Long> collapsedRequest:collection){
            User user=users.get(count++);
            collapsedRequest.setResponse(user);
        }
    }



}
@RestController
public class ConsumerController {



    @Autowired
    private  RestTemplate restTemplate;

    @RequestMapping("/consumer")
    public String helloConsumer() throws ExecutionException, InterruptedException {


        //请求合并
        HystrixRequestContext context = HystrixRequestContext.initializeContext();
        HjcBatchCommand command = new HjcBatchCommand(restTemplate,1L);
        HjcBatchCommand command1 = new HjcBatchCommand(restTemplate,2L);
        HjcBatchCommand command2 = new HjcBatchCommand(restTemplate,3L);

        //这里你必须要异步,因为同步是一个请求完成后,另外的请求才能继续执行,所以必须要异步才能请求合并
        Future<String> future = command.queue();
        Future<String> future1 = command1.queue();

        String r = future.get();
        String r1 = future1.get();

        Thread.sleep(2000);
        //可以看到前面两条命令会合并,最后一条会单独,因为睡了2000毫秒,而你请求设置要求在200毫秒内才合并的。
        Future<String> future2 = command2.queue();
        String r2 = future2.get();

        System.out.println(r);
        System.out.println(r1);
        System.out.println(r2);

        context.close();

        return null;

    }

}
复制代码

使用注解实现请求合并器

在Controller只要调用相应的方法即可,请求合并器会拦截请求并且将请求放到队列中等待一并发送

@Service("peopleService")
public class PeopleServiceImpl implements PeopleService {
    @Autowired
    private RestTemplate restTemplate;

    @HystrixCollapser(batchMethod = "findAll",
            collapserProperties = {@HystrixProperty(name = "timerDelayInMilliseconds", value = "100")})
    public Future<User> find(Long id) {
        throw new RuntimeException("This method body should not be executed");
    }

    @HystrixCommand
    public List<User> findAll(List<Long> ids) {
        System.out.println("Annotation---------" + ids + "Thread.currentThread().getName():" + Thread.currentThread().getName());
        User[] users = restTemplate.getForObject("http://localhost:8762/users?ids={1}", User[].class, StringUtils.join(ids, ","));
        return Arrays.asList(users);
    }

}

7.属性详解

首相讲解四大属性设置的优先级,从低到高

  • 全局默认值:也就是默认值
  • 全局默认属性:通过在配置文件中定义全局属性值,可以通过动态刷新的方式更新配置
  • 实例默认值:通过代码为实例定义全局属性值
  • 实例配置属性:通过配置文件为指定实例进行属性配置

Command属性

execution:控制HystrixCommand.run() 的执行

execution.isolation.strategy来设置执行的隔离策略,参数值有两种

  • THREAD:线程池隔离策略。并发限制受线程数量影响
  • SEMAPHORE:通过信号量分离策略。并发限制受信号量的影响。效率高到时不知道超时和异步请求

书写方式

  • 全局默认值:THREAD
  • 全局默认属性:hystrix.command.default.execution.isolation.strategy
  • 实例默认值
  • 继承HystrixCommandProperties.Setter().withExecutionIsoIationStrategy(ExecutionisoIationStrategy.THREAD)
  • 注解:@HystrixProperty(name="execution.isolation.strategy" ,value="THREAD")
  • 实例配置属性:hystrix.command.HystrixCommondKey.execution.isolation.strategy

execution.isolation.thread.timeoutInMilliseconds设置HystrixCommand执行超时时间,单位为毫秒

  • 全局默认值:1000
  • 全局默认属性:hystrix.command.default.execution.isolation.thread.timeoutInMillseconds
  • 实例默认值
  • 继承HystrixCommandProperties.Setter().withExecutionTimeoutInMillseconds(1000)
  • 注解:@HystrixProperty(name="execution.thread.timeoutnMillseconds" ,value="1000")
  • 实例配置属性:hystrix.command.HystrixCommondKey.execution.isolation.thread.timeoutInMillseconds

execution.timeout.enabled设置HystrixCommand是否启用超时时间

  • 全局默认值:true
  • 全局默认属性:hystrix.command.default.execution.timeout.enabled
  • 实例默认值
  • 继承HystrixCommandProperties.Setter().withExecutionTimeoutEnabled(true)
  • 注解:@HystrixProperty(name="execution.timeout.enabled" ,value="1000")
  • 实例配置属性:hystrix.command.HystrixCommondKey.execution.timeout.enabled

execution.isolation.thread.interruptOnTimeOut设置HystrixCommand.run()执行超时的时候是否要中断

  • 全局默认值:true
  • 全局默认属性:hystrix.command.default.execution.isolation.thread.interruptOnTimeout
  • 实例默认值
  • 继承HystrixCommandProperties.Setter().withExecutionInsolationThreadInterruptOnTimeout(true)
  • 注解:@HystrixProperty(name="execution.isolation.thread.interruptOnTimeOut" ,value="true")
  • 实例配置属性:hystrix.command.HystrixCommondKey.execution.isolation.thread.interrruptOnTimeOut

execution.isolation.thread.interruptOnTimeCancle设置HystrixCommand.run()执行取消的时候是否要中断

  • 全局默认值:true
  • 全局默认属性:hystrix.command.default.execution.isolation.thread.interruptOnCancle
  • 实例默认值
  • 继承HystrixCommandProperties.Setter().withExecutionInsolationThreadInterruptOnCancletrue)
  • 注解:@HystrixProperty(name="execution.isolation.thread.interruptOnCancle" ,value="true")
  • 实例配置属性:hystrix.command.HystrixCommondKey.execution.isolation.thread.interruptOnCancle

execution.isolation.semaphore.maxConcurrentRequests当HystrixCommand的隔离策越使用信号量的时候,用来设置信号量的大小,也就是并发请求的最大值

  • 全局默认值:10
  • 全局默认属性:hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests
  • 实例默认值
  • 继承HystrixCommandProperties.Setter().withExecutionInsolationSemaphoreMaxConcurrentRequests)
  • 注解:@HystrixProperty(name="execution.isolation.semaphore.maxConcurrentRequests" ,value="10")
  • 实例配置属性:hystrix.command.HystrixCommondKey.execution.isolation.semaphore.maxConcurrentRequests

fallback配置

控制HystrixCommand.getFallback()执行。这些属性适用于线程池的信号量的隔离策略

fallback.isolation.semaphore.maxConcurrentRequest设置HystrixCommand.getFallback()执行的最大并发请求数

  • 全局默认值:10
  • 全局默认属性:hystrix.command.default.fallback.isolation.semaphore.interruptOnCancle
  • 实例默认值
  • 继承HystrixCommandProperties.Setter().withFallbackIsolationSemaphoreMaxConcurrentRequests(10)
  • 注解:@HystrixProperty(name="fallback.isolation.semaphore.maxConcurrentRequests" ,value="10")
  • 实例配置属性:hystrix.command.HystrixCommondKey.fallback.isolation.semaphore.maxConcurrentRequests

fallback.enabled设置HystrixCommand.getFallback()执行的时候是否开启服务降级逻辑

  • 全局默认值:true
  • 全局默认属性:hystrix.command.default.fallback.enabled
  • 实例默认值
  • 继承HystrixCommandProperties.Setter().withFallbackEnabled(true)
  • 注解:@HystrixProperty(name="fallback.isolation.semaphore.maxConcurrentRequests" ,value="10")
  • 实例配置属性:hystrix.command.HystrixCommondKey.fallback.enabled

circuitBreaker配置

该属性是断路器的属性配置,用来控制HystrixCircuitBreaker的行为

circuitBreaker.enabled:该属性用来确定当服务请求命令失败时,是否使用断路器来跟踪其健康指标和熔断请求

  • 全局默认值:true
  • 全局默认属性:hystrix.command.default.circuitBreaker.enabled
  • 实例默认值
  • 继承HystrixCommandProperties.Setter().withCircuitBreakerEnabled(true)
  • 注解:@HystrixProperty(name="circuitBreaker.enabled" ,value="false")
  • 实例配置属性:hystrix.command.HystrixCommondKey.circuitBreaker.enabled

circuitBreaker.requestVolumeThreshold:该属性用来设置在滚动时间窗口中,断路器熔断之前的最小请求数,例如设置为20,就算失败了19次也不会打开

  • 全局默认值:20
  • 全局默认属性:hystrix.command.default.circuitBreaker.requestVolumeThreshold
  • 实例默认值
  • 继承HystrixCommandProperties.Setter().withCircuitBreakerRequestVolumeThreshold(true)
  • 注解:@HystrixProperty(name="circuitBreaker.requestVolumeThreshold" ,value="false")
  • 实例配置属性:hystrix.command.HystrixCommondKey.circuitBreaker.requestVolumeThreshold

circuitBreaker.sleepWindowInMillisceonds:该属性用来设置当断路器打开之后的休眠时间窗。休眠时间窗结束之后,会将断路器置为半开状态,如果再次访问熔断请求,如果还是失败则再次关闭

  • 全局默认值:5000
  • 全局默认属性:hystrix.command.default.circuitBreaker.sleepWindowInMillseconds
  • 实例默认值
  • 继承HystrixCommandProperties.Setter().sleepWindowInMillseconds(5000)
  • 注解:@HystrixProperty(name="circuitBreaker.sleepWindowInMillseconds" ,value="5000")
  • 实例配置属性:hystrix.command.HystrixCommondKey.circuitBreaker.sleepWindowInMillseconds

circuitBreaker.errorThresholdPercentage:该属性用来设置断路器打开的错误百分比,若请求数量超过了阈值20且失败率百分之50,则把熔断器设置为打开状态

  • 全局默认值:5000
  • 全局默认属性:hystrix.command.default.circuitBreaker.errorThresholdPercentage
  • 实例默认值
  • 继承HystrixCommandProperties.Setter().errorThresholdPercentage(5000)
  • 注解:@HystrixProperty(name="circuitBreaker.errorThresholdPercentage" ,value="5000")
  • 实例配置属性:hystrix.command.HystrixCommondKey.circuitBreaker.errorThresholdPercentage

circuitBreaker.forceOpen:强制打开断路器,优先级大于强制关闭断路器(从源代码可知)

  • 全局默认值:false
  • 全局默认属性:hystrix.command.default.circuitBreaker.forceOpen
  • 实例默认值
  • 继承HystrixCommandProperties.Setter().forceOpen(false)
  • 注解:@HystrixProperty(name="circuitBreaker.forceOpen" ,value="false")
  • 实例配置属性:hystrix.command.HystrixCommondKey.circuitBreaker.forceOpen

circuitBreaker.forceClosed:强制关闭断路器,优先级小于强制打开断路器(从源代码可知)

  • 全局默认值:false
  • 全局默认属性:hystrix.command.default.circuitBreaker.forceClosed
  • 实例默认值
  • 继承HystrixCommandProperties.Setter().forceClosed(false)
  • 注解:@HystrixProperty(name="circuitBreaker.forceClosed" ,value="false")
  • 实例配置属性:hystrix.command.HystrixCommondKey.circuitBreaker.forceClosed

metrics配置

HystrixCommand和HystrixObservableCommand在运行过程中的指标信息

metrics.rollingStats.timeInMilliseconds 该属性用来设置时间长度,用于断路器判断健康度时需要收集信息的持续时间。断路器也会根据这个时间长度拆分成多个桶,例如默认值10000,则每个桶记1000(动态刷新不更新)

  • 全局默认值:10000
  • 全局默认属性:hystrix.command.default.metrics.rollingStats.timeInMilliseconds
  • 实例默认值
  • 继承HystrixCommandProperties.Setter().withMetricsRollingStatisticalWindowInMilliseconds(10000)
  • 注解:@HystrixProperty(name="metrics.rollingStats.timeInMilliseconds" ,value="10000")
  • 实例配置属性:hystrix.command.HystrixCommondKey.metrics.rollingStats.timeInMilliseconds

metrics.rollingStats.numBuckets 该属性就是用来划分上方刚刚所说的桶的数量,需要注意的是如果参数不能整除则会抛异常而且动态刷新不会生效,防止检测数据丢失

  • 全局默认值:10
  • 全局默认属性:hystrix.command.default.metrics.rollingStats.numBuckets
  • 实例默认值
  • 继承HystrixCommandProperties.Setter().withMetricsRollingStatisticalWindowBuckets(10)
  • 注解:@HystrixProperty(name="metrics.rollingStats.numBuckets" ,value="20")
  • 实例配置属性:hystrix.command.HystrixCommondKey.metrics.rollingStats.numBuckets

metrics.rollingPercentile.enabled该属性用来设置对命令的延迟是否使用百分数来计算,如果设置为false返回-1。通俗的话来说就是开启计算请求命令的耗时时间

  • 全局默认值:true
  • 全局默认属性:hystrix.command.default.metrics.rollingPercentile.enabled
  • 实例默认值
  • 继承HystrixCommandProperties.Setter().withMetricsRollingPercentileEnabled(true)
  • 注解:@HystrixProperty(name="metrics.rollingPercentile.enabled" ,value="false")
  • 实例配置属性:hystrix.command.HystrixCommondKey.metrics.rollingPercentile.numBuckets

metrics.rollingPercentile.timeInMilliseconds该属性用来设置百分位统计的滚动窗口的使用时间(动态刷新无效),设置执行时间在滚动窗口中保留时间,用来计算百分比。通俗来说就是把数据维持在60秒以内

  • 全局默认值:60000
  • 全局默认属性:hystrix.command.default.metrics.rollingPercentile.timeInMilliseconds
  • 实例默认值
  • 继承HystrixCommandProperties.Setter().withMetricsRollingPercentileWindowInMillSeconds(60000)
  • 注解:@HystrixProperty(name="metrics.rollingPercentile.timeInMillseconds" ,value="60000")
  • 实例配置属性:hystrix.command.HystrixCommondKey.metrics.rollingPercentile.numBuckets

metrics.rollingPercentile.numBuckets该属性用来设置百分位统计中窗口使用的桶的数量

  • 全局默认值:6
  • 全局默认属性:hystrix.command.default.metrics.rollingPercentile.numBuckets
  • 实例默认值
  • 继承HystrixCommandProperties.Setter().withMetricsRollingPercentileWindowBuckets(6)
  • 注解:@HystrixProperty(name="metrics.rollingPercentile.numBuckets" ,value="5")
  • 实例配置属性:hystrix.command.HystrixCommondKey.metrics.rollingPercentile.numBuckets

metrics.rollingPercentile.buckSize该属性用来设置桶中保留的最大执行次数,如果设置为100,就算一个桶中执行了500次也只保留最近100次的数据

  • 全局默认值:100
  • 全局默认属性:hystrix.command.default.metrics.rollingPercentile.bucketSize
  • 实例默认值
  • 继承HystrixCommandProperties.Setter().withMetricsRollingPercentileBucketSize(100)
  • 注解:@HystrixProperty(name="metrics.rollingPercentile.bucketSize" ,value="5")
  • 实例配置属性:hystrix.command.HystrixCommondKey.metrics.rollingPercentile.bucketSize

metrics.healthSnapshot.intervalInMillseconds该属性用来设置采集影响断路器状态的健康快照(请求的成功、错误的百分比)的间隔等待时间

  • 全局默认值:500
  • 全局默认属性:hystrix.command.default.metrics.healthSnapshot.intervalInMillseconds
  • 实例默认值
  • 继承HystrixCommandProperties.Setter().withMetricsRollingHealthSnapshotIntervalMilliseconds(500)
  • 注解:@HystrixProperty(name="metrics.healthSnapshot.intervalInMillseconds" ,value="500")
  • 实例配置属性:hystrix.command.HystrixCommondKey.metrics.healthSnapshot.intervalInMillseconds

requestContext配置

涉及HystrixCommand使用的HystrixRequestContext的设置

requestCache.enabled此属性设置是否开启缓存

  • 全局默认值:true
  • 全局默认属性:hystrix.command.default.requestCache.enabled
  • 实例默认值
  • 继承HystrixCommandProperties.Setter().withRequestCacheEnabled(true)
  • 注解:@HystrixProperty(name="requestCache.enabled" ,value="true")
  • 实例配置属性:hystrix.command.HystrixCommondKey.requestCache.enabled

requestLog.enabled是否将HystrixCommand的执行和事件是否打印到HystrixRequestLog中

  • 全局默认值:true
  • 全局默认属性:hystrix.command.default.requestLoge.enabled
  • 实例默认值
  • 继承HystrixCommandProperties.Setter().withRequestLogeEnabled(true)
  • 注解:@HystrixProperty(name="requestLoge.enabled" ,value="true")
  • 实例配置属性:hystrix.command.HystrixCommondKey.Loge.enabled

collapser配置

控制命令合并的相关行为

collapserProperties={@HystrixProperty(name="xxxx",value="20" )}

maxRequestsInBatch该参数用来设置一次请求合并批处理中允许的最大请求数

  • 全局默认值:Integer.MAX_VALUE
  • 全局默认属性:hystrix.collapser.default.maxRequestInBatch
  • 实例默认值
  • 继承HystrixCollapserProperties.Setter().withMaxRequestInBatch(100)
  • 注解:@HystrixProperty(name="maxRequestInBatch" ,value="100")
  • 实例配置属性:hystrix.collapser.HystrixCommondKey.maxRequestInBatch

timerDelayInMilliseconds该参数用来设置批处理过程中每个命令的延迟时间,单位为毫秒

  • 全局默认值:10
  • 全局默认属性:hystrix.collapser.default.timeDeleayInMillseconds
  • 实例默认值
  • 继承HystrixCollapserProperties.Setter().withTtmeDeleayInMillseconds(10)
  • 注解:@HystrixProperty(name="timeDeleayInMillseconds" ,value="10")
  • 实例配置属性:hystrix.collapser.HystrixCommondKey.timeDeleayInMillseconds

requestCache.enabled该参数用来设置批处理过程中请求的缓存

  • 全局默认值:true
  • 全局默认属性:hystrix.collapser.default.requestCache.enabled
  • 实例默认值
  • 继承HystrixCollapserProperties.Setter().withRequestCacheEnabled(true)
  • 注解:@HystrixProperty(name="requestCache.enabled" ,value="true")
  • 实例配置属性:hystrix.command.HystrixCommondKey.requestCache.enabled

threadPool属性

控制Hystrix命令所属的线程池的配置

@HystrixCommand(fallbackMethod="helloFallback",commanKey="helloKey"

,threadPoolProperties={name="coreSize",value="20"})

coreSize:该参数用来设置执行线程池的核心线程数,命令执行的最大线程数

  • 全局默认值:10
  • 全局默认属性:hystrix.threadpool.default.coreSize
  • 实例默认值
  • 继承HystrixThreadPoolProperties.Setter().withCoreSize(true)
  • 注解:@HystrixProperty(name="coreSize" ,value="10")
  • 实例配置属性:hystrix.threadPool.HystrixThreadPoolKey.coreSize

maxQueueSize:该参数用来设置执行线程的最大队列大小,当设置为-1时,线程池将使用SynchronousQueue实现的队列,否则使用将使用的LinkedBlockingQueue实现的队列

  • 全局默认值:-1
  • 全局默认属性:hystrix.threadpool.default.maxQueueSize
  • 实例默认值
  • 继承HystrixThreadPoolProperties.Setter().withMaxQueueSize(-1)
  • 注解:@HystrixProperty(name="maxQueueSize" ,value="-1")
  • 实例配置属性:hystrix.threadPool.HystrixThreadPoolKey.maxQueueSize

queueSizeRejectionThreshold : 设置线程的最大阈值,就算队列没有达到最大值也能拒绝请求

  • 全局默认值:5
  • 全局默认属性:hystrix.threadpool.default.queueSizeRejectionThreshold
  • 实例默认值
  • 继承HystrixThreadPoolProperties.Setter().withQueueSizeRejectionThreshold(5)
  • 注解:@HystrixProperty(name="queueSizeRejectionThreshold" ,value="5")
  • 实例配置属性:hystrix.threadPool.HystrixThreadPoolKey.queueSizeRejectionThreshold

metrics.rollingStats.timeInMilliseconds 该属性用来设置时间长度,用于线程池的指标度量

  • 全局默认值:10000
  • 全局默认属性:hystrix.threadpool.default.metrics.rollingStats.timeInMilliseconds
  • 实例默认值
  • 继承HystrixCommandProperties.Setter().withMetricsRollingStatisticalWindowInMilliseconds(10000)
  • 注解:@HystrixProperty(name="metrics.rollingStats.timeInMilliseconds" ,value="10000")
  • 实例配置属性:hystrix.threadpool.HystrixThreadPoolKey.metrics.rollingStats.timeInMilliseconds

metrics.rollingStats.numBuckets 该属性就是用来划分上方刚刚所说的桶的数量,需要注意的是如果参数不能整除则会抛异常而且动态刷新不会生效,防止检测数据丢失

  • 全局默认值:10
  • 全局默认属性:hystrix.threadPool.default.metrics.rollingStats.numBuckets
  • 实例默认值
  • 继承HystrixCommandProperties.Setter().withMetricsRollingStatisticalWindowBuckets(10)
  • 注解:@HystrixProperty(name="metrics.rollingStats.numBuckets" ,value="20")
  • 实例配置属性:hystrix.threadPool.HystrixCommondKey.metrics.rollingStats.numBuckets

8.Hystrix仪表盘

Hystrix是指标数据的消费者之一,主要用来实时监控Hystrix的各项指标信息,仪表盘组件则是Hystrix Dashboard

首先配置好所有依赖,打上注解


@EnableHystrixDashboard
@SpringCloudApplication
public class DemoApplication {

    @Bean
    RestTemplate restTemplate(){
        return new RestTemplate();
    }

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

随意访问一个服务

@RestController
public class SSController {

    @Autowired
    private SSService ssService;


    @RequestMapping("hello")
    public String getData(){
       return ssService.getData();
    }
}

查看监控数据

从首页上看有三种访问监控的方式,前两种为集群,最后一种为单体引用,我们先讨论单体应用

从首页上看有两个参数

  • Delay:该参数用来控制服务器上的轮询监控信息的延迟时间,默认为2000毫秒
  • Titile:该参数对应了上图头部标题Hystrix Stream之后的内容,默认会使用监控实例的URL

可以在监控信息上看到两个重要的图形信息:一个实心圆和一条曲线

实心圆:颜色从绿色、黄色、橙色、红色递减,健康度逐渐递减,圆越大代表流量越大

曲线:用来记录2分钟内流量的上升和下降

注意:当使用Hystrix Board 监控Spring Cloud Zuul时候,由于Thread Pool信息会一直处于Loading状态,这是由于Zuul默认会使用信号量来隔离,只有配置Hystrix配置隔离机制改为线程池的方式才能得以展示。

9.Turbine集群监控

上一节的/hystrix.stream是对于单体应用的监控而言,但是我们的微服务可能往往有成白个,不可能每个实例都打开页面观看,所以有了Turbine集群监控。Turbine将所有监控信息汇总后再发给Dashboard

注意:一个Dashoboard算一个服务实例,一个Turbine算一个服务实例,其他需要被监控的实例则需要配置端点监控acutor即可。

第一步配置Turbine,打开注解


@EnableTurbine
@EnableDiscoveryClient
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

第二部,配置属性

turbine.app-config=xxx 配置的是需要监控的服务名字

turbine.cluster-name-expression="default" 配置的是这个turbine集群的名字,当需要启用多个turbine的时候可以根据这个区分

turbine.combine-host-port  配置的是服务是否以主机名+端口号来区分,防止本地所有不同的服务聚集在一起 

spring.application.name=turbine
server.port=8989
management.port=8990

eureka.client.service-url.defaultZone=http://localhost:1111/eureka/

turbine.app-config=hystrix-dashboard
turbine.cluster-name-expression="default"
turbine.combine-host-port=true

turbine会将同一个服务的多个实例汇总成一个监控图,host指的是实例数量 

10.与消息代理结合

可以再加入一个RabbitMQ,RabbitMQ负责收集监控数据,然后Turbine再从RabbitMQ中异步获取这些数据,然后在输给DashBoard

第一步创建一个包含amqp的RabbitMq

  • spring-cloud-starter-turbine-amqp 此依赖包含了rabbit依赖和turbine依赖
  • actuator   Turbine断点监控

第二步,使用注解@EnableTurbineStream (java 8 以上)

第三不,配置配置文件

  • 此处不再需要配置服务名字
spring.application.name=turbine
server.port=8989
management.port=8990

eureka.client.service-url.defaultZone=http://localhost:1111/eureka/

第四步,在监控实例上加入依赖

spring-cloud-netflix-hystrix-amqp

最终可以看到一模一样的监控信息。

猜你喜欢

转载自blog.csdn.net/m0_37834471/article/details/81322478