github.com/polarismesh/polaris@v1.17.8/apiserver/eurekaserver/replicate_worker.go (about) 1 /** 2 * Tencent is pleased to support the open source community by making Polaris available. 3 * 4 * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. 5 * 6 * Licensed under the BSD 3-Clause License (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * https://opensource.org/licenses/BSD-3-Clause 11 * 12 * Unless required by applicable law or agreed to in writing, software distributed 13 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 14 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 15 * specific language governing permissions and limitations under the License. 16 */ 17 18 package eurekaserver 19 20 import ( 21 "bytes" 22 "context" 23 "encoding/json" 24 "fmt" 25 "io" 26 "net/http" 27 "time" 28 29 "github.com/emicklei/go-restful/v3" 30 31 "github.com/polarismesh/polaris/common/version" 32 ) 33 34 type ReplicateWorker struct { 35 namespace string 36 peers []string 37 taskChannel chan *ReplicationInstance 38 ctx context.Context 39 } 40 41 const ( 42 batchMaxInterval = 5 * time.Second 43 batchReplicateSize = 10 44 ) 45 46 func NewReplicateWorker(ctx context.Context, namespace string, peers []string) *ReplicateWorker { 47 worker := &ReplicateWorker{ 48 namespace: namespace, 49 peers: peers, 50 taskChannel: make(chan *ReplicationInstance, 1000), 51 ctx: ctx, 52 } 53 go worker.batchReplicate() 54 return worker 55 } 56 57 func (r *ReplicateWorker) AddReplicateTask(task *ReplicationInstance) { 58 r.taskChannel <- task 59 } 60 61 func (r *ReplicateWorker) batchReplicate() { 62 batchTasks := make([]*ReplicationInstance, 0) 63 batchTicker := time.NewTicker(batchMaxInterval) 64 for { 65 select { 66 case <-r.ctx.Done(): 67 eurekalog.Infof("[EUREKA-SERVER] replicate worker done") 68 batchTicker.Stop() 69 case task := <-r.taskChannel: 70 batchTasks = append(batchTasks, task) 71 if len(batchTasks) == batchReplicateSize { 72 // replicate at once when batch size reach max threshold 73 r.doBatchReplicate(batchTasks) 74 batchTasks = make([]*ReplicationInstance, 0) 75 } 76 case <-batchTicker.C: 77 if len(batchTasks) > 0 { 78 // replicate when time interval reached 79 r.doBatchReplicate(batchTasks) 80 batchTasks = make([]*ReplicationInstance, 0) 81 } 82 } 83 } 84 } 85 86 func (r *ReplicateWorker) doBatchReplicate(tasks []*ReplicationInstance) { 87 request := &ReplicationList{ReplicationList: make([]*ReplicationInstance, 0, len(tasks))} 88 for _, task := range tasks { 89 if task.Action == actionHeartbeat { 90 request.ReplicationList = append(request.ReplicationList, &ReplicationInstance{ 91 AppName: task.AppName, 92 Id: task.Id, 93 LastDirtyTimestamp: task.LastDirtyTimestamp, 94 OverriddenStatus: task.OverriddenStatus, 95 Status: task.Status, 96 Action: task.Action, 97 }) 98 } else { 99 request.ReplicationList = append(request.ReplicationList, task) 100 } 101 } 102 jsonData, err := json.Marshal(request) 103 if nil != err { 104 eurekalog.Errorf("[EUREKA-SERVER] fail to marshal replicate tasks: %v", err) 105 return 106 } 107 replicateInfo := make([]string, 0, len(tasks)) 108 for _, task := range tasks { 109 replicateInfo = append(replicateInfo, fmt.Sprintf("%s:%s", task.Action, task.Id)) 110 } 111 eurekalog.Infof("start to send replicate text %s, peers %v", string(jsonData), r.peers) 112 for _, peer := range r.peers { 113 go r.doReplicateToPeer(peer, tasks, jsonData, replicateInfo) 114 } 115 } 116 117 func (r *ReplicateWorker) doReplicateToPeer( 118 peer string, tasks []*ReplicationInstance, jsonData []byte, replicateInfo []string) { 119 response, err := sendHttpRequest(r.namespace, peer, jsonData, replicateInfo) 120 if nil != err { 121 eurekalog.Errorf("[EUREKA-SERVER] fail to batch replicate to %s, err: %v", peer, err) 122 return 123 } 124 if len(response.ResponseList) == 0 { 125 return 126 } 127 for i, respInstance := range response.ResponseList { 128 if respInstance.StatusCode == http.StatusNotFound { 129 task := tasks[i] 130 if task.Action == actionHeartbeat { 131 eurekalog.Infof("[EUREKA-SERVER] instance %s of service %s not exists in %s, do register instance info %+v", 132 task.Id, task.AppName, peer, task.InstanceInfo) 133 // do the re-register 134 registerTask := &ReplicationInstance{ 135 AppName: task.AppName, 136 Id: task.Id, 137 LastDirtyTimestamp: task.LastDirtyTimestamp, 138 Status: StatusUp, 139 InstanceInfo: task.InstanceInfo, 140 Action: actionRegister, 141 } 142 r.AddReplicateTask(registerTask) 143 } 144 } 145 } 146 } 147 148 type ReplicateWorkers struct { 149 works map[string]*ReplicateWorker 150 } 151 152 func NewReplicateWorkers(ctx context.Context, namespacePeers map[string][]string) *ReplicateWorkers { 153 works := make(map[string]*ReplicateWorker) 154 for namespace, peers := range namespacePeers { 155 works[namespace] = NewReplicateWorker(ctx, namespace, peers) 156 } 157 return &ReplicateWorkers{ 158 works: works, 159 } 160 } 161 162 func (r *ReplicateWorkers) Get(namespace string) (*ReplicateWorker, bool) { 163 work, exist := r.works[namespace] 164 return work, exist 165 } 166 167 func sendHttpRequest(namespace string, peer string, 168 jsonData []byte, replicateInfo []string) (*ReplicationListResponse, error) { 169 client := &http.Client{} 170 req, err := http.NewRequest(http.MethodPost, 171 fmt.Sprintf("http://%s/eureka/peerreplication/batch/", peer), bytes.NewBuffer(jsonData)) 172 if nil != err { 173 eurekalog.Errorf("[EUREKA-SERVER] fail to create replicate request: %v", err) 174 return nil, err 175 } 176 req.Header.Set(headerIdentityName, valueIdentityName) 177 req.Header.Set(headerIdentityVersion, version.Version) 178 req.Header.Set(restful.HEADER_ContentType, restful.MIME_JSON) 179 req.Header.Set(restful.HEADER_Accept, restful.MIME_JSON) 180 if len(namespace) != 0 { 181 req.Header.Set(HeaderNamespace, namespace) 182 } 183 response, err := client.Do(req) 184 if err != nil { 185 eurekalog.Errorf("[EUREKA-SERVER] fail to send replicate request: %v", err) 186 return nil, err 187 } 188 defer func() { 189 if nil != response && nil != response.Body { 190 _ = response.Body.Close() 191 } 192 }() 193 respStr, _ := io.ReadAll(response.Body) 194 respObj := &ReplicationListResponse{} 195 err = json.Unmarshal(respStr, respObj) 196 if nil != err { 197 eurekalog.Errorf("[EUREKA-SERVER] fail unmarshal text %s to ReplicationListResponse: %v", string(respStr), err) 198 return nil, err 199 } 200 eurekalog.Infof("[EUREKA-SERVER] success to replicate to %s, instances %v", peer, replicateInfo) 201 return respObj, nil 202 }