// Try all possible ways to find clusters to join // Include -discovery, -peers and log data in -data-dir // // Peer discovery follows this order: // 1. -discovery // 2. -peers // 3. previous peers in -data-dir func (s *PeerServer) findCluster(discoverURL string, peers []string) { // Attempt cluster discovery toDiscover := discoverURL != "" if toDiscover { 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.Debug("This peer is starting a brand new cluster based on discover URL.") s.startAsLeader() } else { s.startAsFollower(discoverPeers) } return } } hasPeerList := len(peers) > 0 // if there is log in data dir, append previous peers to peers in config // to find cluster prevPeers := s.registry.PeerURLs(s.raftServer.Leader(), s.Config.Name) for i := 0; i < len(prevPeers); i++ { u, err := url.Parse(prevPeers[i]) if err != nil { log.Debug("rejoin cannot parse url: ", err) } prevPeers[i] = u.Host } peers = append(peers, prevPeers...) // if there is backup peer lists, use it to find cluster if len(peers) > 0 { ok := s.joinCluster(peers) if !ok { log.Warn("No living peers are found!") } else { log.Debugf("%s restart as a follower based on peers[%v]", s.Config.Name) return } } if !s.raftServer.IsLogEmpty() { log.Debug("Entire cluster is down! %v will restart the cluster.", s.Config.Name) return } if toDiscover { log.Fatalf("Discovery failed, no available peers in backup list, and no log data") } if hasPeerList { log.Fatalf("No available peers in backup list, and no log data") } log.Infof("This peer is starting a brand new cluster now.") s.startAsLeader() }
func (c *Config) handleDiscovery() error { p, err := discovery.Do(c.Discovery, c.Name, c.Peer.Addr) // This is fatal, discovery encountered an unexpected error // and we have no peer list. if err != nil && len(c.Peers) == 0 { log.Fatalf("Discovery failed and a backup peer list wasn't provided: %v", err) return err } // Warn about errors coming from discovery, this isn't fatal // since the user might have provided a peer list elsewhere. if err != nil { log.Warnf("Discovery encountered an error but a backup peer list (%v) was provided: %v", c.Peers, err) } for i := range p { // Strip the scheme off of the peer if it has one // TODO(bp): clean this up! purl, err := url.Parse(p[i]) if err == nil { p[i] = purl.Host } } c.Peers = p return nil }
func (s *PeerServer) startAsFollower(cluster []string) { // start as a follower in a existing cluster for i := 0; i < s.Config.RetryTimes; i++ { ok := s.joinCluster(cluster) if ok { return } log.Warnf("Unable to join the cluster using any of the peers %v. Retrying in %.1f seconds", cluster, s.Config.RetryInterval) time.Sleep(time.Second * time.Duration(s.Config.RetryInterval)) } log.Fatalf("Cannot join the cluster via given peers after %x retries", s.Config.RetryTimes) }
func (s *PeerServer) startAsFollower(cluster []string) { // start as a follower in a existing cluster for i := 0; i < s.RetryTimes; i++ { ok := s.joinCluster(cluster) if ok { return } log.Warnf("cannot join to cluster via given peers, retry in %d seconds", RetryInterval) time.Sleep(time.Second * RetryInterval) } log.Fatalf("Cannot join the cluster via given peers after %x retries", s.RetryTimes) }
// removeSelfFromList removes url of the peerServer from the peer list func (s *PeerServer) removeSelfFromList(peers []string) []string { // Remove its own peer address from the peer list to join u, err := url.Parse(s.Config.URL) if err != nil { log.Fatalf("removeSelfFromList cannot parse peer address %v", s.Config.URL) } newPeers := make([]string, 0) for _, v := range peers { if v != u.Host { newPeers = append(newPeers, v) } } return newPeers }
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)) }
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()) }
// 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 }
// 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) }
// 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) }