forked from mirror/ledisdb
283 lines
6.7 KiB
Go
283 lines
6.7 KiB
Go
package server
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"os"
|
|
"runtime"
|
|
"runtime/debug"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/siddontang/go/sync2"
|
|
"github.com/siddontang/ledisdb/ledis"
|
|
)
|
|
|
|
type info struct {
|
|
sync.Mutex
|
|
|
|
app *App
|
|
|
|
Server struct {
|
|
OS string
|
|
ProceessId int
|
|
}
|
|
|
|
Replication struct {
|
|
PubLogNum sync2.AtomicInt64
|
|
PubLogAckNum sync2.AtomicInt64
|
|
PubLogTotalAckTime sync2.AtomicDuration
|
|
|
|
MasterLastLogID sync2.AtomicUint64
|
|
}
|
|
}
|
|
|
|
func newInfo(app *App) (i *info, err error) {
|
|
i = new(info)
|
|
|
|
i.app = app
|
|
|
|
i.Server.OS = runtime.GOOS
|
|
i.Server.ProceessId = os.Getpid()
|
|
|
|
return i, nil
|
|
}
|
|
|
|
func (i *info) Close() {
|
|
|
|
}
|
|
|
|
func getMemoryHuman(m uint64) string {
|
|
if m > GB {
|
|
return fmt.Sprintf("%0.3fG", float64(m)/float64(GB))
|
|
} else if m > MB {
|
|
return fmt.Sprintf("%0.3fM", float64(m)/float64(MB))
|
|
} else if m > KB {
|
|
return fmt.Sprintf("%0.3fK", float64(m)/float64(KB))
|
|
} else {
|
|
return fmt.Sprintf("%d", m)
|
|
}
|
|
}
|
|
|
|
func (i *info) Dump(section string) []byte {
|
|
buf := &bytes.Buffer{}
|
|
switch strings.ToLower(section) {
|
|
case "":
|
|
i.dumpAll(buf)
|
|
case "server":
|
|
i.dumpServer(buf)
|
|
case "mem":
|
|
i.dumpMem(buf)
|
|
case "gc":
|
|
i.dumpGC(buf)
|
|
case "store":
|
|
i.dumpStore(buf)
|
|
case "replication":
|
|
i.dumpReplication(buf)
|
|
default:
|
|
buf.WriteString(fmt.Sprintf("# %s\r\n", section))
|
|
}
|
|
|
|
return buf.Bytes()
|
|
}
|
|
|
|
type infoPair struct {
|
|
Key string
|
|
Value interface{}
|
|
}
|
|
|
|
func (i *info) dumpAll(buf *bytes.Buffer) {
|
|
i.dumpServer(buf)
|
|
buf.Write(Delims)
|
|
i.dumpStore(buf)
|
|
buf.Write(Delims)
|
|
i.dumpMem(buf)
|
|
buf.Write(Delims)
|
|
i.dumpGC(buf)
|
|
buf.Write(Delims)
|
|
i.dumpReplication(buf)
|
|
}
|
|
|
|
func (i *info) dumpServer(buf *bytes.Buffer) {
|
|
buf.WriteString("# Server\r\n")
|
|
|
|
i.dumpPairs(buf, infoPair{"os", i.Server.OS},
|
|
infoPair{"process_id", i.Server.ProceessId},
|
|
infoPair{"addr", i.app.cfg.Addr},
|
|
infoPair{"http_addr", i.app.cfg.HttpAddr},
|
|
infoPair{"readonly", i.app.cfg.Readonly},
|
|
infoPair{"goroutine_num", runtime.NumGoroutine()},
|
|
infoPair{"cgo_call_num", runtime.NumCgoCall()},
|
|
infoPair{"resp_client_num", i.app.respClientNum()},
|
|
infoPair{"ledisdb_version", ledis.Version},
|
|
)
|
|
}
|
|
|
|
func (i *info) dumpMem(buf *bytes.Buffer) {
|
|
buf.WriteString("# Mem\r\n")
|
|
|
|
var mem runtime.MemStats
|
|
runtime.ReadMemStats(&mem)
|
|
|
|
i.dumpPairs(buf, infoPair{"mem_alloc", getMemoryHuman(mem.Alloc)},
|
|
infoPair{"mem_sys", getMemoryHuman(mem.Sys)},
|
|
infoPair{"mem_looksups", getMemoryHuman(mem.Lookups)},
|
|
infoPair{"mem_mallocs", getMemoryHuman(mem.Mallocs)},
|
|
infoPair{"mem_frees", getMemoryHuman(mem.Frees)},
|
|
infoPair{"mem_total", getMemoryHuman(mem.TotalAlloc)},
|
|
infoPair{"mem_heap_alloc", getMemoryHuman(mem.HeapAlloc)},
|
|
infoPair{"mem_heap_sys", getMemoryHuman(mem.HeapSys)},
|
|
infoPair{"mem_head_idle", getMemoryHuman(mem.HeapIdle)},
|
|
infoPair{"mem_head_inuse", getMemoryHuman(mem.HeapInuse)},
|
|
infoPair{"mem_head_released", getMemoryHuman(mem.HeapReleased)},
|
|
infoPair{"mem_head_objects", mem.HeapObjects},
|
|
)
|
|
}
|
|
|
|
const (
|
|
gcTimeFormat = "2006/01/02 15:04:05.000"
|
|
)
|
|
|
|
func (i *info) dumpGC(buf *bytes.Buffer) {
|
|
buf.WriteString("# GC\r\n")
|
|
|
|
count := 5
|
|
|
|
var st debug.GCStats
|
|
st.Pause = make([]time.Duration, count)
|
|
// st.PauseQuantiles = make([]time.Duration, count)
|
|
debug.ReadGCStats(&st)
|
|
|
|
h := make([]string, 0, count)
|
|
|
|
for i := 0; i < count && i < len(st.Pause); i++ {
|
|
h = append(h, st.Pause[i].String())
|
|
}
|
|
|
|
i.dumpPairs(buf, infoPair{"gc_last_time", st.LastGC.Format(gcTimeFormat)},
|
|
infoPair{"gc_num", st.NumGC},
|
|
infoPair{"gc_pause_total", st.PauseTotal.String()},
|
|
infoPair{"gc_pause_history", strings.Join(h, ",")},
|
|
)
|
|
}
|
|
|
|
func (i *info) dumpStore(buf *bytes.Buffer) {
|
|
buf.WriteString("# Store\r\n")
|
|
|
|
s := i.app.ldb.StoreStat()
|
|
|
|
// getNum := s.GetNum.Get()
|
|
// getTotalTime := s.GetTotalTime.Get()
|
|
|
|
// gt := int64(0)
|
|
// if getNum > 0 {
|
|
// gt = getTotalTime.Nanoseconds() / (getNum * 1e3)
|
|
// }
|
|
|
|
// commitNum := s.BatchCommitNum.Get()
|
|
// commitTotalTime := s.BatchCommitTotalTime.Get()
|
|
|
|
// ct := int64(0)
|
|
// if commitNum > 0 {
|
|
// ct = commitTotalTime.Nanoseconds() / (commitNum * 1e3)
|
|
// }
|
|
|
|
i.dumpPairs(buf, infoPair{"name", i.app.cfg.DBName},
|
|
infoPair{"get", s.GetNum},
|
|
infoPair{"get_missing", s.GetMissingNum},
|
|
infoPair{"put", s.PutNum},
|
|
infoPair{"delete", s.DeleteNum},
|
|
infoPair{"get_total_time", s.GetTotalTime.Get().String()},
|
|
infoPair{"iter", s.IterNum},
|
|
infoPair{"iter_seek", s.IterSeekNum},
|
|
infoPair{"iter_close", s.IterCloseNum},
|
|
infoPair{"batch_commit", s.BatchCommitNum},
|
|
infoPair{"batch_commit_total_time", s.BatchCommitTotalTime.Get().String()},
|
|
)
|
|
}
|
|
|
|
func (i *info) dumpReplication(buf *bytes.Buffer) {
|
|
buf.WriteString("# Replication\r\n")
|
|
|
|
p := []infoPair{}
|
|
i.app.slock.Lock()
|
|
slaves := make([]string, 0, len(i.app.slaves))
|
|
for _, s := range i.app.slaves {
|
|
slaves = append(slaves, s.slaveListeningAddr)
|
|
}
|
|
i.app.slock.Unlock()
|
|
|
|
i.app.m.Lock()
|
|
slaveof := i.app.cfg.SlaveOf
|
|
i.app.m.Unlock()
|
|
|
|
isSlave := len(slaveof) > 0
|
|
|
|
if isSlave {
|
|
p = append(p, infoPair{"role", "slave"})
|
|
} else {
|
|
p = append(p, infoPair{"role", "master"})
|
|
}
|
|
|
|
num := i.Replication.PubLogNum.Get()
|
|
p = append(p, infoPair{"pub_log_num", num})
|
|
|
|
ackNum := i.Replication.PubLogAckNum.Get()
|
|
totalTime := i.Replication.PubLogTotalAckTime.Get().Nanoseconds() / 1e6
|
|
if ackNum != 0 {
|
|
p = append(p, infoPair{"pub_log_ack_per_time", totalTime / ackNum})
|
|
} else {
|
|
p = append(p, infoPair{"pub_log_ack_per_time", 0})
|
|
}
|
|
|
|
p = append(p, infoPair{"slaveof", slaveof})
|
|
|
|
if len(slaves) > 0 {
|
|
p = append(p, infoPair{"slaves", strings.Join(slaves, ",")})
|
|
}
|
|
|
|
s, _ := i.app.ldb.ReplicationStat()
|
|
if s != nil {
|
|
p = append(p, infoPair{"last_log_id", s.LastID})
|
|
p = append(p, infoPair{"first_log_id", s.FirstID})
|
|
p = append(p, infoPair{"commit_log_id", s.CommitID})
|
|
} else {
|
|
p = append(p, infoPair{"last_log_id", 0})
|
|
p = append(p, infoPair{"first_log_id", 0})
|
|
p = append(p, infoPair{"commit_log_id", 0})
|
|
}
|
|
|
|
p = append(p, infoPair{"master_last_log_id", i.Replication.MasterLastLogID.Get()})
|
|
|
|
if isSlave {
|
|
// add some redis slave replication info for outer failover service :-)
|
|
state := i.app.m.state.Get()
|
|
if state == replSyncState || state == replConnectedState {
|
|
p = append(p, infoPair{"master_link_status", "up"})
|
|
} else {
|
|
p = append(p, infoPair{"master_link_status", "down"})
|
|
}
|
|
|
|
// here, all the slaves have same priority now
|
|
p = append(p, infoPair{"slave_priority", 100})
|
|
if s != nil {
|
|
if s.LastID > 0 {
|
|
p = append(p, infoPair{"slave_repl_offset", s.LastID})
|
|
} else {
|
|
p = append(p, infoPair{"slave_repl_offset", s.CommitID})
|
|
}
|
|
} else {
|
|
p = append(p, infoPair{"slave_repl_offset", 0})
|
|
}
|
|
}
|
|
|
|
i.dumpPairs(buf, p...)
|
|
}
|
|
|
|
func (i *info) dumpPairs(buf *bytes.Buffer, pairs ...infoPair) {
|
|
for _, v := range pairs {
|
|
buf.WriteString(fmt.Sprintf("%s:%v\r\n", v.Key, v.Value))
|
|
}
|
|
}
|