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 }