github.com/nacos-group/nacos-sdk-go@v1.1.4/clients/naming_client/push_receiver.go (about)

     1  /*
     2   * Copyright 1999-2020 Alibaba Group Holding Ltd.
     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  package naming_client
    18  
    19  import (
    20  	"bytes"
    21  	"compress/gzip"
    22  	"encoding/json"
    23  	"io/ioutil"
    24  	"math/rand"
    25  	"net"
    26  	"strconv"
    27  	"time"
    28  
    29  	"github.com/nacos-group/nacos-sdk-go/common/logger"
    30  	"github.com/nacos-group/nacos-sdk-go/util"
    31  )
    32  
    33  type PushReceiver struct {
    34  	port        int
    35  	host        string
    36  	hostReactor *HostReactor
    37  }
    38  
    39  type PushData struct {
    40  	PushType    string `json:"type"`
    41  	Data        string `json:"data"`
    42  	LastRefTime int64  `json:"lastRefTime"`
    43  }
    44  
    45  var (
    46  	GZIP_MAGIC = []byte("\x1F\x8B")
    47  )
    48  
    49  func NewPushReceiver(hostReactor *HostReactor) *PushReceiver {
    50  	pr := PushReceiver{
    51  		hostReactor: hostReactor,
    52  	}
    53  	pr.startServer()
    54  	return &pr
    55  }
    56  
    57  func (us *PushReceiver) tryListen() (*net.UDPConn, bool) {
    58  	addr, err := net.ResolveUDPAddr("udp", us.host+":"+strconv.Itoa(us.port))
    59  	if err != nil {
    60  		logger.Errorf("can't resolve address,err: %+v", err)
    61  		return nil, false
    62  	}
    63  
    64  	conn, err := net.ListenUDP("udp", addr)
    65  	if err != nil {
    66  		logger.Errorf("error listening %s:%d,err:%+v", us.host, us.port, err)
    67  		return nil, false
    68  	}
    69  
    70  	return conn, true
    71  }
    72  
    73  func (us *PushReceiver) getConn() *net.UDPConn {
    74  	r := rand.New(rand.NewSource(time.Now().UnixNano()))
    75  
    76  	for i := 0; i < 3; i++ {
    77  		port := r.Intn(1000) + 54951
    78  		us.port = port
    79  		conn, ok := us.tryListen()
    80  		if ok {
    81  			logger.Infof("udp server start, port: " + strconv.Itoa(port))
    82  			return conn
    83  		}
    84  		if !ok && i == 2 {
    85  			logger.Errorf("failed to start udp server after trying 3 times.")
    86  		}
    87  	}
    88  	return nil
    89  }
    90  
    91  func (us *PushReceiver) startServer() {
    92  	conn := us.getConn()
    93  	if conn == nil {
    94  		return
    95  	}
    96  	go func() {
    97  		defer conn.Close()
    98  		for {
    99  			us.handleClient(conn)
   100  		}
   101  	}()
   102  }
   103  
   104  func (us *PushReceiver) handleClient(conn *net.UDPConn) {
   105  
   106  	if conn == nil {
   107  		time.Sleep(time.Second * 5)
   108  		conn = us.getConn()
   109  		if conn == nil {
   110  			return
   111  		}
   112  	}
   113  
   114  	data := make([]byte, 4024)
   115  	n, remoteAddr, err := conn.ReadFromUDP(data)
   116  	if err != nil {
   117  		logger.Errorf("failed to read UDP msg because of %+v", err)
   118  		return
   119  	}
   120  
   121  	s := TryDecompressData(data[:n])
   122  	logger.Info("receive push: "+s+" from: ", remoteAddr)
   123  
   124  	var pushData PushData
   125  	err1 := json.Unmarshal([]byte(s), &pushData)
   126  	if err1 != nil {
   127  		logger.Infof("failed to process push data.err:%+v", err1)
   128  		return
   129  	}
   130  	ack := make(map[string]string)
   131  
   132  	if pushData.PushType == "dom" || pushData.PushType == "service" {
   133  		us.hostReactor.ProcessServiceJson(pushData.Data)
   134  
   135  		ack["type"] = "push-ack"
   136  		ack["lastRefTime"] = strconv.FormatInt(pushData.LastRefTime, 10)
   137  		ack["data"] = ""
   138  
   139  	} else if pushData.PushType == "dump" {
   140  		ack["type"] = "dump-ack"
   141  		ack["lastRefTime"] = strconv.FormatInt(pushData.LastRefTime, 10)
   142  		ack["data"] = util.ToJsonString(us.hostReactor.serviceInfoMap)
   143  	} else {
   144  		ack["type"] = "unknow-ack"
   145  		ack["lastRefTime"] = strconv.FormatInt(pushData.LastRefTime, 10)
   146  		ack["data"] = ""
   147  	}
   148  
   149  	bs, _ := json.Marshal(ack)
   150  	c, err := conn.WriteToUDP(bs, remoteAddr)
   151  	if err != nil {
   152  		logger.Errorf("WriteToUDP failed,return:%d,err:%+v", c, err)
   153  	}
   154  }
   155  
   156  func TryDecompressData(data []byte) string {
   157  
   158  	if !IsGzipFile(data) {
   159  		return string(data)
   160  	}
   161  	reader, err := gzip.NewReader(bytes.NewReader(data))
   162  
   163  	if err != nil {
   164  		logger.Errorf("failed to decompress gzip data,err:%+v", err)
   165  		return ""
   166  	}
   167  
   168  	defer reader.Close()
   169  	bs, err := ioutil.ReadAll(reader)
   170  
   171  	if err != nil {
   172  		logger.Errorf("failed to decompress gzip data,err:%+v", err)
   173  		return ""
   174  	}
   175  
   176  	return string(bs)
   177  }
   178  
   179  func IsGzipFile(data []byte) bool {
   180  	if len(data) < 2 {
   181  		return false
   182  	}
   183  
   184  	return bytes.HasPrefix(data, GZIP_MAGIC)
   185  }