说明

原创不易,如若转载 请标明来源!

欢迎关注本人微信公众号:壹枝花算不算浪漫

更多内容也可查看本人博客:一枝花算不算浪漫

前言

前情回顾

上一讲我们讲解了Hystrix在配合feign的过程中,一个正常的请求逻辑该怎样处理,这里涉及到线程池的创建、HystrixCommand的执行等逻辑。

如图所示:

高清大图:https://www.processon.com/view/link/5e1c128ce4b0169fb51ce77e

本讲目录

这一讲开始讲解Hystrix的看家本领:熔断+降级。

熔断功能是Hystrix最核心的组件,当然也是最复杂的一块。

源码中细节太多,本讲我们主要还是专注于它的设计思想去学习。

目录如下:

  1. HystrixCircuitBreaker初始化过程
  2. Hystrix熔断机制(CLOSED/OPEN/HALF_OPEN)
  3. fallback降级机制

源码分析

HystrixCircuitBreaker初始化过程

我们还是会以AbstractCommand为突破口,这里继续看它的构造函数,其中里面有初始化熔断器initCircuitBreaker()的过程,具体代码如下:

  1. abstract class AbstractCommand<R> implements HystrixInvokableInfo<R>, HystrixObservable<R> {
  2. private static HystrixCircuitBreaker initCircuitBreaker(boolean enabled, HystrixCircuitBreaker fromConstructor,
  3. HystrixCommandGroupKey groupKey, HystrixCommandKey commandKey,
  4. HystrixCommandProperties properties, HystrixCommandMetrics metrics) {
  5. if (enabled) {
  6. if (fromConstructor == null) {
  7. // 构建默认的HystrixCircuitBreaker
  8. return HystrixCircuitBreaker.Factory.getInstance(commandKey, groupKey, properties, metrics);
  9. } else {
  10. return fromConstructor;
  11. }
  12. } else {
  13. return new NoOpCircuitBreaker();
  14. }
  15. }
  16. }
  17. public interface HystrixCircuitBreaker {
  18. public static HystrixCircuitBreaker getInstance(HystrixCommandKey key, HystrixCommandGroupKey group, HystrixCommandProperties properties, HystrixCommandMetrics metrics) {
  19. // circuitBreakersByCommand是一个map,key为commandKey,也就是FeignClient中定义的方法名
  20. // 类似于ServiceAFeignClient.sayHello(String)
  21. HystrixCircuitBreaker previouslyCached = circuitBreakersByCommand.get(key.name());
  22. if (previouslyCached != null) {
  23. return previouslyCached;
  24. }
  25. // 每个commandKey都对应着自己的熔断器,如果没有则会构造一个HystrixCircuitBreaker
  26. HystrixCircuitBreaker cbForCommand = circuitBreakersByCommand.putIfAbsent(key.name(), new HystrixCircuitBreakerImpl(key, group, properties, metrics));
  27. if (cbForCommand == null) {
  28. return circuitBreakersByCommand.get(key.name());
  29. } else {
  30. return cbForCommand;
  31. }
  32. }
  33. class HystrixCircuitBreakerImpl implements HystrixCircuitBreaker {
  34. private final HystrixCommandProperties properties;
  35. private final HystrixCommandMetrics metrics;
  36. private Subscription subscribeToStream() {
  37. // 对HealthCounts进行订阅
  38. // HealthCounts中包含 总请求次数、总失败次数、失败率
  39. // HealthCounts 统计数据有变化则会回调到这里来
  40. return metrics.getHealthCountsStream()
  41. .observe()
  42. .subscribe(new Subscriber<HealthCounts>() {
  43. @Override
  44. public void onCompleted() {
  45. }
  46. @Override
  47. public void onError(Throwable e) {
  48. }
  49. // 判断是否要降级的核心逻辑
  50. @Override
  51. public void onNext(HealthCounts hc) {
  52. // 一个时间窗口(默认10s钟)总请求次数是否大于circuitBreakerRequestVolumeThreshold 默认为20s
  53. if (hc.getTotalRequests() < properties.circuitBreakerRequestVolumeThreshold().get()) {
  54. } else {
  55. // 错误率(总错误次数/总请求次数)小于circuitBreakerErrorThresholdPercentage(默认50%)
  56. if (hc.getErrorPercentage() < properties.circuitBreakerErrorThresholdPercentage().get()) {
  57. } else {
  58. // 反之,熔断状态将从CLOSED变为OPEN,且circuitOpened==>当前时间戳
  59. if (status.compareAndSet(Status.CLOSED, Status.OPEN)) {
  60. circuitOpened.set(System.currentTimeMillis());
  61. }
  62. }
  63. }
  64. }
  65. });
  66. }
  67. }
  68. }

