mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
mrege and change peerstats to followersstats
This commit is contained in:
commit
2eb0625f15
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,4 +1,5 @@
|
|||||||
src/
|
src/
|
||||||
pkg/
|
pkg/
|
||||||
./etcd
|
/etcd
|
||||||
release_version.go
|
release_version.go
|
||||||
|
/machine*
|
||||||
|
2
build
2
build
@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/bin/sh
|
||||||
|
|
||||||
ETCD_PACKAGE=github.com/coreos/etcd
|
ETCD_PACKAGE=github.com/coreos/etcd
|
||||||
export GOPATH="${PWD}"
|
export GOPATH="${PWD}"
|
||||||
|
10
command.go
10
command.go
@ -170,6 +170,12 @@ func (c *JoinCommand) Apply(raftServer *raft.Server) (interface{}, error) {
|
|||||||
value := fmt.Sprintf("raft=%s&etcd=%s&raftVersion=%s", c.RaftURL, c.EtcdURL, c.RaftVersion)
|
value := fmt.Sprintf("raft=%s&etcd=%s&raftVersion=%s", c.RaftURL, c.EtcdURL, c.RaftVersion)
|
||||||
etcdStore.Set(key, value, time.Unix(0, 0), raftServer.CommitIndex())
|
etcdStore.Set(key, value, time.Unix(0, 0), raftServer.CommitIndex())
|
||||||
|
|
||||||
|
// add peer stats
|
||||||
|
if c.Name != r.Name() {
|
||||||
|
r.followersStats.Followers[c.Name] = &raftFollowerStats{}
|
||||||
|
r.followersStats.Followers[c.Name].Latency.Minimum = 1 << 63
|
||||||
|
}
|
||||||
|
|
||||||
return b, err
|
return b, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,7 +200,9 @@ func (c *RemoveCommand) Apply(raftServer *raft.Server) (interface{}, error) {
|
|||||||
key := path.Join("_etcd/machines", c.Name)
|
key := path.Join("_etcd/machines", c.Name)
|
||||||
|
|
||||||
_, err := etcdStore.Delete(key, raftServer.CommitIndex())
|
_, err := etcdStore.Delete(key, raftServer.CommitIndex())
|
||||||
delete(r.peersStats, c.Name)
|
|
||||||
|
// delete from stats
|
||||||
|
delete(r.followersStats.Followers, c.Name)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []byte{0}, err
|
return []byte{0}, err
|
||||||
|
@ -22,7 +22,7 @@ func NewEtcdMuxer() *http.ServeMux {
|
|||||||
etcdMux.Handle("/"+version+"/watch/", errorHandler(WatchHttpHandler))
|
etcdMux.Handle("/"+version+"/watch/", errorHandler(WatchHttpHandler))
|
||||||
etcdMux.Handle("/"+version+"/leader", errorHandler(LeaderHttpHandler))
|
etcdMux.Handle("/"+version+"/leader", errorHandler(LeaderHttpHandler))
|
||||||
etcdMux.Handle("/"+version+"/machines", errorHandler(MachinesHttpHandler))
|
etcdMux.Handle("/"+version+"/machines", errorHandler(MachinesHttpHandler))
|
||||||
etcdMux.Handle("/"+version+"/stats", errorHandler(StatsHttpHandler))
|
etcdMux.Handle("/"+version+"/stats/", errorHandler(StatsHttpHandler))
|
||||||
etcdMux.Handle("/version", errorHandler(VersionHttpHandler))
|
etcdMux.Handle("/version", errorHandler(VersionHttpHandler))
|
||||||
etcdMux.HandleFunc("/test/", TestHttpHandler)
|
etcdMux.HandleFunc("/test/", TestHttpHandler)
|
||||||
return etcdMux
|
return etcdMux
|
||||||
@ -167,22 +167,8 @@ func dispatch(c Command, w http.ResponseWriter, req *http.Request, etcd bool) er
|
|||||||
return etcdErr.NewError(300, "")
|
return etcdErr.NewError(300, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// tell the client where is the leader
|
redirect(leader, etcd, w, req)
|
||||||
path := req.URL.Path
|
|
||||||
|
|
||||||
var url string
|
|
||||||
|
|
||||||
if etcd {
|
|
||||||
etcdAddr, _ := nameToEtcdURL(leader)
|
|
||||||
url = etcdAddr + path
|
|
||||||
} else {
|
|
||||||
raftAddr, _ := nameToRaftURL(leader)
|
|
||||||
url = raftAddr + path
|
|
||||||
}
|
|
||||||
|
|
||||||
debugf("Redirect to %s", url)
|
|
||||||
|
|
||||||
http.Redirect(w, req, url, http.StatusTemporaryRedirect)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return etcdErr.NewError(300, "")
|
return etcdErr.NewError(300, "")
|
||||||
@ -227,9 +213,28 @@ func VersionHttpHandler(w http.ResponseWriter, req *http.Request) error {
|
|||||||
|
|
||||||
// Handler to return the basic stats of etcd
|
// Handler to return the basic stats of etcd
|
||||||
func StatsHttpHandler(w http.ResponseWriter, req *http.Request) error {
|
func StatsHttpHandler(w http.ResponseWriter, req *http.Request) error {
|
||||||
w.WriteHeader(http.StatusOK)
|
option := req.URL.Path[len("/v1/stats/"):]
|
||||||
w.Write(etcdStore.Stats())
|
|
||||||
w.Write(r.Stats())
|
switch option {
|
||||||
|
case "self":
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write(r.Stats())
|
||||||
|
case "leader":
|
||||||
|
if r.State() == raft.Leader {
|
||||||
|
w.Write(r.PeerStats())
|
||||||
|
} else {
|
||||||
|
leader := r.Leader()
|
||||||
|
// current no leader
|
||||||
|
if leader == "" {
|
||||||
|
return etcdErr.NewError(300, "")
|
||||||
|
}
|
||||||
|
redirect(leader, true, w, req)
|
||||||
|
}
|
||||||
|
case "store":
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write(etcdStore.Stats())
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,15 +17,15 @@ import (
|
|||||||
|
|
||||||
type raftServer struct {
|
type raftServer struct {
|
||||||
*raft.Server
|
*raft.Server
|
||||||
version string
|
version string
|
||||||
joinIndex uint64
|
joinIndex uint64
|
||||||
name string
|
name string
|
||||||
url string
|
url string
|
||||||
listenHost string
|
listenHost string
|
||||||
tlsConf *TLSConfig
|
tlsConf *TLSConfig
|
||||||
tlsInfo *TLSInfo
|
tlsInfo *TLSInfo
|
||||||
peersStats map[string]*raftPeerStats
|
followersStats *raftFollowersStats
|
||||||
serverStats *raftServerStats
|
serverStats *raftServerStats
|
||||||
}
|
}
|
||||||
|
|
||||||
var r *raftServer
|
var r *raftServer
|
||||||
@ -48,7 +48,10 @@ func newRaftServer(name string, url string, listenHost string, tlsConf *TLSConfi
|
|||||||
listenHost: listenHost,
|
listenHost: listenHost,
|
||||||
tlsConf: tlsConf,
|
tlsConf: tlsConf,
|
||||||
tlsInfo: tlsInfo,
|
tlsInfo: tlsInfo,
|
||||||
peersStats: make(map[string]*raftPeerStats),
|
followersStats: &raftFollowersStats{
|
||||||
|
Leader: name,
|
||||||
|
Followers: make(map[string]*raftFollowerStats),
|
||||||
|
},
|
||||||
serverStats: &raftServerStats{
|
serverStats: &raftServerStats{
|
||||||
StartTime: time.Now(),
|
StartTime: time.Now(),
|
||||||
sendRateQueue: &statsQueue{
|
sendRateQueue: &statsQueue{
|
||||||
@ -63,7 +66,6 @@ func newRaftServer(name string, url string, listenHost string, tlsConf *TLSConfi
|
|||||||
|
|
||||||
// Start the raft server
|
// Start the raft server
|
||||||
func (r *raftServer) ListenAndServe() {
|
func (r *raftServer) ListenAndServe() {
|
||||||
|
|
||||||
// Setup commands.
|
// Setup commands.
|
||||||
registerCommands()
|
registerCommands()
|
||||||
|
|
||||||
@ -282,7 +284,7 @@ func joinByMachine(s *raft.Server, machine string, scheme string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *raftServer) Stats() []byte {
|
func (r *raftServer) Stats() []byte {
|
||||||
r.serverStats.LeaderUptime = time.Now().Sub(r.serverStats.leaderStartTime).String()
|
r.serverStats.LeaderInfo.Uptime = time.Now().Sub(r.serverStats.LeaderInfo.startTime).String()
|
||||||
|
|
||||||
queue := r.serverStats.sendRateQueue
|
queue := r.serverStats.sendRateQueue
|
||||||
|
|
||||||
@ -292,20 +294,17 @@ func (r *raftServer) Stats() []byte {
|
|||||||
|
|
||||||
r.serverStats.RecvingPkgRate, r.serverStats.RecvingBandwidthRate = queue.Rate()
|
r.serverStats.RecvingPkgRate, r.serverStats.RecvingBandwidthRate = queue.Rate()
|
||||||
|
|
||||||
sBytes, err := json.Marshal(r.serverStats)
|
b, _ := json.Marshal(r.serverStats)
|
||||||
|
|
||||||
if err != nil {
|
return b
|
||||||
warn(err)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
func (r *raftServer) PeerStats() []byte {
|
||||||
if r.State() == raft.Leader {
|
if r.State() == raft.Leader {
|
||||||
pBytes, _ := json.Marshal(r.peersStats)
|
b, _ := json.Marshal(r.followersStats)
|
||||||
|
|
||||||
b := append(sBytes, pBytes...)
|
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
return sBytes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register commands to raft server
|
// Register commands to raft server
|
||||||
|
@ -33,10 +33,14 @@ func (ps *packageStats) Time() time.Time {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type raftServerStats struct {
|
type raftServerStats struct {
|
||||||
State string `json:"state"`
|
State string `json:"state"`
|
||||||
StartTime time.Time `json:"startTime"`
|
StartTime time.Time `json:"startTime"`
|
||||||
Leader string `json:"leader"`
|
|
||||||
LeaderUptime string `json:"leaderUptime"`
|
LeaderInfo struct {
|
||||||
|
Name string `json:"leader"`
|
||||||
|
Uptime string `json:"uptime"`
|
||||||
|
startTime time.Time
|
||||||
|
} `json:"leaderInfo"`
|
||||||
|
|
||||||
RecvAppendRequestCnt uint64 `json:"recvAppendRequestCnt,"`
|
RecvAppendRequestCnt uint64 `json:"recvAppendRequestCnt,"`
|
||||||
RecvingPkgRate float64 `json:"recvPkgRate,omitempty"`
|
RecvingPkgRate float64 `json:"recvPkgRate,omitempty"`
|
||||||
@ -46,16 +50,15 @@ type raftServerStats struct {
|
|||||||
SendingPkgRate float64 `json:"sendPkgRate,omitempty"`
|
SendingPkgRate float64 `json:"sendPkgRate,omitempty"`
|
||||||
SendingBandwidthRate float64 `json:"sendBandwidthRate,omitempty"`
|
SendingBandwidthRate float64 `json:"sendBandwidthRate,omitempty"`
|
||||||
|
|
||||||
leaderStartTime time.Time
|
sendRateQueue *statsQueue
|
||||||
sendRateQueue *statsQueue
|
recvRateQueue *statsQueue
|
||||||
recvRateQueue *statsQueue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ss *raftServerStats) RecvAppendReq(leaderName string, pkgSize int) {
|
func (ss *raftServerStats) RecvAppendReq(leaderName string, pkgSize int) {
|
||||||
ss.State = raft.Follower
|
ss.State = raft.Follower
|
||||||
if leaderName != ss.Leader {
|
if leaderName != ss.LeaderInfo.Name {
|
||||||
ss.Leader = leaderName
|
ss.LeaderInfo.Name = leaderName
|
||||||
ss.leaderStartTime = time.Now()
|
ss.LeaderInfo.startTime = time.Now()
|
||||||
}
|
}
|
||||||
|
|
||||||
ss.recvRateQueue.Insert(NewPackageStats(time.Now(), pkgSize))
|
ss.recvRateQueue.Insert(NewPackageStats(time.Now(), pkgSize))
|
||||||
@ -64,55 +67,66 @@ func (ss *raftServerStats) RecvAppendReq(leaderName string, pkgSize int) {
|
|||||||
|
|
||||||
func (ss *raftServerStats) SendAppendReq(pkgSize int) {
|
func (ss *raftServerStats) SendAppendReq(pkgSize int) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
if ss.State != raft.Leader {
|
if ss.State != raft.Leader {
|
||||||
ss.State = raft.Leader
|
ss.State = raft.Leader
|
||||||
ss.Leader = r.Name()
|
ss.LeaderInfo.Name = r.Name()
|
||||||
ss.leaderStartTime = now
|
ss.LeaderInfo.startTime = now
|
||||||
}
|
}
|
||||||
|
|
||||||
ss.sendRateQueue.Insert(NewPackageStats(time.Now(), pkgSize))
|
ss.sendRateQueue.Insert(NewPackageStats(now, pkgSize))
|
||||||
|
|
||||||
ss.SendAppendRequestCnt++
|
ss.SendAppendRequestCnt++
|
||||||
}
|
}
|
||||||
|
|
||||||
type raftPeerStats struct {
|
type raftFollowersStats struct {
|
||||||
Latency float64 `json:"latency"`
|
Leader string `json:"leader"`
|
||||||
AvgLatency float64 `json:"averageLatency"`
|
Followers map[string]*raftFollowerStats `json:"peers"`
|
||||||
avgLatencySquare float64
|
|
||||||
SdvLatency float64 `json:"sdvLatency"`
|
|
||||||
MinLatency float64 `json:"minLatency"`
|
|
||||||
MaxLatency float64 `json:"maxLatency"`
|
|
||||||
FailCnt uint64 `json:"failsCount"`
|
|
||||||
SuccCnt uint64 `json:"successCount"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Succ function update the raftPeerStats with a successful send
|
type raftFollowerStats struct {
|
||||||
func (ps *raftPeerStats) Succ(d time.Duration) {
|
Latency struct {
|
||||||
total := float64(ps.SuccCnt) * ps.AvgLatency
|
Current float64 `json:"current"`
|
||||||
totalSquare := float64(ps.SuccCnt) * ps.avgLatencySquare
|
Average float64 `json:"average"`
|
||||||
|
averageSquare float64
|
||||||
|
StandardDeviation float64 `json:"standardDeviation"`
|
||||||
|
Minimum float64 `json:"minimum"`
|
||||||
|
Maximum float64 `json:"maximum"`
|
||||||
|
} `json:"latency"`
|
||||||
|
|
||||||
ps.SuccCnt++
|
Counts struct {
|
||||||
|
Fail uint64 `json:"fail"`
|
||||||
|
Success uint64 `json:"success"`
|
||||||
|
} `json:"counts"`
|
||||||
|
}
|
||||||
|
|
||||||
ps.Latency = float64(d) / (1000000.0)
|
// Succ function update the raftFollowerStats with a successful send
|
||||||
|
func (ps *raftFollowerStats) Succ(d time.Duration) {
|
||||||
|
total := float64(ps.Counts.Success) * ps.Latency.Average
|
||||||
|
totalSquare := float64(ps.Counts.Success) * ps.Latency.averageSquare
|
||||||
|
|
||||||
if ps.Latency > ps.MaxLatency {
|
ps.Counts.Success++
|
||||||
ps.MaxLatency = ps.Latency
|
|
||||||
|
ps.Latency.Current = float64(d) / (1000000.0)
|
||||||
|
|
||||||
|
if ps.Latency.Current > ps.Latency.Maximum {
|
||||||
|
ps.Latency.Maximum = ps.Latency.Current
|
||||||
}
|
}
|
||||||
|
|
||||||
if ps.Latency < ps.MinLatency {
|
if ps.Latency.Current < ps.Latency.Minimum {
|
||||||
ps.MinLatency = ps.Latency
|
ps.Latency.Minimum = ps.Latency.Current
|
||||||
}
|
}
|
||||||
|
|
||||||
ps.AvgLatency = (total + ps.Latency) / float64(ps.SuccCnt)
|
ps.Latency.Average = (total + ps.Latency.Current) / float64(ps.Counts.Success)
|
||||||
ps.avgLatencySquare = (totalSquare + ps.Latency*ps.Latency) / float64(ps.SuccCnt)
|
ps.Latency.averageSquare = (totalSquare + ps.Latency.Current*ps.Latency.Current) / float64(ps.Counts.Success)
|
||||||
|
|
||||||
// sdv = sqrt(avg(x^2) - avg(x)^2)
|
// sdv = sqrt(avg(x^2) - avg(x)^2)
|
||||||
ps.SdvLatency = math.Sqrt(ps.avgLatencySquare - ps.AvgLatency*ps.AvgLatency)
|
ps.Latency.StandardDeviation = math.Sqrt(ps.Latency.averageSquare - ps.Latency.Average*ps.Latency.Average)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fail function update the raftPeerStats with a unsuccessful send
|
// Fail function update the raftFollowerStats with a unsuccessful send
|
||||||
func (ps *raftPeerStats) Fail() {
|
func (ps *raftFollowerStats) Fail() {
|
||||||
ps.FailCnt++
|
ps.Counts.Fail++
|
||||||
}
|
}
|
||||||
|
|
||||||
type statsQueue struct {
|
type statsQueue struct {
|
||||||
|
19
scripts/test-cluster
Executable file
19
scripts/test-cluster
Executable file
@ -0,0 +1,19 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
SESSION=etcd-cluster
|
||||||
|
|
||||||
|
tmux new-session -d -s $SESSION
|
||||||
|
|
||||||
|
# Setup a window for tailing log files
|
||||||
|
tmux new-window -t $SESSION:1 -n 'machines'
|
||||||
|
tmux split-window -h
|
||||||
|
tmux select-pane -t 0
|
||||||
|
tmux send-keys "./etcd -s 127.0.0.1:7001 -c 127.0.0.1:4001 -d machine1 -n machine1" C-m
|
||||||
|
|
||||||
|
for i in 2 3; do
|
||||||
|
tmux select-pane -t 0
|
||||||
|
tmux split-window -v
|
||||||
|
tmux send-keys "./etcd -cors='*' -s 127.0.0.1:700${i} -c 127.0.0.1:400${i} -C 127.0.0.1:7001 -d machine${i} -n machine${i}" C-m
|
||||||
|
done
|
||||||
|
|
||||||
|
# Attach to session
|
||||||
|
tmux attach-session -t $SESSION
|
@ -66,11 +66,12 @@ func (t *transporter) SendAppendEntriesRequest(server *raft.Server, peer *raft.P
|
|||||||
|
|
||||||
debugf("Send LogEntries to %s ", u)
|
debugf("Send LogEntries to %s ", u)
|
||||||
|
|
||||||
thisPeerStats, ok := r.peersStats[peer.Name]
|
thisFollowerStats, ok := r.followersStats.Followers[peer.Name]
|
||||||
|
|
||||||
if !ok { // we first see this peer
|
if !ok { //this is the first time this follower has been seen
|
||||||
thisPeerStats = &raftPeerStats{MinLatency: 1 << 63}
|
thisFollowerStats = &raftFollowerStats{}
|
||||||
r.peersStats[peer.Name] = thisPeerStats
|
thisFollowerStats.Latency.Minimum = 1 << 63
|
||||||
|
r.followersStats.Followers[peer.Name] = thisFollowerStats
|
||||||
}
|
}
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
@ -82,11 +83,11 @@ func (t *transporter) SendAppendEntriesRequest(server *raft.Server, peer *raft.P
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
debugf("Cannot send AppendEntriesRequest to %s: %s", u, err)
|
debugf("Cannot send AppendEntriesRequest to %s: %s", u, err)
|
||||||
if ok {
|
if ok {
|
||||||
thisPeerStats.Fail()
|
thisFollowerStats.Fail()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ok {
|
if ok {
|
||||||
thisPeerStats.Succ(end.Sub(start))
|
thisFollowerStats.Succ(end.Sub(start))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
17
util.go
17
util.go
@ -128,6 +128,23 @@ func sanitizeListenHost(listen string, advertised string) string {
|
|||||||
return net.JoinHostPort(listen, aport)
|
return net.JoinHostPort(listen, aport)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func redirect(node string, etcd bool, w http.ResponseWriter, req *http.Request) {
|
||||||
|
var url string
|
||||||
|
path := req.URL.Path
|
||||||
|
|
||||||
|
if etcd {
|
||||||
|
etcdAddr, _ := nameToEtcdURL(node)
|
||||||
|
url = etcdAddr + path
|
||||||
|
} else {
|
||||||
|
raftAddr, _ := nameToRaftURL(node)
|
||||||
|
url = raftAddr + path
|
||||||
|
}
|
||||||
|
|
||||||
|
debugf("Redirect to %s", url)
|
||||||
|
|
||||||
|
http.Redirect(w, req, url, http.StatusTemporaryRedirect)
|
||||||
|
}
|
||||||
|
|
||||||
func check(err error) {
|
func check(err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatal(err)
|
fatal(err)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user