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 }