上面就是熔断器初始化过程,这里面做了几件事:

  1. 每个commandKey都有自己的一个熔断器

    commandKey表现形式为:ServiceAFeignClient#sayHello(String)

  2. 如果commandKey不存在熔断器,则构建默认熔断器

    默认熔断器会对HealthCounts进行订阅。HealthCounts中包含时间窗口内(默认10s钟)请求的总次数、失败次数、失败率

  3. HealthCounts中统计数据有变化则会回调subscribe.onNext()方法进行熔断开启判断

  4. 熔断开启条件:

  • 时间窗口内(默认10s钟)总请求次数大于20次
  • 时间窗口内(默认10s钟)失败率大于50%
  • 满足上述两个条件后熔断器状态从CLOSED变成OPEN

熔断器在第一次请求时会初始化AbtractCommand,同时也会创建对应commandKey的熔断器 ,熔断器默认都是关闭的(可配置为强制开启),只有满足触发条件才会被开启。下面就一起来看下熔断、半开等状态是如何触发的吧。

Hystrix熔断机制(CLOSED/OPEN/HALF_OPEN)

这里我们以AbstractCommand.applyHystrixSemantics() 为入口,一步步往下探究,这个方法在上一讲已经提到过,一个正常的Feign请求都会调用此方法。

  1. abstract class AbstractCommand<R> implements HystrixInvokableInfo<R>, HystrixObservable<R> {
  2. private Observable<R> applyHystrixSemantics(final AbstractCommand<R> _cmd) {
  3. // 如果熔断了,这这里返回为false
  4. // 这里也包含HALF_OPEN逻辑
  5. if (circuitBreaker.attemptExecution()) {
  6. final Action1<Throwable> markExceptionThrown = new Action1<Throwable>() {
  7. @Override
  8. public void call(Throwable t) {
  9. eventNotifier.markEvent(HystrixEventType.EXCEPTION_THROWN, commandKey);
  10. }
  11. };
  12. if (executionSemaphore.tryAcquire()) {
  13. try {
  14. executionResult = executionResult.setInvocationStartTime(System.currentTimeMillis());
  15. return executeCommandAndObserve(_cmd)
  16. .doOnError(markExceptionThrown)
  17. .doOnTerminate(singleSemaphoreRelease)
  18. .doOnUnsubscribe(singleSemaphoreRelease);
  19. } catch (RuntimeException e) {
  20. return Observable.error(e);
  21. }
  22. } else {
  23. return handleSemaphoreRejectionViaFallback();
  24. }
  25. } else {
  26. return handleShortCircuitViaFallback();
  27. }
  28. }
  29. }

circuitBreaker.attemptExecution() 这个逻辑就是判断,如果熔断了,那么返回false。而且这里还包含HALF_OPEN的逻辑,我们先看如何触发熔断的,这个后面再接着看。

