go-hystrix源码分析

1, 函数结果如何统计的?

每一个操作都抽象为了command 结构体,每一个command结构体都关联了一个CircuitBreaker。

type CircuitBreaker struct {
  Name                   string
  open                   bool
  forceOpen              bool
  mutex                  *sync.RWMutex
  openedOrLastTestedTime int64  // 上次打开时间

  executorPool *executorPool   // 做限流
  metrics      *metricExchange  // 失败成功的计数器
}
type command struct {
	sync.Mutex

	ticket      *struct{}
	start       time.Time
	errChan     chan error
	finished    chan bool
	circuit     *CircuitBreaker
	run         runFuncC   // 真正执行函数
	fallback    fallbackFuncC  // 熔断后执行函数
	runDuration time.Duration
	events      []string
}

函数执行结果传递图

go-hystrix源码分析_第1张图片

command执行run函数后reportEvent success,

		runStart := time.Now()
   	runErr := run(ctx)
   	returnOnce.Do(func() {
   		defer reportAllEvent()
   		cmd.runDuration = time.Since(runStart)
   		returnTicket()
   		if runErr != nil {
   			cmd.errorWithFallback(ctx, runErr)
   			return
   		}
   		cmd.reportEvent("success")
   	})

commnad的reportEvent其实就是往command的里的events数组里append

	func (c *command) reportEvent(eventType string) {
   c.Lock()
   defer c.Unlock()

   c.events = append(c.events, eventType)
}

reportAllEvent函数才是真正传递事件给CircuitBreaker 的方法,reportAllEvent调用CircuitBreaker的ReportEvent方法

	// ReportEvent records command metrics for tracking recent error rates and exposing data to the dashboard.
func (circuit *CircuitBreaker) ReportEvent(eventTypes []string, start time.Time, runDuration time.Duration) error {
   if len(eventTypes) == 0 {
   	return fmt.Errorf("no event types sent for metrics")
   }

   circuit.mutex.RLock()
   o := circuit.open
   circuit.mutex.RUnlock()
   if eventTypes[0] == "success" && o {
   	circuit.setClose()
   }

   var concurrencyInUse float64
   if circuit.executorPool.Max > 0 {
   	concurrencyInUse = float64(circuit.executorPool.ActiveCount()) / float64(circuit.executorPool.Max)
   }
   // 其实就是把事件通过channel从CircuitBreaker传递给了metrics
   select {
   case circuit.metrics.Updates <- &commandExecution{
   	Types:            eventTypes,
   	Start:            start,
   	RunDuration:      runDuration,
   	ConcurrencyInUse: concurrencyInUse,
   }:
   default:
   	return CircuitError{Message: fmt.Sprintf("metrics channel (%v) is at capacity", circuit.Name)}
   }

   return nil
}

metrics在创建的时候会启动一个go程去监听Updates的数据,当有新数据到来的时候,会去进行处理,来看看这个monitor咋写的

	type metricExchange struct {
   Name    string
   // 更新事件
   Updates chan *commandExecution
   Mutex   *sync.RWMutex
   // 统计器
   metricCollectors []metricCollector.MetricCollector
}

其实就是让MetricCollector 去拿到更新事件去进行统计,这里默认情况下其实只有一个metricCollectors

func (m *metricExchange) Monitor() {
   for update := range m.Updates {
   	// we only grab a read lock to make sure Reset() isn't changing the numbers.
   	m.Mutex.RLock()

   	totalDuration := time.Since(update.Start)
   	wg := &sync.WaitGroup{}
   	for _, collector := range m.metricCollectors {
   		wg.Add(1)
   		go m.IncrementMetrics(wg, collector, update, totalDuration)
   	}
   	wg.Wait()

   	m.Mutex.RUnlock()
   }
}

看下collector怎么更新的

func (d *DefaultMetricCollector) Update(r MetricResult) {
   d.mutex.RLock()
   defer d.mutex.RUnlock()

   d.numRequests.Increment(r.Attempts)
   d.errors.Increment(r.Errors)
   d.successes.Increment(r.Successes)
   d.failures.Increment(r.Failures)
   d.rejects.Increment(r.Rejects)
   d.shortCircuits.Increment(r.ShortCircuits)
   d.timeouts.Increment(r.Timeouts)
   d.fallbackSuccesses.Increment(r.FallbackSuccesses)
   d.fallbackFailures.Increment(r.FallbackFailures)
   d.contextCanceled.Increment(r.ContextCanceled)
   d.contextDeadlineExceeded.Increment(r.ContextDeadlineExceeded)

   d.totalDuration.Add(r.TotalDuration)
   d.runDuration.Add(r.RunDuration)
}

collector 其实是对各种指标的封装,各种指标是如何封装的呢? 每个指标都被Number对象封装起来,Number 记录该指标10s以内出现的此时。看看Number 是如何记录的。
Number 维护了一个map map的key值就是时间,value值为当前时间出现的次数

type Number struct {
	Buckets map[int64]*numberBucket
	Mutex   *sync.RWMutex
}

type numberBucket struct {
	Value float64
}

Number 的Increment方法会获取到当前时间的value值,然后移除掉10s以前的记录

func (r *Number) getCurrentBucket() *numberBucket {
	now := time.Now().Unix()
	var bucket *numberBucket
	var ok bool

	if bucket, ok = r.Buckets[now]; !ok {
		bucket = &numberBucket{}
		r.Buckets[now] = bucket
	}

	return bucket
}

func (r *Number) removeOldBuckets() {
	now := time.Now().Unix() - 10

	for timestamp := range r.Buckets {
		// TODO: configurable rolling window
		if timestamp <= now {
			delete(r.Buckets, timestamp)
		}
	}
}

// Increment increments the number in current timeBucket.
func (r *Number) Increment(i float64) {
	if i == 0 {
		return
	}

	r.Mutex.Lock()
	defer r.Mutex.Unlock()

	b := r.getCurrentBucket()
	b.Value += i
	r.removeOldBuckets()
}

你可能感兴趣的:(编程日记)