168 lines
4.8 KiB
Go
168 lines
4.8 KiB
Go
/**
|
||
* Tencent is pleased to support the open source community by making polaris-go available.
|
||
*
|
||
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
|
||
*
|
||
* Licensed under the BSD 3-Clause License (the "License");
|
||
* you may not use this file except in compliance with the License.
|
||
* You may obtain a copy of the License at
|
||
*
|
||
* https://opensource.org/licenses/BSD-3-Clause
|
||
*
|
||
* Unless required by applicable law or agreed to in writing, software distributed
|
||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||
* specific language governing permissions and limitations under the License.
|
||
*/
|
||
|
||
package flow
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"sync"
|
||
"sync/atomic"
|
||
"time"
|
||
|
||
"github.com/polarismesh/polaris-go/pkg/log"
|
||
"github.com/polarismesh/polaris-go/pkg/model"
|
||
"github.com/polarismesh/polaris-go/pkg/plugin/common"
|
||
)
|
||
|
||
const (
|
||
keySourceRoute = "sourceRoute"
|
||
keyDstRoute = "destinationRoute"
|
||
keyDstRateLimit = "destinationRateLimit"
|
||
keyDstInstances = "destinationInstances"
|
||
keyDstServices = "destinationServices"
|
||
)
|
||
|
||
// ContextKey 上下文标识
|
||
type ContextKey struct {
|
||
// 服务信息
|
||
ServiceKey *model.ServiceKey
|
||
// 操作信息
|
||
Operation string
|
||
}
|
||
|
||
// String ToString方法
|
||
func (c ContextKey) String() string {
|
||
return fmt.Sprintf("{ServiceKey: %s, Operation: %s}", *c.ServiceKey, c.Operation)
|
||
}
|
||
|
||
// SingleNotifyContext 同步调用回调上下文
|
||
type SingleNotifyContext struct {
|
||
name *ContextKey
|
||
notifier *common.Notifier
|
||
}
|
||
|
||
// NewSingleNotifyContext 创建回调上下文
|
||
func NewSingleNotifyContext(name *ContextKey, notifier *common.Notifier) *SingleNotifyContext {
|
||
return &SingleNotifyContext{name: name, notifier: notifier}
|
||
}
|
||
|
||
// Err 返回异常信息返回异常信息
|
||
func (s *SingleNotifyContext) Err() model.SDKError {
|
||
return s.notifier.GetError()
|
||
}
|
||
|
||
// Wait notify 异步任务执行回调函数
|
||
func (s *SingleNotifyContext) Wait(timeout time.Duration) bool {
|
||
afterTimer := time.After(timeout)
|
||
select {
|
||
case <-afterTimer:
|
||
return true
|
||
case <-s.notifier.GetContext().Done():
|
||
log.GetBaseLogger().Debugf("context %s has been notified", *s.name)
|
||
return false
|
||
}
|
||
}
|
||
|
||
// CombineNotifyContext 复合的回调上下文,等待所有的子回调都返回才会触发回调
|
||
type CombineNotifyContext struct {
|
||
svcKey *model.ServiceKey
|
||
waitCount int32
|
||
notifiers []*SingleNotifyContext
|
||
doneContextKeys *model.SyncHashSet
|
||
}
|
||
|
||
// NewCombineNotifyContext 创建复合回调上下文
|
||
func NewCombineNotifyContext(svcKey *model.ServiceKey, notifiers []*SingleNotifyContext) *CombineNotifyContext {
|
||
maxWaitCount := len(notifiers)
|
||
combineCtx := &CombineNotifyContext{
|
||
svcKey: svcKey,
|
||
notifiers: notifiers,
|
||
waitCount: int32(maxWaitCount),
|
||
doneContextKeys: model.NewSyncHashSet(),
|
||
}
|
||
return combineCtx
|
||
}
|
||
|
||
// IsDone 是否已经完成
|
||
func (c *CombineNotifyContext) IsDone() bool {
|
||
log.GetBaseLogger().Debugf("CombineNotifyContext waitCount %d", atomic.LoadInt32(&c.waitCount))
|
||
return atomic.LoadInt32(&c.waitCount) <= 0
|
||
}
|
||
|
||
// Errs 获取错误信息集合
|
||
func (c *CombineNotifyContext) Errs() map[ContextKey]model.SDKError {
|
||
var errs = make(map[ContextKey]model.SDKError, len(c.notifiers))
|
||
for _, notifier := range c.notifiers {
|
||
err := notifier.Err()
|
||
if err != nil {
|
||
errs[*notifier.name] = err
|
||
}
|
||
}
|
||
return errs
|
||
}
|
||
|
||
// logNotifier 打印通知日志
|
||
func (c *CombineNotifyContext) logNotifier(operation string, notifier *SingleNotifyContext, restWait int32) {
|
||
log.GetBaseLogger().Debugf("notifier %s of %s has been notified, rest %v", *notifier.name, c.svcKey, restWait)
|
||
}
|
||
|
||
// Wait notify 异步任务执行回调函数
|
||
// 返回值,是否超时
|
||
func (c *CombineNotifyContext) Wait(timeout time.Duration) (exceedTime bool) {
|
||
var restWait = atomic.LoadInt32(&c.waitCount)
|
||
if restWait == 0 {
|
||
return false
|
||
}
|
||
log.GetBaseLogger().Debugf("notifiers of %s start to wait, rest %d", *c.svcKey, restWait)
|
||
doneKeyChan := make(chan string)
|
||
ctx, cancel := context.WithCancel(context.Background())
|
||
go func() {
|
||
for {
|
||
select {
|
||
case <-ctx.Done():
|
||
return
|
||
case key := <-doneKeyChan:
|
||
c.doneContextKeys.Add(key)
|
||
}
|
||
}
|
||
}()
|
||
wg := &sync.WaitGroup{}
|
||
wg.Add(int(restWait))
|
||
for _, notifierValue := range c.notifiers {
|
||
if c.doneContextKeys.Contains(notifierValue.name.Operation) {
|
||
continue
|
||
}
|
||
go func(notifier *SingleNotifyContext) {
|
||
defer wg.Done()
|
||
afterTimer := time.After(timeout)
|
||
select {
|
||
case <-afterTimer:
|
||
return
|
||
case <-notifier.notifier.GetContext().Done():
|
||
doneKeyChan <- notifier.name.Operation
|
||
nextWait := atomic.AddInt32(&c.waitCount, -1)
|
||
c.logNotifier(notifier.name.Operation, notifier, nextWait)
|
||
}
|
||
}(notifierValue)
|
||
}
|
||
wg.Wait()
|
||
cancel()
|
||
// 如果没有waitCount还是大于0,说明还需要远程获取信息
|
||
return atomic.LoadInt32(&c.waitCount) > 0
|
||
}
|