接着往下跟进executeCommandAndObserve() 方法:

  1. abstract class AbstractCommand<R> implements HystrixInvokableInfo<R>, HystrixObservable<R> {
  2. private Observable<R> executeCommandAndObserve(final AbstractCommand<R> _cmd) {
  3. final HystrixRequestContext currentRequestContext = HystrixRequestContext.getContextForCurrentThread();
  4. // 省略部分代码...
  5. // 运行过程中,出现异常等都会进入此回调函数
  6. final Func1<Throwable, Observable<R>> handleFallback = new Func1<Throwable, Observable<R>>() {
  7. @Override
  8. public Observable<R> call(Throwable t) {
  9. circuitBreaker.markNonSuccess();
  10. Exception e = getExceptionFromThrowable(t);
  11. executionResult = executionResult.setExecutionException(e);
  12. if (e instanceof RejectedExecutionException) {
  13. return handleThreadPoolRejectionViaFallback(e);
  14. } else if (t instanceof HystrixTimeoutException) {
  15. return handleTimeoutViaFallback();
  16. } else if (t instanceof HystrixBadRequestException) {
  17. return handleBadRequestByEmittingError(e);
  18. } else {
  19. /*
  20. * Treat HystrixBadRequestException from ExecutionHook like a plain HystrixBadRequestException.
  21. */
  22. if (e instanceof HystrixBadRequestException) {
  23. eventNotifier.markEvent(HystrixEventType.BAD_REQUEST, commandKey);
  24. return Observable.error(e);
  25. }
  26. return handleFailureViaFallback(e);
  27. }
  28. }
  29. };
  30. Observable<R> execution;
  31. if (properties.executionTimeoutEnabled().get()) {
  32. // 这里创建一个 HystrixObservableTimeoutOperator
  33. execution = executeCommandWithSpecifiedIsolation(_cmd)
  34. .lift(new HystrixObservableTimeoutOperator<R>(_cmd));
  35. } else {
  36. execution = executeCommandWithSpecifiedIsolation(_cmd);
  37. }
  38. return execution.doOnNext(markEmits)
  39. .doOnCompleted(markOnCompleted)
  40. .onErrorResumeNext(handleFallback)
  41. .doOnEach(setRequestContext);
  42. }
  43. }

当我们服务调用中出现异常都会进入handleFallback()中,里面的方法我们就不继续跟入了,猜测里面会有更新HealthCounts中的属性,然后触发 HystrixCircuitBreaker中的onNext()方法,当满足熔断条件时 则会将熔断状态从CLOSED变成OPEN

这里我们会跟进下HystrixObservableTimeoutOperator 代码,这个是对我们执行过程中判断是否超时。

上面代码中,执行executeCommandWithSpecifiedIsolation() 方法时也会创建一个超时监视器:

  1. private static class HystrixObservableTimeoutOperator<R> implements Operator<R, R> {
  2. final AbstractCommand<R> originalCommand;
  3. public HystrixObservableTimeoutOperator(final AbstractCommand<R> originalCommand) {
  4. this.originalCommand = originalCommand;
  5. }
  6. @Override
  7. public Subscriber<? super R> call(final Subscriber<? super R> child) {
  8. TimerListener listener = new TimerListener() {
  9. @Override
  10. public void tick() {
  11. // 判断command的timeOut状态,如果是未执行状态,则更新为已超时
  12. if (originalCommand.isCommandTimedOut.compareAndSet(TimedOutStatus.NOT_EXECUTED, TimedOutStatus.TIMED_OUT)) {
  13. originalCommand.eventNotifier.markEvent(HystrixEventType.TIMEOUT, originalCommand.commandKey);
  14. s.unsubscribe();
  15. final HystrixContextRunnable timeoutRunnable = new HystrixContextRunnable(originalCommand.concurrencyStrategy, hystrixRequestContext, new Runnable() {
  16. @Override
  17. public void run() {
  18. child.onError(new HystrixTimeoutException());
  19. }
  20. });
  21. timeoutRunnable.run();
  22. }
  23. }
  24. @Override
  25. public int getIntervalTimeInMilliseconds() {
  26. return originalCommand.properties.executionTimeoutInMilliseconds().get();
  27. }
  28. };
  29. final Reference<TimerListener> tl = HystrixTimer.getInstance().addTimerListener(listener);
  30. originalCommand.timeoutTimer.set(tl);
  31. // 省略部分代码...
  32. s.add(parent);
  33. return parent;
  34. }
  35. }
  36. public class HystrixTimer {
  37. public Reference<TimerListener> addTimerListener(final TimerListener listener) {
  38. startThreadIfNeeded();
  39. Runnable r = new Runnable() {
  40. @Override
  41. public void run() {
  42. try {
  43. // 执行上面的tick方法,改变command timeout状态
  44. listener.tick();
  45. } catch (Exception e) {
  46. logger.error("Failed while ticking TimerListener", e);
  47. }
  48. }
  49. };
  50. // 执行调度任务,延迟加载,延迟时间和调度时间默认都为1s钟
  51. // 这里使用线程池,coreSize=cpu核心数 maxSize为Integer.Max
  52. ScheduledFuture<?> f = executor.get().getThreadPool().scheduleAtFixedRate(r, listener.getIntervalTimeInMilliseconds(), listener.getIntervalTimeInMilliseconds(), TimeUnit.MILLISECONDS);
  53. return new TimerReference(listener, f);
  54. }
  55. }

