github.com/aacfactory/fns@v1.2.86-0.20240310083819-80d667fc0a17/clusters/development.go (about)

     1  /*
     2   * Copyright 2023 Wang Min Xiang
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   * 	http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   *
    16   */
    17  
    18  package clusters
    19  
    20  import (
    21  	"fmt"
    22  	"github.com/aacfactory/errors"
    23  	"github.com/aacfactory/fns/barriers"
    24  	"github.com/aacfactory/fns/clusters/proxy"
    25  	"github.com/aacfactory/fns/commons/bytex"
    26  	"github.com/aacfactory/fns/commons/mmhash"
    27  	"github.com/aacfactory/fns/commons/signatures"
    28  	"github.com/aacfactory/fns/context"
    29  	"github.com/aacfactory/fns/shareds"
    30  	"github.com/aacfactory/fns/transports"
    31  	"github.com/aacfactory/json"
    32  	"github.com/aacfactory/logs"
    33  	"strings"
    34  	"time"
    35  )
    36  
    37  const developmentName = "dev"
    38  
    39  type DevelopmentConfig struct {
    40  	ProxyAddr string `json:"proxyAddr"`
    41  }
    42  
    43  func NewDevelopment(dialer transports.Dialer, signature signatures.Signature) Cluster {
    44  	return &Development{
    45  		log:       nil,
    46  		signature: signature,
    47  		address:   nil,
    48  		dialer:    dialer,
    49  		client:    nil,
    50  		events:    make(chan NodeEvent, 64),
    51  		closeFn:   nil,
    52  		nodes:     make(Nodes, 0, 1),
    53  	}
    54  }
    55  
    56  type Development struct {
    57  	log       logs.Logger
    58  	signature signatures.Signature
    59  	address   []byte
    60  	dialer    transports.Dialer
    61  	client    transports.Client
    62  	events    chan NodeEvent
    63  	closeFn   context.CancelFunc
    64  	nodes     Nodes
    65  }
    66  
    67  func (cluster *Development) Construct(options ClusterOptions) (err error) {
    68  	cluster.log = options.Log
    69  	config := DevelopmentConfig{}
    70  	configErr := options.Config.As(&config)
    71  	if configErr != nil {
    72  		err = errors.Warning("fns: dev cluster construct failed").WithCause(configErr)
    73  		return
    74  	}
    75  	address := strings.TrimSpace(config.ProxyAddr)
    76  	if address == "" {
    77  		err = errors.Warning("fns: dev cluster construct failed").WithCause(fmt.Errorf("address is required"))
    78  		return
    79  	}
    80  	cluster.address = []byte(address)
    81  	return
    82  }
    83  
    84  func (cluster *Development) AddService(_ Service) {
    85  	return
    86  }
    87  
    88  func (cluster *Development) Join(ctx context.Context) (err error) {
    89  	cluster.client, err = cluster.dialer.Dial(cluster.address)
    90  	if err != nil {
    91  		err = errors.Warning("fns: dev cluster join failed").WithCause(err)
    92  		return
    93  	}
    94  	ctx, cluster.closeFn = context.WithCancel(ctx)
    95  	go cluster.watching(ctx)
    96  	return
    97  }
    98  
    99  func (cluster *Development) Leave(_ context.Context) (err error) {
   100  	cluster.closeFn()
   101  	close(cluster.events)
   102  	return
   103  }
   104  
   105  func (cluster *Development) NodeEvents() (events <-chan NodeEvent) {
   106  	events = cluster.events
   107  	return
   108  }
   109  
   110  func (cluster *Development) Shared() (shared shareds.Shared) {
   111  	shared = proxy.NewShared(cluster.Client, cluster.signature)
   112  	return
   113  }
   114  
   115  func (cluster *Development) Client() transports.Client {
   116  	return cluster.client
   117  }
   118  
   119  func (cluster *Development) Barrier() (barrier barriers.Barrier) {
   120  	barrier = barriers.New()
   121  	return
   122  }
   123  
   124  func (cluster *Development) watching(ctx context.Context) {
   125  	cluster.fetchAndUpdate(ctx)
   126  	stop := false
   127  	timer := time.NewTimer(10 * time.Second)
   128  	for {
   129  		select {
   130  		case <-ctx.Done():
   131  			stop = true
   132  			break
   133  		case <-timer.C:
   134  			cluster.fetchAndUpdate(ctx)
   135  			break
   136  		}
   137  		if stop {
   138  			timer.Stop()
   139  			break
   140  		}
   141  	}
   142  }
   143  
   144  func (cluster *Development) fetchAndUpdate(ctx context.Context) {
   145  	nodes := cluster.fetchNodes(ctx)
   146  	if len(nodes) == 0 {
   147  		if len(cluster.nodes) == 0 {
   148  			return
   149  		}
   150  		cluster.events <- NodeEvent{
   151  			Kind: Remove,
   152  			Node: cluster.nodes[0],
   153  		}
   154  	} else {
   155  		op, _ := json.Marshal(cluster.nodes)
   156  		np, _ := json.Marshal(nodes)
   157  		if mmhash.Sum64(op) == mmhash.Sum64(np) {
   158  			return
   159  		}
   160  		cluster.events <- NodeEvent{
   161  			Kind: Remove,
   162  			Node: nodes[0],
   163  		}
   164  		cluster.events <- NodeEvent{
   165  			Kind: Add,
   166  			Node: nodes[0],
   167  		}
   168  	}
   169  	cluster.nodes = nodes
   170  	return
   171  }
   172  
   173  func (cluster *Development) fetchNodes(ctx context.Context) (nodes Nodes) {
   174  	infos, infosErr := proxy.FetchEndpointInfos(ctx, cluster.client, cluster.signature)
   175  	if infosErr != nil {
   176  		if cluster.log.WarnEnabled() {
   177  			cluster.log.Warn().Cause(infosErr).With("cluster", developmentName).Message("fns: fetch endpoint infos failed")
   178  		}
   179  		return
   180  	}
   181  	for i, info := range infos {
   182  		info.Address = bytex.ToString(cluster.address)
   183  		infos[i] = info
   184  	}
   185  	nodes = MapEndpointInfosToNodes(infos)
   186  	return
   187  }