func (this *Redis) runPing(zkzone *zk.ZkZone) { var wg sync.WaitGroup allRedis := zkzone.AllRedis() this.topInfos = make([]redisTopInfo, 0, len(allRedis)) for _, hostPort := range allRedis { host, port, err := net.SplitHostPort(hostPort) if err != nil { this.Ui.Error(hostPort) continue } nport, err := strconv.Atoi(port) if err != nil || nport < 0 { this.Ui.Error(hostPort) continue } wg.Add(1) go func(wg *sync.WaitGroup, host string, port int) { defer wg.Done() t0 := time.Now() spec := redis.DefaultSpec().Host(host).Port(port) client, err := redis.NewSynchClientWithSpec(spec) if err != nil { this.Ui.Error(fmt.Sprintf("[%s:%d] %v", host, port, err)) return } defer client.Quit() if err := client.Ping(); err != nil { this.Ui.Error(fmt.Sprintf("[%s:%d] %v", host, port, err)) return } latency := time.Since(t0) this.mu.Lock() this.topInfos = append(this.topInfos, redisTopInfo{ host: host, port: port, t0: t0, latency: latency, }) this.mu.Unlock() }(&wg, host, nport) } wg.Wait() latency := metrics.NewRegisteredHistogram("redis.latency", metrics.DefaultRegistry, metrics.NewExpDecaySample(1028, 0.015)) sortutil.AscByField(this.topInfos, "latency") lines := []string{"#|Host|Port|latency"} if this.debug { lines = []string{"#|Host|Port|StartedAt|latency"} } for i, info := range this.topInfos { latency.Update(info.latency.Nanoseconds() / 1e6) if this.debug { lines = append(lines, fmt.Sprintf("%4d|%s|%d|%s|%s", i+1, info.host, info.port, info.t0, info.latency)) } else { lines = append(lines, fmt.Sprintf("%4d|%s|%d|%s", i+1, info.host, info.port, info.latency)) } } this.Ui.Output(columnize.SimpleFormat(lines)) // summary ps := latency.Percentiles([]float64{0.7, 0.90, 0.95, 0.99, 0.999}) this.Ui.Info(fmt.Sprintf("N:%d Min:%dms Max:%dms Mean:%.1fms 70%%:%1.fms 90%%:%.1fms 95%%:%.1fms 99%%:%.1fms", latency.Count(), latency.Min(), latency.Max(), latency.Mean(), ps[0], ps[1], ps[2], ps[3])) }
func (this *Redis) runTop(zkzone *zk.ZkZone, interval time.Duration) { termui.Init() this.mainScreen = true this.w, this.h = termbox.Size() this.rows = this.h - 3 // head,max/total if this.batchMode { termbox.Close() } else { defer termui.Close() termbox.SetInputMode(termbox.InputEsc | termbox.InputMouse) eventChan := make(chan termbox.Event, 16) go this.handleEvents(eventChan) go func() { for { ev := termbox.PollEvent() eventChan <- ev } }() this.drawSplash() } this.topInfos = make([]redisTopInfo, 0, 100) this.topInfos1 = make([]redisTopInfo, 0, 100) for { var wg sync.WaitGroup this.mu.Lock() this.topInfos1 = this.topInfos1[:0] freezedPorts := make(map[string]struct{}) // clone freezedPorts to avoid concurrent map access if len(this.freezedPorts) > 0 { for port, _ := range this.freezedPorts { freezedPorts[port] = struct{}{} } } this.mu.Unlock() for _, hostPort := range zkzone.AllRedis() { host, port, err := net.SplitHostPort(hostPort) if err != nil { log.Error("invalid redis instance: %s", hostPort) continue } if len(this.ports) > 0 { if _, present := this.ports[port]; !present { continue } } if len(freezedPorts) > 0 { if _, present := freezedPorts[port]; !present { continue } } nport, err := strconv.Atoi(port) if err != nil || nport < 0 { log.Error("invalid redis instance: %s", hostPort) continue } wg.Add(1) go this.updateRedisInfo(&wg, host, nport) // update happens on topInfos1 } wg.Wait() this.mu.Lock() this.topInfos1, this.topInfos = this.topInfos, this.topInfos1 this.mu.Unlock() if this.mainScreen { this.render() } select { case <-time.After(interval): case <-this.quit: return } } }