这里面核心业务是起一个调度任务,默认每秒钟执行一次,然后调用tick()方法,如果当前command状态还是NOT_EXECUTED状态,那么将command状态改为TIMED_OUT 。此时会进入到之前的handleFallback回调函数中,这里又会更新HealthCounts中的数据,对应的触发之前熔断的判断条件:

  1. protected HystrixCircuitBreakerImpl(HystrixCommandKey key, HystrixCommandGroupKey commandGroup, final HystrixCommandProperties properties, HystrixCommandMetrics metrics) {
  2. this.properties = properties;
  3. this.metrics = metrics;
  4. //On a timer, this will set the circuit between OPEN/CLOSED as command executions occur
  5. Subscription s = subscribeToStream();
  6. activeSubscription.set(s);
  7. }
  8. private Subscription subscribeToStream() {
  9. //这里会在每次执行onNext()事件的时候来评估是否需要打开或者关闭断路器
  10. return metrics.getHealthCountsStream()
  11. .observe()
  12. .subscribe(new Subscriber<HealthCounts>() {
  13. @Override
  14. public void onCompleted() {
  15. }
  16. @Override
  17. public void onError(Throwable e) {
  18. }
  19. @Override
  20. public void onNext(HealthCounts hc) {
  21. //首先校验的时在时间窗范围内的请求次数,如果低于阈值(默认是20),不做处理,如果高于阈值,则去判断接口请求的错误率
  22. if (hc.getTotalRequests() < properties.circuitBreakerRequestVolumeThreshold().get()) { // 如果没有超过统计阈值的最低窗口值,就没有必要去改变断路器的状态
  23. // 当前如果断路器是关闭的,那么就保持关闭状态无需更改;
  24. // 如果断路器状态为半开状态,需要等待直到有成功的命令执行;
  25. // 如果断路器是打开状态,需要等待休眠窗口过期。
  26. } else {
  27. //判断接口请求的错误率(阈值默认是50),如果高于这个值,则断路器打开
  28. if (hc.getErrorPercentage() < properties.circuitBreakerErrorThresholdPercentage().get()) {
  29. // 如果当前请求的错误率小于断路器设置的容错率百分比,也不会拦截请求
  30. } else {
  31. // 如果当前错误率太高则打开断路器
  32. if (status.compareAndSet(Status.CLOSED, Status.OPEN)) {
  33. circuitOpened.set(System.currentTimeMillis());
  34. }
  35. }
  36. }
  37. }
  38. });
  39. }

如果符合熔断条件,那么command熔断状态就会变为OPEN,此时熔断器打开。

如果我们command执行成功,那么就会清理掉这个timeout timer schedule任务。

  1. abstract class AbstractCommand<R> implements HystrixInvokableInfo<R>, HystrixObservable<R> {
  2. private void handleCommandEnd(boolean commandExecutionStarted) {
  3. Reference<TimerListener> tl = timeoutTimer.get();
  4. // 如果timeOutTimer不为空,这里则clear一下
  5. // clear会关闭启动的调度任务
  6. if (tl != null) {
  7. tl.clear();
  8. }
  9. long userThreadLatency = System.currentTimeMillis() - commandStartTimestamp;
  10. executionResult = executionResult.markUserThreadCompletion((int) userThreadLatency);
  11. if (executionResultAtTimeOfCancellation == null) {
  12. // metrics统计数据
  13. metrics.markCommandDone(executionResult, commandKey, threadPoolKey, commandExecutionStarted);
  14. } else {
  15. metrics.markCommandDone(executionResultAtTimeOfCancellation, commandKey, threadPoolKey, commandExecutionStarted);
  16. }
  17. if (endCurrentThreadExecutingCommand != null) {
  18. endCurrentThreadExecutingCommand.call();
  19. }
  20. }
  21. }

