github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/common/monitor/port.go (about)

     1  package monitor
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/server"
     7  	"github.com/spf13/viper"
     8  	"os/exec"
     9  	"strconv"
    10  	"strings"
    11  	"sync"
    12  )
    13  
    14  var (
    15  	portMonitor     *PortMonitor
    16  	initPortMonitor sync.Once
    17  )
    18  
    19  // GetPortMonitor gets the global instance of PortMonitor
    20  func GetPortMonitor() *PortMonitor {
    21  	initPortMonitor.Do(func() {
    22  		portMonitor = NewPortMonitor(parsePorts(viper.GetString(server.FlagPortMonitor)))
    23  	})
    24  
    25  	return portMonitor
    26  }
    27  
    28  // PortMonitor - structure of monitor for ports
    29  type PortMonitor struct {
    30  	enable bool
    31  	ports  []uint64
    32  	// max total connecting numbers in one round
    33  	maxConnectingNumberTotal int
    34  	// connecting number of each port in one round
    35  	connectingMap map[uint64]int
    36  	// max connecting number record of each port
    37  	connectingMaxMap map[uint64]int
    38  }
    39  
    40  // NewPortMonitor creates a new instance of PortMonitor
    41  func NewPortMonitor(ports []string) *PortMonitor {
    42  	if len(ports) == 0 {
    43  		// disable the port monitor
    44  		return &PortMonitor{
    45  			enable: false,
    46  		}
    47  	}
    48  	// check port format
    49  	var portsUint64 []uint64
    50  	connectingMaxMap := make(map[uint64]int)
    51  	for _, portStr := range ports {
    52  		port := ParsePort(portStr)
    53  		portsUint64 = append(portsUint64, port)
    54  		// init connectingMaxMap with -1
    55  		connectingMaxMap[port] = -1
    56  	}
    57  
    58  	return &PortMonitor{
    59  		enable:                   true,
    60  		ports:                    portsUint64,
    61  		connectingMap:            make(map[uint64]int),
    62  		connectingMaxMap:         connectingMaxMap,
    63  		maxConnectingNumberTotal: -1,
    64  	}
    65  }
    66  
    67  // reset resets the status of PortMonitor
    68  func (pm *PortMonitor) reset() {
    69  	for _, port := range pm.ports {
    70  		pm.connectingMap[port] = -1
    71  	}
    72  }
    73  
    74  // getConnectingNumbers gets the connecting numbers from ports
    75  func (pm *PortMonitor) getConnectingNumbers() error {
    76  	var connectingNumTotal int
    77  	for _, port := range pm.ports {
    78  		connectingNumber, err := getConnectingNumbersFromPort(port)
    79  		if err != nil {
    80  			return fmt.Errorf("failed to get connecting numbers of port %d: %s", port, err.Error())
    81  		}
    82  
    83  		// update max connecting map
    84  		if connectingNumber > pm.connectingMaxMap[port] {
    85  			pm.connectingMaxMap[port] = connectingNumber
    86  		}
    87  
    88  		// update connecting map for this round
    89  		pm.connectingMap[port] = connectingNumber
    90  		connectingNumTotal += connectingNumber
    91  	}
    92  
    93  	// max total check
    94  	if connectingNumTotal > pm.maxConnectingNumberTotal {
    95  		pm.maxConnectingNumberTotal = connectingNumTotal
    96  	}
    97  	return nil
    98  }
    99  
   100  // Run starts monitoring
   101  func (pm *PortMonitor) Run() error {
   102  	// PortMonitor disabled
   103  	if !pm.enable {
   104  		return nil
   105  	}
   106  
   107  	pm.reset()
   108  	err := pm.getConnectingNumbers()
   109  	if err != nil {
   110  		return err
   111  	}
   112  
   113  	return nil
   114  }
   115  
   116  // GetResultString gets the format string result
   117  func (pm *PortMonitor) GetResultString() string {
   118  	// PortMonitor disabled
   119  	if !pm.enable {
   120  		return ""
   121  	}
   122  
   123  	var buffer bytes.Buffer
   124  
   125  	// connecting number of each port in this round
   126  	for _, port := range pm.ports {
   127  		buffer.WriteString(fmt.Sprintf("%d<%d>, ", port, pm.connectingMap[port]))
   128  	}
   129  
   130  	// max connecting number of each port
   131  	for _, port := range pm.ports {
   132  		buffer.WriteString(fmt.Sprintf("%d-Max<%d>, ", port, pm.connectingMaxMap[port]))
   133  	}
   134  
   135  	// statistics
   136  	buffer.WriteString(fmt.Sprintf("MaxConNum<%d>", pm.maxConnectingNumberTotal))
   137  	return buffer.String()
   138  }
   139  
   140  // GetConnectingMap gets connectingMap
   141  func (pm *PortMonitor) GetConnectingMap() map[uint64]int {
   142  	return pm.connectingMap
   143  }
   144  
   145  // tools function
   146  func getConnectingNumbersFromPort(port uint64) (int, error) {
   147  	// get connecting number from a shell command running
   148  	shellCmd := fmt.Sprintf("netstat -nat | grep -i %d | wc -l", port)
   149  	resBytes, err := exec.Command("/bin/sh", "-c", shellCmd).Output()
   150  	if err != nil {
   151  		return -1, err
   152  	}
   153  
   154  	// data washing
   155  	return strconv.Atoi(string(bytes.TrimSpace(resBytes)))
   156  }
   157  
   158  func parsePorts(inputStr string) []string {
   159  	inputStr = strings.TrimSpace(inputStr)
   160  	if len(inputStr) == 0 {
   161  		// nothing input
   162  		return nil
   163  	}
   164  
   165  	return strings.Split(inputStr, ",")
   166  }
   167  
   168  // ParsePort parses port into uint from a string
   169  func ParsePort(inputStr string) uint64 {
   170  	inputStr = strings.TrimSpace(inputStr)
   171  	port, err := strconv.ParseUint(inputStr, 10, 64)
   172  	if err != nil {
   173  		panic(fmt.Sprintf("fail to convert port string %s to integer: %s", inputStr, err.Error()))
   174  	}
   175  
   176  	if port > 65535 {
   177  		panic(fmt.Sprintf("invalid port %d. It should be between 0 and 65535", port))
   178  	}
   179  
   180  	return port
   181  }