// NewListener creates a net.Listener // If the given scheme is "https", it will use TLS config to set listener. // If any error happens, this function will call log.Fatal func NewListener(scheme, addr string, cfg *tls.Config) net.Listener { if scheme == "https" { l, err := newTLSListener(addr, cfg) if err != nil { log.Fatal("Failed to create TLS listener: ", err) } return l } l, err := newListener(addr) if err != nil { log.Fatal("Failed to create listener: ", err) } return l }
func (e *Etcd) runServer() { var removeNotify <-chan bool for { if e.mode == PeerMode { log.Infof("%v starting in peer mode", e.Config.Name) // Starting peer server should be followed close by listening on its port // If not, it may leave many requests unaccepted, or cannot receive heartbeat from the cluster. // One severe problem caused if failing receiving heartbeats is when the second node joins one-node cluster, // the cluster could be out of work as long as the two nodes cannot transfer messages. e.PeerServer.Start(e.Config.Snapshot, e.Config.ClusterConfig()) removeNotify = e.PeerServer.RemoveNotify() } else { log.Infof("%v starting in standby mode", e.Config.Name) e.StandbyServer.Start() removeNotify = e.StandbyServer.RemoveNotify() } // etcd server is ready to accept connections, notify waiters. e.onceReady.Do(func() { close(e.readyNotify) }) select { case <-e.closeChan: e.PeerServer.Stop() e.StandbyServer.Stop() return case <-removeNotify: } if e.mode == PeerMode { peerURLs := e.Registry.PeerURLs(e.PeerServer.RaftServer().Leader(), e.Config.Name) e.StandbyServer.SyncCluster(peerURLs) e.setMode(StandbyMode) } else { // Create etcd key-value store and registry. e.Store = store.New() e.Registry = server.NewRegistry(e.Store) e.PeerServer.SetStore(e.Store) e.PeerServer.SetRegistry(e.Registry) e.Server.SetStore(e.Store) e.Server.SetRegistry(e.Registry) // Generate new peer server here. // TODO(yichengq): raft server cannot be started after stopped. // It should be removed when raft restart is implemented. heartbeatInterval := time.Duration(e.Config.Peer.HeartbeatInterval) * time.Millisecond electionTimeout := time.Duration(e.Config.Peer.ElectionTimeout) * time.Millisecond raftServer, err := raft.NewServer(e.Config.Name, e.Config.DataDir, e.PeerServer.RaftServer().Transporter(), e.Store, e.PeerServer, "") if err != nil { log.Fatal(err) } raftServer.SetElectionTimeout(electionTimeout) raftServer.SetHeartbeatInterval(heartbeatInterval) e.PeerServer.SetRaftServer(raftServer, e.Config.Snapshot) e.StandbyServer.SetRaftServer(raftServer) e.PeerServer.SetJoinIndex(e.StandbyServer.JoinIndex()) e.setMode(PeerMode) } } }
// Parses non-configuration flags. func parseFlags() { var versionFlag bool var cpuprofile string f := flag.NewFlagSet(os.Args[0], -1) f.SetOutput(ioutil.Discard) f.BoolVar(&versionFlag, "version", false, "print the version and exit") f.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to file") f.Parse(os.Args[1:]) // Print version if necessary. if versionFlag { fmt.Println(server.ReleaseVersion) os.Exit(0) } // Begin CPU profiling if specified. if cpuprofile != "" { f, err := os.Create(cpuprofile) if err != nil { log.Fatal(err) } pprof.StartCPUProfile(f) c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) go func() { sig := <-c log.Infof("captured %v, stopping profiler and exiting..", sig) pprof.StopCPUProfile() os.Exit(1) }() } }
// NameFromHostname sets the machine name from the hostname. This is to help // people get started without thinking up a name. func (c *Config) NameFromHostname() { host, err := os.Hostname() if err != nil && host == "" { log.Fatal("Node name required and hostname not set. e.g. '-name=name'") } c.Name = host }
func NewPeerServer(name string, path string, url string, bindAddr string, tlsConf *TLSConfig, tlsInfo *TLSInfo, registry *Registry, store store.Store, snapshotCount int, heartbeatTimeout, electionTimeout time.Duration) *PeerServer { s := &PeerServer{ name: name, url: url, bindAddr: bindAddr, tlsConf: tlsConf, tlsInfo: tlsInfo, registry: registry, store: store, followersStats: &raftFollowersStats{ Leader: name, Followers: make(map[string]*raftFollowerStats), }, serverStats: &raftServerStats{ Name: name, StartTime: time.Now(), sendRateQueue: &statsQueue{ back: -1, }, recvRateQueue: &statsQueue{ back: -1, }, }, HeartbeatTimeout: heartbeatTimeout, ElectionTimeout: electionTimeout, timeoutThresholdChan: make(chan interface{}, 1), } // Create transporter for raft raftTransporter := newTransporter(tlsConf.Scheme, tlsConf.Client, s) // Create raft server raftServer, err := raft.NewServer(name, path, raftTransporter, s.store, s, "") if err != nil { log.Fatal(err) } s.snapConf = &snapshotConf{ checkingInterval: time.Second * 3, // this is not accurate, we will update raft to provide an api lastIndex: raftServer.CommitIndex(), snapshotThr: uint64(snapshotCount), } s.raftServer = raftServer s.raftServer.AddEventListener(raft.StateChangeEventType, s.raftEventLogger) s.raftServer.AddEventListener(raft.LeaderChangeEventType, s.raftEventLogger) s.raftServer.AddEventListener(raft.TermChangeEventType, s.raftEventLogger) s.raftServer.AddEventListener(raft.AddPeerEventType, s.raftEventLogger) s.raftServer.AddEventListener(raft.RemovePeerEventType, s.raftEventLogger) s.raftServer.AddEventListener(raft.HeartbeatTimeoutEventType, s.raftEventLogger) s.raftServer.AddEventListener(raft.ElectionTimeoutThresholdEventType, s.raftEventLogger) return s }
// TLSServerConfig generates tls configuration based on TLSInfo // If any error happens, this function will call log.Fatal func TLSServerConfig(info *TLSInfo) *tls.Config { if info.KeyFile == "" || info.CertFile == "" { return nil } cfg, err := info.ServerConfig() if err != nil { log.Fatal("TLS info error: ", err) } return cfg }
// NewListener creates a net.Listener // If the given scheme is "https", it will generate TLS configuration based on TLSInfo. // If any error happens, this function will call log.Fatal func NewListener(scheme, addr string, tlsInfo *TLSInfo) net.Listener { if scheme == "https" { cfg, err := tlsInfo.ServerConfig() if err != nil { log.Fatal("TLS info error: ", err) } l, err := newTLSListener(addr, cfg) if err != nil { log.Fatal("Failed to create TLS listener: ", err) } return l } l, err := newListener(addr) if err != nil { log.Fatal("Failed to create listener: ", err) } return l }
// profile starts CPU profiling. func profile(path string) { f, err := os.Create(path) if err != nil { log.Fatal(err) } pprof.StartCPUProfile(f) c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) go func() { sig := <-c log.Infof("captured %v, stopping profiler and exiting..", sig) pprof.StopCPUProfile() os.Exit(1) }() }
func (s *PeerServer) joinCluster(cluster []string) bool { for _, peer := range cluster { if len(peer) == 0 { continue } err := s.joinByPeer(s.raftServer, peer, s.tlsConf.Scheme) if err == nil { log.Debugf("%s success join to the cluster via peer %s", s.name, peer) return true } else { if _, ok := err.(etcdErr.Error); ok { log.Fatal(err) } log.Debugf("cannot join to cluster via peer %s %s", peer, err) } } return false }
func NewPeerServer(name string, path string, url string, bindAddr string, tlsConf *TLSConfig, tlsInfo *TLSInfo, registry *Registry, store store.Store, snapshotCount int) *PeerServer { s := &PeerServer{ name: name, url: url, bindAddr: bindAddr, tlsConf: tlsConf, tlsInfo: tlsInfo, registry: registry, store: store, snapConf: &snapshotConf{time.Second * 3, 0, uint64(snapshotCount)}, followersStats: &raftFollowersStats{ Leader: name, Followers: make(map[string]*raftFollowerStats), }, serverStats: &raftServerStats{ StartTime: time.Now(), sendRateQueue: &statsQueue{ back: -1, }, recvRateQueue: &statsQueue{ back: -1, }, }, HeartbeatTimeout: defaultHeartbeatTimeout, ElectionTimeout: defaultElectionTimeout, } // Create transporter for raft raftTransporter := newTransporter(tlsConf.Scheme, tlsConf.Client, s) // Create raft server raftServer, err := raft.NewServer(name, path, raftTransporter, s.store, s, "") if err != nil { log.Fatal(err) } s.raftServer = raftServer return s }
func (s *PeerServer) joinCluster(cluster []string) bool { for _, peer := range cluster { if len(peer) == 0 { continue } err := s.joinByPeer(s.raftServer, peer, s.Config.Scheme) if err == nil { log.Debugf("%s joined the cluster via peer %s", s.Config.Name, peer) return true } if _, ok := err.(etcdErr.Error); ok { log.Fatal(err) } log.Warnf("Attempt to join via %s failed: %s", peer, err) } return false }
func main() { // Load configuration. var config = config.New() if err := config.Load(os.Args[1:]); err != nil { fmt.Println(server.Usage() + "\n") fmt.Println(err.Error() + "\n") os.Exit(1) } else if config.ShowVersion { fmt.Println("etcd version", server.ReleaseVersion) os.Exit(0) } else if config.ShowHelp { fmt.Println(server.Usage() + "\n") os.Exit(0) } // Enable options. if config.VeryVeryVerbose { log.Verbose = true raft.SetLogLevel(raft.Trace) } else if config.VeryVerbose { log.Verbose = true raft.SetLogLevel(raft.Debug) } else if config.Verbose { log.Verbose = true } if config.CPUProfileFile != "" { profile(config.CPUProfileFile) } if config.DataDir == "" { log.Fatal("The data dir was not set and could not be guessed from machine name") } // Create data directory if it doesn't already exist. if err := os.MkdirAll(config.DataDir, 0744); err != nil { log.Fatalf("Unable to create path: %s", err) } // Warn people if they have an info file info := filepath.Join(config.DataDir, "info") if _, err := os.Stat(info); err == nil { log.Warnf("All cached configuration is now ignored. The file %s can be removed.", info) } var mbName string if config.Trace() { mbName = config.MetricsBucketName() runtime.SetBlockProfileRate(1) } mb := metrics.NewBucket(mbName) if config.GraphiteHost != "" { err := mb.Publish(config.GraphiteHost) if err != nil { panic(err) } } // Retrieve CORS configuration corsInfo, err := ehttp.NewCORSInfo(config.CorsOrigins) if err != nil { log.Fatal("CORS:", err) } // Create etcd key-value store and registry. store := store.New() registry := server.NewRegistry(store) // Create stats objects followersStats := server.NewRaftFollowersStats(config.Name) serverStats := server.NewRaftServerStats(config.Name) // Calculate all of our timeouts heartbeatInterval := time.Duration(config.Peer.HeartbeatInterval) * time.Millisecond electionTimeout := time.Duration(config.Peer.ElectionTimeout) * time.Millisecond dialTimeout := (3 * heartbeatInterval) + electionTimeout responseHeaderTimeout := (3 * heartbeatInterval) + electionTimeout // Create peer server psConfig := server.PeerServerConfig{ Name: config.Name, Scheme: config.PeerTLSInfo().Scheme(), URL: config.Peer.Addr, SnapshotCount: config.SnapshotCount, MaxClusterSize: config.MaxClusterSize, RetryTimes: config.MaxRetryAttempts, RetryInterval: config.RetryInterval, } ps := server.NewPeerServer(psConfig, registry, store, &mb, followersStats, serverStats) var psListener net.Listener if psConfig.Scheme == "https" { peerServerTLSConfig, err := config.PeerTLSInfo().ServerConfig() if err != nil { log.Fatal("peer server TLS error: ", err) } psListener, err = server.NewTLSListener(config.Peer.BindAddr, peerServerTLSConfig) if err != nil { log.Fatal("Failed to create peer listener: ", err) } } else { psListener, err = server.NewListener(config.Peer.BindAddr) if err != nil { log.Fatal("Failed to create peer listener: ", err) } } // Create raft transporter and server raftTransporter := server.NewTransporter(followersStats, serverStats, registry, heartbeatInterval, dialTimeout, responseHeaderTimeout) if psConfig.Scheme == "https" { raftClientTLSConfig, err := config.PeerTLSInfo().ClientConfig() if err != nil { log.Fatal("raft client TLS error: ", err) } raftTransporter.SetTLSConfig(*raftClientTLSConfig) } raftServer, err := raft.NewServer(config.Name, config.DataDir, raftTransporter, store, ps, "") if err != nil { log.Fatal(err) } raftServer.SetElectionTimeout(electionTimeout) raftServer.SetHeartbeatInterval(heartbeatInterval) ps.SetRaftServer(raftServer) // Create etcd server s := server.New(config.Name, config.Addr, ps, registry, store, &mb) if config.Trace() { s.EnableTracing() } var sListener net.Listener if config.EtcdTLSInfo().Scheme() == "https" { etcdServerTLSConfig, err := config.EtcdTLSInfo().ServerConfig() if err != nil { log.Fatal("etcd TLS error: ", err) } sListener, err = server.NewTLSListener(config.BindAddr, etcdServerTLSConfig) if err != nil { log.Fatal("Failed to create TLS etcd listener: ", err) } } else { sListener, err = server.NewListener(config.BindAddr) if err != nil { log.Fatal("Failed to create etcd listener: ", err) } } ps.SetServer(s) ps.Start(config.Snapshot, config.Discovery, config.Peers) go func() { log.Infof("peer server [name %s, listen on %s, advertised url %s]", ps.Config.Name, psListener.Addr(), ps.Config.URL) sHTTP := &ehttp.CORSHandler{ps.HTTPHandler(), corsInfo} log.Fatal(http.Serve(psListener, sHTTP)) }() log.Infof("etcd server [name %s, listen on %s, advertised url %s]", s.Name, sListener.Addr(), s.URL()) sHTTP := &ehttp.CORSHandler{s.HTTPHandler(), corsInfo} log.Fatal(http.Serve(sListener, sHTTP)) }
func main() { // Load configuration. var config = server.NewConfig() if err := config.Load(os.Args[1:]); err != nil { fmt.Println(server.Usage() + "\n") fmt.Println(err.Error() + "\n") os.Exit(1) } else if config.ShowVersion { fmt.Println(server.ReleaseVersion) os.Exit(0) } else if config.ShowHelp { fmt.Println(server.Usage() + "\n") os.Exit(0) } // Enable options. if config.VeryVeryVerbose { log.Verbose = true raft.SetLogLevel(raft.Trace) } else if config.VeryVerbose { log.Verbose = true raft.SetLogLevel(raft.Debug) } else if config.Verbose { log.Verbose = true } if config.CPUProfileFile != "" { profile(config.CPUProfileFile) } if config.DataDir == "" { log.Fatal("The data dir was not set and could not be guessed from machine name") } // Create data directory if it doesn't already exist. if err := os.MkdirAll(config.DataDir, 0744); err != nil { log.Fatalf("Unable to create path: %s", err) } // Load info object. info, err := config.Info() if err != nil { log.Fatal("info:", err) } // Retrieve TLS configuration. tlsConfig, err := info.EtcdTLS.Config() if err != nil { log.Fatal("Client TLS:", err) } peerTLSConfig, err := info.RaftTLS.Config() if err != nil { log.Fatal("Peer TLS:", err) } var mbName string if config.Trace() { mbName = config.MetricsBucketName() runtime.SetBlockProfileRate(1) } mb := metrics.NewBucket(mbName) if config.GraphiteHost != "" { err := mb.Publish(config.GraphiteHost) if err != nil { panic(err) } } // Create etcd key-value store and registry. store := store.New() registry := server.NewRegistry(store) // Create peer server. heartbeatTimeout := time.Duration(config.Peer.HeartbeatTimeout) * time.Millisecond electionTimeout := time.Duration(config.Peer.ElectionTimeout) * time.Millisecond ps := server.NewPeerServer(info.Name, config.DataDir, info.RaftURL, info.RaftListenHost, &peerTLSConfig, &info.RaftTLS, registry, store, config.SnapshotCount, heartbeatTimeout, electionTimeout, &mb) ps.MaxClusterSize = config.MaxClusterSize ps.RetryTimes = config.MaxRetryAttempts // Create client server. s := server.New(info.Name, info.EtcdURL, info.EtcdListenHost, &tlsConfig, &info.EtcdTLS, ps, registry, store, &mb) if err := s.AllowOrigins(config.CorsOrigins); err != nil { panic(err) } if config.Trace() { s.EnableTracing() } ps.SetServer(s) // Run peer server in separate thread while the client server blocks. go func() { log.Fatal(ps.ListenAndServe(config.Snapshot, config.Peers)) }() log.Fatal(s.ListenAndServe()) }
func main() { // Load configuration. var config = server.NewConfig() if err := config.Load(os.Args[1:]); err != nil { fmt.Println(server.Usage() + "\n") fmt.Println(err.Error() + "\n") os.Exit(1) } else if config.ShowVersion { fmt.Println(server.ReleaseVersion) os.Exit(0) } else if config.ShowHelp { fmt.Println(server.Usage() + "\n") os.Exit(0) } // Enable options. if config.VeryVeryVerbose { log.Verbose = true raft.SetLogLevel(raft.Trace) } else if config.VeryVerbose { log.Verbose = true raft.SetLogLevel(raft.Debug) } else if config.Verbose { log.Verbose = true } if config.CPUProfileFile != "" { profile(config.CPUProfileFile) } if config.DataDir == "" { log.Fatal("The data dir was not set and could not be guessed from machine name") } // Create data directory if it doesn't already exist. if err := os.MkdirAll(config.DataDir, 0744); err != nil { log.Fatalf("Unable to create path: %s", err) } // Load info object. info, err := config.Info() if err != nil { log.Fatal("info:", err) } // Retrieve TLS configuration. tlsConfig, err := info.EtcdTLS.Config() if err != nil { log.Fatal("Client TLS:", err) } peerTLSConfig, err := info.RaftTLS.Config() if err != nil { log.Fatal("Peer TLS:", err) } var mbName string if config.Trace() { mbName = config.MetricsBucketName() runtime.SetBlockProfileRate(1) } mb := metrics.NewBucket(mbName) if config.GraphiteHost != "" { err := mb.Publish(config.GraphiteHost) if err != nil { panic(err) } } // Retrieve CORS configuration corsInfo, err := ehttp.NewCORSInfo(config.CorsOrigins) if err != nil { log.Fatal("CORS:", err) } // Create etcd key-value store and registry. store := store.New() registry := server.NewRegistry(store) // Create stats objects followersStats := server.NewRaftFollowersStats(info.Name) serverStats := server.NewRaftServerStats(info.Name) // Calculate all of our timeouts heartbeatTimeout := time.Duration(config.Peer.HeartbeatTimeout) * time.Millisecond electionTimeout := time.Duration(config.Peer.ElectionTimeout) * time.Millisecond dialTimeout := (3 * heartbeatTimeout) + electionTimeout responseHeaderTimeout := (3 * heartbeatTimeout) + electionTimeout // Create peer server. psConfig := server.PeerServerConfig{ Name: info.Name, Scheme: peerTLSConfig.Scheme, URL: info.RaftURL, SnapshotCount: config.SnapshotCount, MaxClusterSize: config.MaxClusterSize, RetryTimes: config.MaxRetryAttempts, } ps := server.NewPeerServer(psConfig, registry, store, &mb, followersStats, serverStats) var psListener net.Listener if psConfig.Scheme == "https" { psListener, err = server.NewTLSListener(info.RaftListenHost, info.RaftTLS.CertFile, info.RaftTLS.KeyFile) } else { psListener, err = server.NewListener(info.RaftListenHost) } if err != nil { panic(err) } // Create Raft transporter and server raftTransporter := server.NewTransporter(followersStats, serverStats, registry, heartbeatTimeout, dialTimeout, responseHeaderTimeout) if psConfig.Scheme == "https" { raftTransporter.SetTLSConfig(peerTLSConfig.Client) } raftServer, err := raft.NewServer(info.Name, config.DataDir, raftTransporter, store, ps, "") if err != nil { log.Fatal(err) } raftServer.SetElectionTimeout(electionTimeout) raftServer.SetHeartbeatTimeout(heartbeatTimeout) ps.SetRaftServer(raftServer) // Create client server. s := server.New(info.Name, info.EtcdURL, ps, registry, store, &mb) if config.Trace() { s.EnableTracing() } var sListener net.Listener if tlsConfig.Scheme == "https" { sListener, err = server.NewTLSListener(info.EtcdListenHost, info.EtcdTLS.CertFile, info.EtcdTLS.KeyFile) } else { sListener, err = server.NewListener(info.EtcdListenHost) } if err != nil { panic(err) } ps.SetServer(s) ps.Start(config.Snapshot, config.Peers) // Run peer server in separate thread while the client server blocks. go func() { log.Infof("raft server [name %s, listen on %s, advertised url %s]", ps.Config.Name, psListener.Addr(), ps.Config.URL) sHTTP := &ehttp.CORSHandler{ps.HTTPHandler(), corsInfo} log.Fatal(http.Serve(psListener, sHTTP)) }() log.Infof("etcd server [name %s, listen on %s, advertised url %s]", s.Name, sListener.Addr(), s.URL()) sHTTP := &ehttp.CORSHandler{s.HTTPHandler(), corsInfo} log.Fatal(http.Serve(sListener, sHTTP)) }
// Run the etcd instance. func (e *Etcd) Run() { // Enable options. if e.Config.VeryVeryVerbose { log.Verbose = true raft.SetLogLevel(raft.Trace) goetcd.SetLogger( golog.New( "go-etcd", false, golog.CombinedSink( os.Stdout, "[%s] %s %-9s | %s\n", []string{"prefix", "time", "priority", "message"}, ), ), ) } else if e.Config.VeryVerbose { log.Verbose = true raft.SetLogLevel(raft.Debug) } else if e.Config.Verbose { log.Verbose = true } if e.Config.CPUProfileFile != "" { profile(e.Config.CPUProfileFile) } if e.Config.DataDir == "" { log.Fatal("The data dir was not set and could not be guessed from machine name") } // Create data directory if it doesn't already exist. if err := os.MkdirAll(e.Config.DataDir, 0744); err != nil { log.Fatalf("Unable to create path: %s", err) } // Warn people if they have an info file info := filepath.Join(e.Config.DataDir, "info") if _, err := os.Stat(info); err == nil { log.Warnf("All cached configuration is now ignored. The file %s can be removed.", info) } var mbName string if e.Config.Trace() { mbName = e.Config.MetricsBucketName() runtime.SetBlockProfileRate(1) } mb := metrics.NewBucket(mbName) if e.Config.GraphiteHost != "" { err := mb.Publish(e.Config.GraphiteHost) if err != nil { panic(err) } } // Retrieve CORS configuration corsInfo, err := ehttp.NewCORSInfo(e.Config.CorsOrigins) if err != nil { log.Fatal("CORS:", err) } // Create etcd key-value store and registry. e.Store = store.New() e.Registry = server.NewRegistry(e.Store) // Create stats objects followersStats := server.NewRaftFollowersStats(e.Config.Name) serverStats := server.NewRaftServerStats(e.Config.Name) // Calculate all of our timeouts heartbeatInterval := time.Duration(e.Config.Peer.HeartbeatInterval) * time.Millisecond electionTimeout := time.Duration(e.Config.Peer.ElectionTimeout) * time.Millisecond dialTimeout := (3 * heartbeatInterval) + electionTimeout responseHeaderTimeout := (3 * heartbeatInterval) + electionTimeout // Create peer server psConfig := server.PeerServerConfig{ Name: e.Config.Name, Scheme: e.Config.PeerTLSInfo().Scheme(), URL: e.Config.Peer.Addr, SnapshotCount: e.Config.SnapshotCount, RetryTimes: e.Config.MaxRetryAttempts, RetryInterval: e.Config.RetryInterval, } e.PeerServer = server.NewPeerServer(psConfig, e.Registry, e.Store, &mb, followersStats, serverStats) // Create raft transporter and server raftTransporter := server.NewTransporter(followersStats, serverStats, e.Registry, heartbeatInterval, dialTimeout, responseHeaderTimeout) if psConfig.Scheme == "https" { raftClientTLSConfig, err := e.Config.PeerTLSInfo().ClientConfig() if err != nil { log.Fatal("raft client TLS error: ", err) } raftTransporter.SetTLSConfig(*raftClientTLSConfig) } raftServer, err := raft.NewServer(e.Config.Name, e.Config.DataDir, raftTransporter, e.Store, e.PeerServer, "") if err != nil { log.Fatal(err) } raftServer.SetElectionTimeout(electionTimeout) raftServer.SetHeartbeatInterval(heartbeatInterval) e.PeerServer.SetRaftServer(raftServer) // Create etcd server e.Server = server.New(e.Config.Name, e.Config.Addr, e.PeerServer, e.Registry, e.Store, &mb) if e.Config.Trace() { e.Server.EnableTracing() } e.PeerServer.SetServer(e.Server) // Generating config could be slow. // Put it here to make listen happen immediately after peer-server starting. peerTLSConfig := server.TLSServerConfig(e.Config.PeerTLSInfo()) etcdTLSConfig := server.TLSServerConfig(e.Config.EtcdTLSInfo()) log.Infof("etcd server [name %s, listen on %s, advertised url %s]", e.Server.Name, e.Config.BindAddr, e.Server.URL()) e.listener = server.NewListener(e.Config.EtcdTLSInfo().Scheme(), e.Config.BindAddr, etcdTLSConfig) close(e.readyC) // etcd server is ready to accept connections, notify waiters. // An error string equivalent to net.errClosing for using with // http.Serve() during server shutdown. Need to re-declare // here because it is not exported by "net" package. const errClosing = "use of closed network connection" peerServerClosed := make(chan bool) go func() { // Starting peer server should be followed close by listening on its port // If not, it may leave many requests unaccepted, or cannot receive heartbeat from the cluster. // One severe problem caused if failing receiving heartbeats is when the second node joins one-node cluster, // the cluster could be out of work as long as the two nodes cannot transfer messages. e.PeerServer.Start(e.Config.Snapshot, e.Config.Discovery, e.Config.Peers) log.Infof("peer server [name %s, listen on %s, advertised url %s]", e.PeerServer.Config.Name, e.Config.Peer.BindAddr, e.PeerServer.Config.URL) e.peerListener = server.NewListener(psConfig.Scheme, e.Config.Peer.BindAddr, peerTLSConfig) sHTTP := &ehttp.CORSHandler{e.PeerServer.HTTPHandler(), corsInfo} if err = http.Serve(e.peerListener, sHTTP); err != nil { if !strings.Contains(err.Error(), errClosing) { log.Fatal(err) } } close(peerServerClosed) }() sHTTP := &ehttp.CORSHandler{e.Server.HTTPHandler(), corsInfo} if err = http.Serve(e.listener, sHTTP); err != nil { if !strings.Contains(err.Error(), errClosing) { log.Fatal(err) } } <-peerServerClosed log.Infof("etcd instance is stopped [name %s]", e.Config.Name) }
func main() { parseFlags() // Load configuration. var config = server.NewConfig() if err := config.Load(os.Args[1:]); err != nil { log.Fatal("Configuration error:", err) } // Turn on logging. if config.VeryVerbose { log.Verbose = true raft.SetLogLevel(raft.Debug) } else if config.Verbose { log.Verbose = true } // Setup a default directory based on the machine name if config.DataDir == "" { config.DataDir = config.Name + ".etcd" log.Warnf("Using the directory %s as the etcd configuration directory because a directory was not specified. ", config.DataDir) } // Create data directory if it doesn't already exist. if err := os.MkdirAll(config.DataDir, 0744); err != nil { log.Fatalf("Unable to create path: %s", err) } // Load info object. info, err := config.Info() if err != nil { log.Fatal("info:", err) } if info.Name == "" { host, err := os.Hostname() if err != nil || host == "" { log.Fatal("Machine name required and hostname not set. e.g. '-n=machine_name'") } log.Warnf("Using hostname %s as the machine name. You must ensure this name is unique among etcd machines.", host) info.Name = host } // Retrieve TLS configuration. tlsConfig, err := info.EtcdTLS.Config() if err != nil { log.Fatal("Client TLS:", err) } peerTLSConfig, err := info.RaftTLS.Config() if err != nil { log.Fatal("Peer TLS:", err) } // Create etcd key-value store and registry. store := store.New() registry := server.NewRegistry(store) // Create peer server. ps := server.NewPeerServer(info.Name, config.DataDir, info.RaftURL, info.RaftListenHost, &peerTLSConfig, &info.RaftTLS, registry, store, config.SnapCount) ps.MaxClusterSize = config.MaxClusterSize ps.RetryTimes = config.MaxRetryAttempts // Create client server. s := server.New(info.Name, info.EtcdURL, info.EtcdListenHost, &tlsConfig, &info.EtcdTLS, ps, registry, store) if err := s.AllowOrigins(config.Cors); err != nil { panic(err) } ps.SetServer(s) // Run peer server in separate thread while the client server blocks. go func() { log.Fatal(ps.ListenAndServe(config.Snapshot, config.Machines)) }() log.Fatal(s.ListenAndServe()) }
// Run the etcd instance. func (e *Etcd) Run() { // Sanitize all the input fields. if err := e.Config.Sanitize(); err != nil { log.Fatalf("failed sanitizing configuration: %v", err) } // Force remove server configuration if specified. if e.Config.Force { e.Config.Reset() } // Enable options. if e.Config.VeryVeryVerbose { log.Verbose = true raft.SetLogLevel(raft.Trace) goetcd.SetLogger( golog.New( "go-etcd", false, golog.CombinedSink( os.Stdout, "[%s] %s %-9s | %s\n", []string{"prefix", "time", "priority", "message"}, ), ), ) } else if e.Config.VeryVerbose { log.Verbose = true raft.SetLogLevel(raft.Debug) } else if e.Config.Verbose { log.Verbose = true } if e.Config.CPUProfileFile != "" { profile(e.Config.CPUProfileFile) } if e.Config.DataDir == "" { log.Fatal("The data dir was not set and could not be guessed from machine name") } // Create data directory if it doesn't already exist. if err := os.MkdirAll(e.Config.DataDir, 0744); err != nil { log.Fatalf("Unable to create path: %s", err) } // Warn people if they have an info file info := filepath.Join(e.Config.DataDir, "info") if _, err := os.Stat(info); err == nil { log.Warnf("All cached configuration is now ignored. The file %s can be removed.", info) } var mbName string if e.Config.Trace() { mbName = e.Config.MetricsBucketName() runtime.SetBlockProfileRate(1) } mb := metrics.NewBucket(mbName) if e.Config.GraphiteHost != "" { err := mb.Publish(e.Config.GraphiteHost) if err != nil { panic(err) } } // Retrieve CORS configuration corsInfo, err := ehttp.NewCORSInfo(e.Config.CorsOrigins) if err != nil { log.Fatal("CORS:", err) } // Create etcd key-value store and registry. e.Store = store.New() e.Registry = server.NewRegistry(e.Store) // Create stats objects followersStats := server.NewRaftFollowersStats(e.Config.Name) serverStats := server.NewRaftServerStats(e.Config.Name) // Calculate all of our timeouts heartbeatInterval := time.Duration(e.Config.Peer.HeartbeatInterval) * time.Millisecond electionTimeout := time.Duration(e.Config.Peer.ElectionTimeout) * time.Millisecond dialTimeout := (3 * heartbeatInterval) + electionTimeout responseHeaderTimeout := (3 * heartbeatInterval) + electionTimeout clientTransporter := &httpclient.Transport{ ResponseHeaderTimeout: responseHeaderTimeout + extraTimeout, // This is a workaround for Transport.CancelRequest doesn't work on // HTTPS connections blocked. The patch for it is in progress, // and would be available in Go1.3 // More: https://codereview.appspot.com/69280043/ ConnectTimeout: dialTimeout + extraTimeout, RequestTimeout: responseHeaderTimeout + dialTimeout + 2*extraTimeout, } if e.Config.PeerTLSInfo().Scheme() == "https" { clientTLSConfig, err := e.Config.PeerTLSInfo().ClientConfig() if err != nil { log.Fatal("client TLS error: ", err) } clientTransporter.TLSClientConfig = clientTLSConfig clientTransporter.DisableCompression = true } client := server.NewClient(clientTransporter) // Create peer server psConfig := server.PeerServerConfig{ Name: e.Config.Name, Scheme: e.Config.PeerTLSInfo().Scheme(), URL: e.Config.Peer.Addr, SnapshotCount: e.Config.SnapshotCount, RetryTimes: e.Config.MaxRetryAttempts, RetryInterval: e.Config.RetryInterval, } e.PeerServer = server.NewPeerServer(psConfig, client, e.Registry, e.Store, &mb, followersStats, serverStats) // Create raft transporter and server raftTransporter := server.NewTransporter(followersStats, serverStats, e.Registry, heartbeatInterval, dialTimeout, responseHeaderTimeout) if e.Config.PeerTLSInfo().Scheme() == "https" { raftClientTLSConfig, err := e.Config.PeerTLSInfo().ClientConfig() if err != nil { log.Fatal("raft client TLS error: ", err) } raftTransporter.SetTLSConfig(*raftClientTLSConfig) } raftServer, err := raft.NewServer(e.Config.Name, e.Config.DataDir, raftTransporter, e.Store, e.PeerServer, "") if err != nil { log.Fatal(err) } raftServer.SetElectionTimeout(electionTimeout) raftServer.SetHeartbeatInterval(heartbeatInterval) e.PeerServer.SetRaftServer(raftServer, e.Config.Snapshot) // Create etcd server e.Server = server.New(e.Config.Name, e.Config.Addr, e.PeerServer, e.Registry, e.Store, &mb) if e.Config.Trace() { e.Server.EnableTracing() } e.PeerServer.SetServer(e.Server) // Create standby server ssConfig := server.StandbyServerConfig{ Name: e.Config.Name, PeerScheme: e.Config.PeerTLSInfo().Scheme(), PeerURL: e.Config.Peer.Addr, ClientURL: e.Config.Addr, DataDir: e.Config.DataDir, } e.StandbyServer = server.NewStandbyServer(ssConfig, client) e.StandbyServer.SetRaftServer(raftServer) // Generating config could be slow. // Put it here to make listen happen immediately after peer-server starting. peerTLSConfig := server.TLSServerConfig(e.Config.PeerTLSInfo()) etcdTLSConfig := server.TLSServerConfig(e.Config.EtcdTLSInfo()) if !e.StandbyServer.IsRunning() { startPeerServer, possiblePeers, err := e.PeerServer.FindCluster(e.Config.Discovery, e.Config.Peers) if err != nil { log.Fatal(err) } if startPeerServer { e.setMode(PeerMode) } else { e.StandbyServer.SyncCluster(possiblePeers) e.setMode(StandbyMode) } } else { e.setMode(StandbyMode) } serverHTTPHandler := &ehttp.CORSHandler{e.Server.HTTPHandler(), corsInfo} peerServerHTTPHandler := &ehttp.CORSHandler{e.PeerServer.HTTPHandler(), corsInfo} standbyServerHTTPHandler := &ehttp.CORSHandler{e.StandbyServer.ClientHTTPHandler(), corsInfo} log.Infof("etcd server [name %s, listen on %s, advertised url %s]", e.Server.Name, e.Config.BindAddr, e.Server.URL()) listener := server.NewListener(e.Config.EtcdTLSInfo().Scheme(), e.Config.BindAddr, etcdTLSConfig) e.server = &http.Server{Handler: &ModeHandler{e, serverHTTPHandler, standbyServerHTTPHandler}, ReadTimeout: time.Duration(e.Config.HTTPReadTimeout) * time.Second, WriteTimeout: time.Duration(e.Config.HTTPWriteTimeout) * time.Second, } log.Infof("peer server [name %s, listen on %s, advertised url %s]", e.PeerServer.Config.Name, e.Config.Peer.BindAddr, e.PeerServer.Config.URL) peerListener := server.NewListener(e.Config.PeerTLSInfo().Scheme(), e.Config.Peer.BindAddr, peerTLSConfig) e.peerServer = &http.Server{Handler: &ModeHandler{e, peerServerHTTPHandler, http.NotFoundHandler()}, ReadTimeout: time.Duration(server.DefaultReadTimeout) * time.Second, WriteTimeout: time.Duration(server.DefaultWriteTimeout) * time.Second, } wg := sync.WaitGroup{} wg.Add(2) go func() { <-e.readyNotify defer wg.Done() if err := e.server.Serve(listener); err != nil { if !isListenerClosing(err) { log.Fatal(err) } } }() go func() { <-e.readyNotify defer wg.Done() if err := e.peerServer.Serve(peerListener); err != nil { if !isListenerClosing(err) { log.Fatal(err) } } }() e.runServer() listener.Close() peerListener.Close() wg.Wait() log.Infof("etcd instance is stopped [name %s]", e.Config.Name) close(e.stopNotify) }
// Try all possible ways to find clusters to join // Include log data in -data-dir, -discovery and -peers // // Peer discovery follows this order: // 1. previous peers in -data-dir // 2. -discovery // 3. -peers // // TODO(yichengq): RaftServer should be started as late as possible. // Current implementation to start it is not that good, // and should be refactored later. func (s *PeerServer) findCluster(discoverURL string, peers []string) { name := s.Config.Name isNewNode := s.raftServer.IsLogEmpty() // Try its best to find possible peers, and connect with them. if !isNewNode { // It is not allowed to join the cluster with existing peer address // This prevents old node joining with different name by mistake. if !s.checkPeerAddressNonconflict() { log.Fatalf("%v is not allowed to join the cluster with existing URL %v", s.Config.Name, s.Config.URL) } // Take old nodes into account. allPeers := s.getKnownPeers() // Discover registered peers. // TODO(yichengq): It may mess up discoverURL if this is // set wrong by mistake. This may need to refactor discovery // module. Fix it later. if discoverURL != "" { discoverPeers, _ := s.handleDiscovery(discoverURL) allPeers = append(allPeers, discoverPeers...) } allPeers = append(allPeers, peers...) allPeers = s.removeSelfFromList(allPeers) // If there is possible peer list, use it to find cluster. if len(allPeers) > 0 { // TODO(yichengq): joinCluster may fail if there's no leader for // current cluster. It should wait if the cluster is under // leader election, or the node with changed IP cannot join // the cluster then. if err := s.startAsFollower(allPeers, 1); err == nil { log.Debugf("%s joins to the previous cluster %v", name, allPeers) return } log.Warnf("%s cannot connect to previous cluster %v", name, allPeers) } // TODO(yichengq): Think about the action that should be done // if it cannot connect any of the previous known node. s.raftServer.Start() log.Debugf("%s is restarting the cluster %v", name, allPeers) return } // Attempt cluster discovery if discoverURL != "" { discoverPeers, discoverErr := s.handleDiscovery(discoverURL) // It is registered in discover url if discoverErr == nil { // start as a leader in a new cluster if len(discoverPeers) == 0 { log.Debugf("%s is starting a new cluster via discover service", name) s.startAsLeader() } else { log.Debugf("%s is joining a cluster %v via discover service", name, discoverPeers) if err := s.startAsFollower(discoverPeers, s.Config.RetryTimes); err != nil { log.Fatal(err) } } return } log.Warnf("%s failed to connect discovery service[%v]: %v", name, discoverURL, discoverErr) if len(peers) == 0 { log.Fatalf("%s, the new leader, must register itself to discovery service as required", name) } } if len(peers) > 0 { if err := s.startAsFollower(peers, s.Config.RetryTimes); err != nil { log.Fatalf("%s cannot connect to existing cluster %v", name, peers) } return } log.Infof("%s is starting a new cluster.", s.Config.Name) s.startAsLeader() return }