如上所属,我们已经知道了熔断开启的触发时机,那么如果一个commandKey开启了熔断,下次的请求是该如何直接降级呢?我们来看下代码:

  1. abstract class AbstractCommand<R> implements HystrixInvokableInfo<R>, HystrixObservable<R> {
  2. private Observable<R> applyHystrixSemantics(final AbstractCommand<R> _cmd) {
  3. // 这个if条件就代表是否开启熔断
  4. if (circuitBreaker.attemptExecution()) {
  5. // 执行业务逻辑代码...
  6. } else {
  7. return handleShortCircuitViaFallback();
  8. }
  9. }
  10. }
  11. class HystrixCircuitBreakerImpl implements HystrixCircuitBreaker {
  12. public boolean attemptExecution() {
  13. // 如果熔断配置的为强制开启,那么直接返回false执行熔断逻辑
  14. if (properties.circuitBreakerForceOpen().get()) {
  15. return false;
  16. }
  17. // 如果熔断配置为强制关闭,那么永远不走熔断逻辑
  18. if (properties.circuitBreakerForceClosed().get()) {
  19. return true;
  20. }
  21. // 熔断开启时 circuitOpened设置为当前时间戳
  22. if (circuitOpened.get() == -1) {
  23. return true;
  24. } else {
  25. // 如果当前时间距离熔断小于5s钟,那么将熔断状态从OPEN改为HALF_OPEN
  26. if (isAfterSleepWindow()) {
  27. if (status.compareAndSet(Status.OPEN, Status.HALF_OPEN)) {
  28. //only the first request after sleep window should execute
  29. return true;
  30. } else {
  31. return false;
  32. }
  33. } else {
  34. return false;
  35. }
  36. }
  37. }
  38. }
  39. private boolean isAfterSleepWindow() {
  40. final long circuitOpenTime = circuitOpened.get();
  41. final long currentTime = System.currentTimeMillis();
  42. // circuitBreakerSleepWindowInMilliseconds 默认为5s钟
  43. final long sleepWindowTime = properties.circuitBreakerSleepWindowInMilliseconds().get();
  44. // 当前熔断距离熔断是否超过5s钟
  45. return currentTime > circuitOpenTime + sleepWindowTime;
  46. }
  47. }

我们可以看到,在applyHystrixSemantics()这个核心的方法中,先判断是否熔断,如果熔断则直接走fallback逻辑。

attemptExecution()判断条件中还涉及到HALF_OPEN的逻辑,如果熔断开启,下一次请求的时候,会判断当前时间距离上一次时间是否超过了5s钟,如果没有超过,则会将熔断状态从OPEN变为HALF_OPEN,此时会放一个请求按照正常逻辑去执行:

  1. 执行失败,熔断状态又会从HALF_OPEN变成OPEN
  2. 执行成功,熔断状态从HALF_OPEN变成CLOSED,并清除熔断相关设置

执行成功后代码:

  1. class HystrixCircuitBreakerImpl implements HystrixCircuitBreaker {
  2. public void markSuccess() {
  3. if (status.compareAndSet(Status.HALF_OPEN, Status.CLOSED)) {
  4. //This thread wins the race to close the circuit - it resets the stream to start it over from 0
  5. metrics.resetStream();
  6. Subscription previousSubscription = activeSubscription.get();
  7. if (previousSubscription != null) {
  8. previousSubscription.unsubscribe();
  9. }
  10. Subscription newSubscription = subscribeToStream();
  11. activeSubscription.set(newSubscription);
  12. circuitOpened.set(-1L);
  13. }
  14. }
  15. }

上面对整个熔断的状态:CLOSED、OPEN、HALF_OPEN梳理的已经很清楚了,下面看看降级是该如何处理的吧。

fallback降级机制

上面已经讲解了Hystrix 熔断开启的机制等内容,这里主要是说如果一个请求失败(线程池拒绝、超时、badRequest等),那么Hystrix是如何执行降级的呢?

还是回到我们最初的代码 HystrixInvocationHandler类中,看看其invoke()方法中的getFallback回调函数:

  1. protected Object getFallback() {
  2. if (fallbackFactory == null) {
  3. return super.getFallback();
  4. }
  5. try {
  6. // 通过我们配置好的fallbackFactory找到对应的FeignClient,这里是获取ServiceAFeignClient
  7. Object fallback = fallbackFactory.create(getExecutionException());
  8. // fallbackMap中key为ServiceAFeignClient.sayHello(Integer)
  9. // 获取具体的降级method方法
  10. Object result = fallbackMethodMap.get(method).invoke(fallback, args);
  11. if (isReturnsHystrixCommand(method)) {
  12. return ((HystrixCommand) result).execute();
  13. } else if (isReturnsObservable(method)) {
  14. // Create a cold Observable
  15. return ((Observable) result).toBlocking().first();
  16. } else if (isReturnsSingle(method)) {
  17. // Create a cold Observable as a Single
  18. return ((Single) result).toObservable().toBlocking().first();
  19. } else if (isReturnsCompletable(method)) {
  20. ((Completable) result).await();
  21. return null;
  22. } else {
  23. return result;
  24. }
  25. } catch (IllegalAccessException e) {
  26. // shouldn't happen as method is public due to being an interface
  27. throw new AssertionError(e);
  28. } catch (InvocationTargetException e) {
  29. // Exceptions on fallback are tossed by Hystrix
  30. throw new AssertionError(e.getCause());
  31. }
  32. }
  33. };

这里很简单,其实就是先获取到我们自己在FallbackFactory中配置的的降级方法,然后执行降级逻辑。

总结

这一讲核心逻辑主要是Hystrix熔断状态的变化,主要是CLOSED、OPEN、HALF_OPEN几种状态触发的时间,互相转变的流程,以及执行降级逻辑的原理。

我们仍然是用一个流程图来总结一下:

高清大图链接:

https://www.processon.com/view/link/5e1ee0afe4b0c62462aae684

(点击原文可以直接查看大图哦

【一起学源码-微服务】Hystrix 源码三:Hystrix核心流程:Hystix降级、熔断等原理剖析的更多相关文章

  1. 【一起学源码-微服务】Nexflix Eureka 源码十:服务下线及实例摘除,一个client下线到底多久才会被其他实例感知?

    前言 前情回顾 上一讲我们讲了 client端向server端发送心跳检查,也是默认每30钟发送一次,server端接收后会更新注册表的一个时间戳属性,然后一次心跳(续约)也就完成了. 本讲目录 这一 ...

  2. 【一起学源码-微服务】Nexflix Eureka 源码十三:Eureka源码解读完结撒花篇~!

    前言 想说的话 [一起学源码-微服务-Netflix Eureka]专栏到这里就已经全部结束了. 实话实说,从最开始Eureka Server和Eureka Client初始化的流程还是一脸闷逼,到现 ...

  3. 【一起学源码-微服务】Ribbon源码五:Ribbon源码解读汇总篇~

    前言 想说的话 [一起学源码-微服务-Ribbon]专栏到这里就已经全部结束了,共更新四篇文章. Ribbon比较小巧,这里是直接 读的spring cloud 内嵌封装的版本,里面的各种config ...

  4. 【一起学源码-微服务】Eureka+Ribbon+Feign阶段性总结

    前言 想说的话 这里已经梳理完Eureka.Ribbon.Feign三大组件的基本原理了,今天做一个总结,里面会有一个比较详细的调用关系流程图. 说明 原创不易,如若转载 请标明来源! 博客地址:一枝 ...

  5. springcloud微服务实战:Eureka+Zuul+Ribbon+Hystrix+SpringConfig

    原文地址:http://blog.csdn.net/yp090416/article/details/78017552 springcloud微服务实战:Eureka+Zuul+Ribbon+Hyst ...

  6. 通过Dapr实现一个简单的基于.net的微服务电商系统(三)——一步一步教你如何撸Dapr

    目录:一.通过Dapr实现一个简单的基于.net的微服务电商系统 二.通过Dapr实现一个简单的基于.net的微服务电商系统(二)--通讯框架讲解 三.通过Dapr实现一个简单的基于.net的微服务电 ...

  7. 【一起学源码-微服务】Hystrix 源码一:Hystrix基础原理与Demo搭建

    说明 原创不易,如若转载 请标明来源! 欢迎关注本人微信公众号:壹枝花算不算浪漫 更多内容也可查看本人博客:一枝花算不算浪漫 前言 前情回顾 上一个系列文章讲解了Feign的源码,主要是Feign动态 ...

  8. 【一起学源码-微服务】Nexflix Eureka 源码二:EurekaServer启动之配置文件加载以及面向接口的配置项读取

    前言 上篇文章已经介绍了 为何要读netflix eureka源码了,这里就不再概述,下面开始正式源码解读的内容. 如若转载 请标明来源:一枝花算不算浪漫 代码总览 还记得上文中,我们通过web.xm ...

  9. 【一起学源码-微服务】Nexflix Eureka 源码八:EurekaClient注册表抓取 精妙设计分析!

    前言 前情回顾 上一讲 我们通过单元测试 来梳理了EurekaClient是如何注册到server端,以及server端接收到请求是如何处理的,这里最重要的关注点是注册表的一个数据结构:Concurr ...

随机推荐

  1. H3C 启动包过滤防火墙功能

  2. Mysql5.7在忘记密码的情况下如何修改密码?

    1.停止服务 2.mysqld --skip-grant-tables 3.回车之后就不要动了,再新打开一个命令提示符窗口,同样进入mysql的安装目录下, 输入:mysql -u root -p 密 ...

  3. uni-app 常用框架内置方法 更新中 .....

    获取 登录信息,getStorage 初始化页面数据   请求  下拉刷新页面 加载更多  点击跳转  个人中心  uni.request(OBJECT)   success=成功    fail=失 ...

  4. Python--day39--进程池原理及效率测试

    #为什么要有进程池的概念 #效率 #每次开启进程都要创建一个属于这个进程的内存空间 #寄存器 堆栈 文件 #进程过多 操作系统调度进程 # #进程池 #python中的 先创建一个属于进程的池子 #这 ...

  5. Scheduler

    先看看文档对于Scheduler的作用介绍 https://code4craft.gitbooks.io/webmagic-in-action/content/zh/posts/ch1-overvie ...

  6. python基础八之文件操作

    python的文件操作 1,打开文件 编码方式要和文件的编码方式相同! #open('路径','打开方式','指定编码方式') f = open(r'E:\pycharm\学习\day8\test', ...

  7. linux ioctl 接口

    大部分驱动需要 -- 除了读写设备的能力 -- 通过设备驱动进行各种硬件控制的能力. 大 部分设备可进行超出简单的数据传输之外的操作; 用户空间必须常常能够请求, 例如, 设 备锁上它的门, 弹出它的 ...

  8. ASP.NET MVC 实现页落网资源分享网站+充值管理+后台管理(9)之系统登录

    前面我们已经做好了一个文章管理功能模块,接下来,我们回头来做登录窗口,登录不仅涉及到登录验证还涉及到登录日志还有缓存时长等. 对于缓存的相关设置,我们已经写好封装在Bobo.Utilities.dll ...

  9. 2019-8-31-win10-uwp-使用-WinDbg-调试

    title author date CreateTime categories win10 uwp 使用 WinDbg 调试 lindexi 2019-08-31 10:30:35 +0800 201 ...

  10. 2019-9-2-win10-uwp-获得焦点改变

    title author date CreateTime categories win10 uwp 获得焦点改变 lindexi 2019-09-02 12:57:38 +0800 2018-2-13 ...