mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
Refactored.
This commit is contained in:
@@ -5,7 +5,6 @@ import (
|
||||
|
||||
etcdErr "github.com/coreos/etcd/error"
|
||||
"github.com/coreos/etcd/log"
|
||||
"github.com/coreos/etcd/store"
|
||||
"github.com/coreos/go-raft"
|
||||
)
|
||||
|
||||
@@ -37,7 +36,6 @@ func (c *JoinCommand) CommandName() string {
|
||||
|
||||
// Join a server to the cluster
|
||||
func (c *JoinCommand) Apply(server *raft.Server) (interface{}, error) {
|
||||
s, _ := server.StateMachine().(*store.Store)
|
||||
ps, _ := server.Context().(*PeerServer)
|
||||
|
||||
b := make([]byte, 8)
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
|
||||
type PeerServer struct {
|
||||
*raft.Server
|
||||
server Server
|
||||
server *Server
|
||||
joinIndex uint64
|
||||
name string
|
||||
url string
|
||||
@@ -148,7 +148,7 @@ func (s *PeerServer) RaftServer() *raft.Server {
|
||||
}
|
||||
|
||||
// Associates the client server with the peer server.
|
||||
func (s *PeerServer) SetServer(server Server) {
|
||||
func (s *PeerServer) SetServer(server *Server) {
|
||||
s.server = server
|
||||
}
|
||||
|
||||
@@ -239,15 +239,33 @@ func (s *PeerServer) EtcdURLHttpHandler(w http.ResponseWriter, req *http.Request
|
||||
}
|
||||
|
||||
// Response to the join request
|
||||
func (s *PeerServer) JoinHttpHandler(w http.ResponseWriter, req *http.Request) error {
|
||||
func (s *PeerServer) JoinHttpHandler(w http.ResponseWriter, req *http.Request) {
|
||||
command := &JoinCommand{}
|
||||
|
||||
if err := decodeJsonRequest(req, command); err == nil {
|
||||
log.Debugf("Receive Join Request from %s", command.Name)
|
||||
return s.dispatchRaftCommand(command, w, req)
|
||||
} else {
|
||||
// Write CORS header.
|
||||
if s.server.OriginAllowed("*") {
|
||||
w.Header().Add("Access-Control-Allow-Origin", "*")
|
||||
} else if s.server.OriginAllowed(req.Header.Get("Origin")) {
|
||||
w.Header().Add("Access-Control-Allow-Origin", req.Header.Get("Origin"))
|
||||
}
|
||||
|
||||
err := decodeJsonRequest(req, command)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
log.Debugf("Receive Join Request from %s", command.Name)
|
||||
err = s.dispatchRaftCommand(command, w, req)
|
||||
|
||||
// Return status.
|
||||
if err != nil {
|
||||
if etcdErr, ok := err.(*etcdErr.Error); ok {
|
||||
log.Debug("Return error: ", (*etcdErr).Error())
|
||||
etcdErr.Write(w)
|
||||
} else {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,7 +344,7 @@ func (s *PeerServer) startTransport(scheme string, tlsConf tls.Config) {
|
||||
// internal commands
|
||||
raftMux.HandleFunc("/name", s.NameHttpHandler)
|
||||
raftMux.HandleFunc("/version", s.RaftVersionHttpHandler)
|
||||
raftMux.Handle("/join", errorHandler(s.JoinHttpHandler))
|
||||
raftMux.HandleFunc("/join", s.JoinHttpHandler)
|
||||
raftMux.HandleFunc("/remove/", s.RemoveHttpHandler)
|
||||
raftMux.HandleFunc("/vote", s.VoteHttpHandler)
|
||||
raftMux.HandleFunc("/log", s.GetLogHttpHandler)
|
||||
@@ -421,7 +439,7 @@ func (s *PeerServer) joinByMachine(server *raft.Server, machine string, scheme s
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
b, _ := ioutil.ReadAll(resp.Body)
|
||||
server.joinIndex, _ = binary.Uvarint(b)
|
||||
s.joinIndex, _ = binary.Uvarint(b)
|
||||
return nil
|
||||
}
|
||||
if resp.StatusCode == http.StatusTemporaryRedirect {
|
||||
@@ -429,12 +447,12 @@ func (s *PeerServer) joinByMachine(server *raft.Server, machine string, scheme s
|
||||
address := resp.Header.Get("Location")
|
||||
log.Debugf("Send Join Request to %s", address)
|
||||
|
||||
json.NewEncoder(&b).Encode(newJoinCommand(PeerVersion, server.Name(), s.url, s.server.URL()))
|
||||
json.NewEncoder(&b).Encode(NewJoinCommand(PeerVersion, server.Name(), s.url, s.server.URL()))
|
||||
|
||||
resp, req, err = t.Post(address, &b)
|
||||
|
||||
} else if resp.StatusCode == http.StatusBadRequest {
|
||||
debug("Reach max number machines in the cluster")
|
||||
log.Debug("Reach max number machines in the cluster")
|
||||
decoder := json.NewDecoder(resp.Body)
|
||||
err := &etcdErr.Error{}
|
||||
decoder.Decode(err)
|
||||
@@ -477,15 +495,15 @@ func (s *PeerServer) monitorSnapshot() {
|
||||
time.Sleep(s.snapConf.checkingInterval)
|
||||
currentWrites := 0
|
||||
if uint64(currentWrites) > s.snapConf.writesThr {
|
||||
r.TakeSnapshot()
|
||||
s.TakeSnapshot()
|
||||
s.snapConf.lastWrites = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PeerServer) dispatch(c raft.Command, w http.ResponseWriter, req *http.Request) error {
|
||||
if r.State() == raft.Leader {
|
||||
if response, err := r.Do(c); err != nil {
|
||||
if s.State() == raft.Leader {
|
||||
if response, err := s.Do(c); err != nil {
|
||||
return err
|
||||
} else {
|
||||
if response == nil {
|
||||
@@ -515,7 +533,7 @@ func (s *PeerServer) dispatch(c raft.Command, w http.ResponseWriter, req *http.R
|
||||
}
|
||||
|
||||
} else {
|
||||
leader := r.Leader()
|
||||
leader := s.Leader()
|
||||
// current no leader
|
||||
if leader == "" {
|
||||
return etcdErr.NewError(300, "", store.UndefIndex, store.UndefTerm)
|
||||
@@ -528,35 +546,3 @@ func (s *PeerServer) dispatch(c raft.Command, w http.ResponseWriter, req *http.R
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
type errorHandler func(http.ResponseWriter, *http.Request) error
|
||||
|
||||
// addCorsHeader parses the request Origin header and loops through the user
|
||||
// provided allowed origins and sets the Access-Control-Allow-Origin header if
|
||||
// there is a match.
|
||||
func addCorsHeader(w http.ResponseWriter, r *http.Request) {
|
||||
val, ok := corsList["*"]
|
||||
if val && ok {
|
||||
w.Header().Add("Access-Control-Allow-Origin", "*")
|
||||
return
|
||||
}
|
||||
|
||||
requestOrigin := r.Header.Get("Origin")
|
||||
val, ok = corsList[requestOrigin]
|
||||
if val && ok {
|
||||
w.Header().Add("Access-Control-Allow-Origin", requestOrigin)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (fn errorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
addCorsHeader(w, r)
|
||||
if e := fn(w, r); e != nil {
|
||||
if etcdErr, ok := e.(*etcdErr.Error); ok {
|
||||
debug("Return error: ", (*etcdErr).Error())
|
||||
etcdErr.Write(w)
|
||||
} else {
|
||||
http.Error(w, e.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path"
|
||||
"sync"
|
||||
|
||||
"github.com/coreos/etcd/store"
|
||||
@@ -48,13 +51,13 @@ func (r *Registry) Unregister(name string, commitIndex uint64, term uint64) erro
|
||||
defer r.Unlock()
|
||||
|
||||
// Remove the key from the store.
|
||||
_, err := s.Delete(path.Join(RegistryKey, name), false, commitIndex, term)
|
||||
_, err := r.store.Delete(path.Join(RegistryKey, name), false, commitIndex, term)
|
||||
return err
|
||||
}
|
||||
|
||||
// Returns the number of nodes in the cluster.
|
||||
func (r *Registry) Count() int {
|
||||
e, err := s.Get(RegistryKey, false, false, 0, 0)
|
||||
e, err := r.store.Get(RegistryKey, false, false, 0, 0)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
@@ -86,7 +89,7 @@ func (r *Registry) URLs() []string {
|
||||
defer r.Unlock()
|
||||
|
||||
// Retrieve a list of all nodes.
|
||||
e, err := s.Get(RegistryKey, false, false, 0, 0)
|
||||
e, err := r.store.Get(RegistryKey, false, false, 0, 0)
|
||||
if err != nil {
|
||||
return make([]string, 0)
|
||||
}
|
||||
@@ -94,7 +97,9 @@ func (r *Registry) URLs() []string {
|
||||
// Lookup the URL for each one.
|
||||
urls := make([]string, 0)
|
||||
for _, pair := range e.KVPairs {
|
||||
urls = append(urls, r.url(pair.Key))
|
||||
if url, ok := r.url(pair.Key); ok {
|
||||
urls = append(urls, url)
|
||||
}
|
||||
}
|
||||
|
||||
return urls
|
||||
@@ -126,7 +131,7 @@ func (r *Registry) PeerURLs() []string {
|
||||
defer r.Unlock()
|
||||
|
||||
// Retrieve a list of all nodes.
|
||||
e, err := s.Get(RegistryKey, false, false, 0, 0)
|
||||
e, err := r.store.Get(RegistryKey, false, false, 0, 0)
|
||||
if err != nil {
|
||||
return make([]string, 0)
|
||||
}
|
||||
@@ -134,7 +139,9 @@ func (r *Registry) PeerURLs() []string {
|
||||
// Lookup the URL for each one.
|
||||
urls := make([]string, 0)
|
||||
for _, pair := range e.KVPairs {
|
||||
urls = append(urls, r.peerURL(pair.Key))
|
||||
if url, ok := r.peerURL(pair.Key); ok {
|
||||
urls = append(urls, url)
|
||||
}
|
||||
}
|
||||
|
||||
return urls
|
||||
@@ -147,7 +154,7 @@ func (r *Registry) load(name string) {
|
||||
}
|
||||
|
||||
// Retrieve from store.
|
||||
e, err := etcdStore.Get(path.Join(RegistryKey, name), false, false, 0, 0)
|
||||
e, err := r.store.Get(path.Join(RegistryKey, name), false, false, 0, 0)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -2,8 +2,9 @@ package server
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"os"
|
||||
|
||||
"github.com/coreos/etcd/store"
|
||||
"github.com/coreos/etcd/log"
|
||||
"github.com/coreos/go-raft"
|
||||
)
|
||||
|
||||
@@ -23,7 +24,6 @@ func (c *RemoveCommand) CommandName() string {
|
||||
|
||||
// Remove a server from the cluster
|
||||
func (c *RemoveCommand) Apply(server *raft.Server) (interface{}, error) {
|
||||
s, _ := server.StateMachine().(*store.Store)
|
||||
ps, _ := server.Context().(*PeerServer)
|
||||
|
||||
// Remove node from the shared registry.
|
||||
@@ -50,11 +50,11 @@ func (c *RemoveCommand) Apply(server *raft.Server) (interface{}, error) {
|
||||
// start. It is sure that this node received a new remove
|
||||
// command and need to be removed
|
||||
if server.CommitIndex() > ps.joinIndex && ps.joinIndex != 0 {
|
||||
debugf("server [%s] is removed", server.Name())
|
||||
log.Debugf("server [%s] is removed", server.Name())
|
||||
os.Exit(0)
|
||||
} else {
|
||||
// else ignore remove
|
||||
debugf("ignore previous remove command.")
|
||||
log.Debugf("ignore previous remove command.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
113
server/server.go
113
server/server.go
@@ -1,25 +1,26 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
etcdErr "github.com/coreos/etcd/error"
|
||||
"github.com/coreos/etcd/log"
|
||||
"github.com/coreos/etcd/server/v1"
|
||||
"github.com/coreos/etcd/store"
|
||||
"github.com/coreos/go-raft"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// The Server provides an HTTP interface to the underlying store.
|
||||
type Server interface {
|
||||
CommitIndex() uint64
|
||||
Term() uint64
|
||||
URL() string
|
||||
Dispatch(raft.Command, http.ResponseWriter, *http.Request)
|
||||
}
|
||||
|
||||
// This is the default implementation of the Server interface.
|
||||
type server struct {
|
||||
type Server struct {
|
||||
http.Server
|
||||
raftServer *raft.Server
|
||||
registry *Registry
|
||||
store *store.Store
|
||||
name string
|
||||
url string
|
||||
tlsConf *TLSConfig
|
||||
@@ -28,14 +29,15 @@ type server struct {
|
||||
}
|
||||
|
||||
// Creates a new Server.
|
||||
func New(name string, urlStr string, listenHost string, tlsConf *TLSConfig, tlsInfo *TLSInfo, raftServer *raft.Server) *Server {
|
||||
s := &server{
|
||||
func New(name string, urlStr string, listenHost string, tlsConf *TLSConfig, tlsInfo *TLSInfo, raftServer *raft.Server, registry *Registry, store *store.Store) *Server {
|
||||
s := &Server{
|
||||
Server: http.Server{
|
||||
Handler: mux.NewRouter(),
|
||||
TLSConfig: &tlsConf.Server,
|
||||
Addr: listenHost,
|
||||
},
|
||||
name: name,
|
||||
store: store,
|
||||
registry: registry,
|
||||
url: urlStr,
|
||||
tlsConf: tlsConf,
|
||||
tlsInfo: tlsInfo,
|
||||
@@ -49,72 +51,117 @@ func New(name string, urlStr string, listenHost string, tlsConf *TLSConfig, tlsI
|
||||
}
|
||||
|
||||
// The current Raft committed index.
|
||||
func (s *server) CommitIndex() uint64 {
|
||||
func (s *Server) CommitIndex() uint64 {
|
||||
return s.raftServer.CommitIndex()
|
||||
}
|
||||
|
||||
// The current Raft term.
|
||||
func (s *server) Term() uint64 {
|
||||
func (s *Server) Term() uint64 {
|
||||
return s.raftServer.Term()
|
||||
}
|
||||
|
||||
// The server URL.
|
||||
func (s *server) URL() string {
|
||||
func (s *Server) URL() string {
|
||||
return s.url
|
||||
}
|
||||
|
||||
func (s *server) installV1() {
|
||||
s.handleFunc("/v1/keys/{key:.*}", v1.GetKeyHandler).Methods("GET")
|
||||
s.handleFunc("/v1/keys/{key:.*}", v1.SetKeyHandler).Methods("POST", "PUT")
|
||||
s.handleFunc("/v1/keys/{key:.*}", v1.DeleteKeyHandler).Methods("DELETE")
|
||||
// Returns a reference to the Store.
|
||||
func (s *Server) Store() *store.Store {
|
||||
return s.store
|
||||
}
|
||||
|
||||
s.handleFunc("/v1/watch/{key:.*}", v1.WatchKeyHandler).Methods("GET", "POST")
|
||||
func (s *Server) installV1() {
|
||||
s.handleFuncV1("/v1/keys/{key:.*}", v1.GetKeyHandler).Methods("GET")
|
||||
s.handleFuncV1("/v1/keys/{key:.*}", v1.SetKeyHandler).Methods("POST", "PUT")
|
||||
s.handleFuncV1("/v1/keys/{key:.*}", v1.DeleteKeyHandler).Methods("DELETE")
|
||||
|
||||
s.handleFuncV1("/v1/watch/{key:.*}", v1.WatchKeyHandler).Methods("GET", "POST")
|
||||
}
|
||||
|
||||
// Adds a v1 server handler to the router.
|
||||
func (s *Server) handleFuncV1(path string, f func(http.ResponseWriter, *http.Request, v1.Server) error) *mux.Route {
|
||||
return s.handleFunc(path, func(w http.ResponseWriter, req *http.Request, s *Server) error {
|
||||
return f(w, req, s)
|
||||
})
|
||||
}
|
||||
|
||||
// Adds a server handler to the router.
|
||||
func (s *server) handleFunc(path string, f func(http.ResponseWriter, *http.Request, Server) error) *mux.Route {
|
||||
func (s *Server) handleFunc(path string, f func(http.ResponseWriter, *http.Request, *Server) error) *mux.Route {
|
||||
r := s.Handler.(*mux.Router)
|
||||
|
||||
// Wrap the standard HandleFunc interface to pass in the server reference.
|
||||
return r.HandleFunc(path, func(w http.ResponseWriter, req *http.Request) {
|
||||
// Log request.
|
||||
debugf("[recv] %s %s [%s]", req.Method, s.url, req.URL.Path, req.RemoteAddr)
|
||||
log.Debugf("[recv] %s %s [%s]", req.Method, s.url, req.URL.Path, req.RemoteAddr)
|
||||
|
||||
// Write CORS header.
|
||||
if s.OriginAllowed("*") {
|
||||
w.Header().Add("Access-Control-Allow-Origin", "*")
|
||||
} else if s.OriginAllowed(r.Header.Get("Origin")) {
|
||||
w.Header().Add("Access-Control-Allow-Origin", r.Header.Get("Origin"))
|
||||
} else if origin := req.Header.Get("Origin"); s.OriginAllowed(origin) {
|
||||
w.Header().Add("Access-Control-Allow-Origin", origin)
|
||||
}
|
||||
|
||||
// Execute handler function and return error if necessary.
|
||||
if err := f(w, req, s); err != nil {
|
||||
if etcdErr, ok := err.(*etcdErr.Error); ok {
|
||||
debug("Return error: ", (*etcdErr).Error())
|
||||
log.Debug("Return error: ", (*etcdErr).Error())
|
||||
etcdErr.Write(w)
|
||||
} else {
|
||||
http.Error(w, e.Error(), http.StatusInternalServerError)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Start to listen and response etcd client command
|
||||
func (s *server) ListenAndServe() {
|
||||
infof("etcd server [name %s, listen on %s, advertised url %s]", s.name, s.Server.Addr, s.url)
|
||||
func (s *Server) ListenAndServe() {
|
||||
log.Infof("etcd server [name %s, listen on %s, advertised url %s]", s.name, s.Server.Addr, s.url)
|
||||
|
||||
if s.tlsConf.Scheme == "http" {
|
||||
fatal(s.Server.ListenAndServe())
|
||||
log.Fatal(s.Server.ListenAndServe())
|
||||
} else {
|
||||
fatal(s.Server.ListenAndServeTLS(s.tlsInfo.CertFile, s.tlsInfo.KeyFile))
|
||||
log.Fatal(s.Server.ListenAndServeTLS(s.tlsInfo.CertFile, s.tlsInfo.KeyFile))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) Dispatch(c raft.Command, w http.ResponseWriter, req *http.Request) error {
|
||||
if s.raftServer.State() == raft.Leader {
|
||||
event, err := s.raftServer.Do(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if event == nil {
|
||||
return etcdErr.NewError(300, "Empty result from raft", store.UndefIndex, store.UndefTerm)
|
||||
}
|
||||
|
||||
response := event.(*store.Event).Response()
|
||||
b, _ := json.Marshal(response)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(b)
|
||||
|
||||
return nil
|
||||
|
||||
} else {
|
||||
leader := s.raftServer.Leader()
|
||||
|
||||
// No leader available.
|
||||
if leader == "" {
|
||||
return etcdErr.NewError(300, "", store.UndefIndex, store.UndefTerm)
|
||||
}
|
||||
|
||||
url, _ := s.registry.PeerURL(leader)
|
||||
redirect(url, w, req)
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Sets a comma-delimited list of origins that are allowed.
|
||||
func (s *server) AllowOrigins(origins string) error {
|
||||
func (s *Server) AllowOrigins(origins string) error {
|
||||
// Construct a lookup of all origins.
|
||||
m := make(map[string]bool)
|
||||
for _, v := range strings.Split(cors, ",") {
|
||||
for _, v := range strings.Split(origins, ",") {
|
||||
if v != "*" {
|
||||
if _, err := url.Parse(v); err != nil {
|
||||
return fmt.Errorf("Invalid CORS origin: %s", err)
|
||||
@@ -128,6 +175,6 @@ func (s *server) AllowOrigins(origins string) error {
|
||||
}
|
||||
|
||||
// Determines whether the server will allow a given CORS origin.
|
||||
func (s *server) OriginAllowed(origin string) {
|
||||
func (s *Server) OriginAllowed(origin string) bool {
|
||||
return s.corsOrigins["*"] || s.corsOrigins[origin]
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package server
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// The amount of time to elapse without a heartbeat before becoming a candidate.
|
||||
ElectionTimeout = 200 * time.Millisecond
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/etcd/log"
|
||||
"github.com/coreos/go-raft"
|
||||
)
|
||||
|
||||
@@ -29,13 +30,13 @@ var tranTimeout = ElectionTimeout
|
||||
type transporter struct {
|
||||
client *http.Client
|
||||
transport *http.Transport
|
||||
raftServer *raftServer
|
||||
peerServer *PeerServer
|
||||
}
|
||||
|
||||
// Create transporter using by raft server
|
||||
// Create http or https transporter based on
|
||||
// whether the user give the server cert and key
|
||||
func newTransporter(scheme string, tlsConf tls.Config, raftServer *raftServer) *transporter {
|
||||
func newTransporter(scheme string, tlsConf tls.Config, peerServer *PeerServer) *transporter {
|
||||
t := transporter{}
|
||||
|
||||
tr := &http.Transport{
|
||||
@@ -50,7 +51,7 @@ func newTransporter(scheme string, tlsConf tls.Config, raftServer *raftServer) *
|
||||
|
||||
t.client = &http.Client{Transport: tr}
|
||||
t.transport = tr
|
||||
t.raftServer = raftServer
|
||||
t.peerServer = peerServer
|
||||
|
||||
return &t
|
||||
}
|
||||
@@ -69,18 +70,18 @@ func (t *transporter) SendAppendEntriesRequest(server *raft.Server, peer *raft.P
|
||||
|
||||
size := b.Len()
|
||||
|
||||
t.raftServer.serverStats.SendAppendReq(size)
|
||||
t.peerServer.serverStats.SendAppendReq(size)
|
||||
|
||||
u, _ := nameToRaftURL(peer.Name)
|
||||
u, _ := t.peerServer.registry.PeerURL(peer.Name)
|
||||
|
||||
debugf("Send LogEntries to %s ", u)
|
||||
log.Debugf("Send LogEntries to %s ", u)
|
||||
|
||||
thisFollowerStats, ok := t.raftServer.followersStats.Followers[peer.Name]
|
||||
thisFollowerStats, ok := t.peerServer.followersStats.Followers[peer.Name]
|
||||
|
||||
if !ok { //this is the first time this follower has been seen
|
||||
thisFollowerStats = &raftFollowerStats{}
|
||||
thisFollowerStats.Latency.Minimum = 1 << 63
|
||||
t.raftServer.followersStats.Followers[peer.Name] = thisFollowerStats
|
||||
t.peerServer.followersStats.Followers[peer.Name] = thisFollowerStats
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
@@ -90,7 +91,7 @@ func (t *transporter) SendAppendEntriesRequest(server *raft.Server, peer *raft.P
|
||||
end := time.Now()
|
||||
|
||||
if err != nil {
|
||||
debugf("Cannot send AppendEntriesRequest to %s: %s", u, err)
|
||||
log.Debugf("Cannot send AppendEntriesRequest to %s: %s", u, err)
|
||||
if ok {
|
||||
thisFollowerStats.Fail()
|
||||
}
|
||||
@@ -121,13 +122,13 @@ func (t *transporter) SendVoteRequest(server *raft.Server, peer *raft.Peer, req
|
||||
var b bytes.Buffer
|
||||
json.NewEncoder(&b).Encode(req)
|
||||
|
||||
u, _ := nameToRaftURL(peer.Name)
|
||||
debugf("Send Vote to %s", u)
|
||||
u, _ := t.peerServer.registry.PeerURL(peer.Name)
|
||||
log.Debugf("Send Vote to %s", u)
|
||||
|
||||
resp, httpRequest, err := t.Post(fmt.Sprintf("%s/vote", u), &b)
|
||||
|
||||
if err != nil {
|
||||
debugf("Cannot send VoteRequest to %s : %s", u, err)
|
||||
log.Debugf("Cannot send VoteRequest to %s : %s", u, err)
|
||||
}
|
||||
|
||||
if resp != nil {
|
||||
@@ -150,14 +151,14 @@ func (t *transporter) SendSnapshotRequest(server *raft.Server, peer *raft.Peer,
|
||||
var b bytes.Buffer
|
||||
json.NewEncoder(&b).Encode(req)
|
||||
|
||||
u, _ := nameToRaftURL(peer.Name)
|
||||
debugf("Send Snapshot to %s [Last Term: %d, LastIndex %d]", u,
|
||||
u, _ := t.peerServer.registry.PeerURL(peer.Name)
|
||||
log.Debugf("Send Snapshot to %s [Last Term: %d, LastIndex %d]", u,
|
||||
req.LastTerm, req.LastIndex)
|
||||
|
||||
resp, httpRequest, err := t.Post(fmt.Sprintf("%s/snapshot", u), &b)
|
||||
|
||||
if err != nil {
|
||||
debugf("Cannot send SendSnapshotRequest to %s : %s", u, err)
|
||||
log.Debugf("Cannot send SendSnapshotRequest to %s : %s", u, err)
|
||||
}
|
||||
|
||||
if resp != nil {
|
||||
@@ -181,14 +182,14 @@ func (t *transporter) SendSnapshotRecoveryRequest(server *raft.Server, peer *raf
|
||||
var b bytes.Buffer
|
||||
json.NewEncoder(&b).Encode(req)
|
||||
|
||||
u, _ := nameToRaftURL(peer.Name)
|
||||
debugf("Send SnapshotRecovery to %s [Last Term: %d, LastIndex %d]", u,
|
||||
u, _ := t.peerServer.registry.PeerURL(peer.Name)
|
||||
log.Debugf("Send SnapshotRecovery to %s [Last Term: %d, LastIndex %d]", u,
|
||||
req.LastTerm, req.LastIndex)
|
||||
|
||||
resp, _, err := t.Post(fmt.Sprintf("%s/snapshotRecovery", u), &b)
|
||||
|
||||
if err != nil {
|
||||
debugf("Cannot send SendSnapshotRecoveryRequest to %s : %s", u, err)
|
||||
log.Debugf("Cannot send SendSnapshotRecoveryRequest to %s : %s", u, err)
|
||||
}
|
||||
|
||||
if resp != nil {
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/coreos/etcd/log"
|
||||
)
|
||||
|
||||
@@ -18,7 +21,7 @@ func decodeJsonRequest(req *http.Request, data interface{}) error {
|
||||
func redirect(hostname string, w http.ResponseWriter, req *http.Request) {
|
||||
path := req.URL.Path
|
||||
url := hostname + path
|
||||
debugf("Redirect to %s", url)
|
||||
log.Debugf("Redirect to %s", url)
|
||||
http.Redirect(w, req, url, http.StatusTemporaryRedirect)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/coreos/etcd/store"
|
||||
"net/http"
|
||||
"github.com/coreos/etcd/store"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// Removes a key from the store.
|
||||
func DeleteKeyHandler(w http.ResponseWriter, req *http.Request, s Server) error {
|
||||
vars := mux.Vars(req)
|
||||
key := "/" + vars["key"]
|
||||
command := &DeleteCommand{Key: key}
|
||||
return s.Dispatch(command, w, req)
|
||||
c := &store.DeleteCommand{Key: key}
|
||||
return s.Dispatch(c, w, req)
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
package v1
|
||||
|
||||
// Dispatch the command to leader.
|
||||
func dispatchCommand(c Command, w http.ResponseWriter, req *http.Request, s *server.Server) error {
|
||||
return dispatch(c, w, req, s, nameToEtcdURL)
|
||||
}
|
||||
|
||||
// Dispatches a command to a given URL.
|
||||
func dispatch(c Command, w http.ResponseWriter, req *http.Request, s *server.Server, toURL func(name string) (string, bool)) error {
|
||||
r := s.raftServer
|
||||
if r.State() == raft.Leader {
|
||||
event, err := r.Do(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if event == nil {
|
||||
return etcdErr.NewError(300, "Empty result from raft", store.UndefIndex, store.UndefTerm)
|
||||
}
|
||||
|
||||
event, _ := event.(*store.Event)
|
||||
response := eventToResponse(event)
|
||||
b, _ := json.Marshal(response)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(b)
|
||||
|
||||
return nil
|
||||
|
||||
} else {
|
||||
leader := r.Leader()
|
||||
|
||||
// No leader available.
|
||||
if leader == "" {
|
||||
return etcdErr.NewError(300, "", store.UndefIndex, store.UndefTerm)
|
||||
}
|
||||
|
||||
url, _ := toURL(leader)
|
||||
redirect(url, w, req)
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,13 @@ package v1
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/coreos/etcd/store"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// Retrieves the value for a given key.
|
||||
func GetKeyHandler(w http.ResponseWriter, req *http.Request, e *etcdServer) error {
|
||||
func GetKeyHandler(w http.ResponseWriter, req *http.Request, s Server) error {
|
||||
vars := mux.Vars(req)
|
||||
key := "/" + vars["key"]
|
||||
|
||||
@@ -18,9 +19,7 @@ func GetKeyHandler(w http.ResponseWriter, req *http.Request, e *etcdServer) erro
|
||||
}
|
||||
|
||||
// Convert event to a response and write to client.
|
||||
event, _ := event.(*store.Event)
|
||||
response := eventToResponse(event)
|
||||
b, _ := json.Marshal(response)
|
||||
b, _ := json.Marshal(event.Response())
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(b)
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/coreos/etcd/store"
|
||||
"net/http"
|
||||
|
||||
etcdErr "github.com/coreos/etcd/error"
|
||||
"github.com/coreos/etcd/store"
|
||||
"github.com/coreos/go-raft"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// Sets the value for a given key.
|
||||
@@ -16,19 +19,19 @@ func SetKeyHandler(w http.ResponseWriter, req *http.Request, s Server) error {
|
||||
// Parse non-blank value.
|
||||
value := req.Form.Get("value")
|
||||
if len(value) == 0 {
|
||||
return error.NewError(200, "Set", store.UndefIndex, store.UndefTerm)
|
||||
return etcdErr.NewError(200, "Set", store.UndefIndex, store.UndefTerm)
|
||||
}
|
||||
|
||||
// Convert time-to-live to an expiration time.
|
||||
expireTime, err := durationToExpireTime(req.Form.Get("ttl"))
|
||||
expireTime, err := store.TTL(req.Form.Get("ttl"))
|
||||
if err != nil {
|
||||
return etcdErr.NewError(202, "Set", store.UndefIndex, store.UndefTerm)
|
||||
}
|
||||
|
||||
// If the "prevValue" is specified then test-and-set. Otherwise create a new key.
|
||||
var c command.Command
|
||||
var c raft.Command
|
||||
if prevValueArr, ok := req.Form["prevValue"]; ok && len(prevValueArr) > 0 {
|
||||
c = &TestAndSetCommand{
|
||||
c = &store.TestAndSetCommand{
|
||||
Key: key,
|
||||
Value: value,
|
||||
PrevValue: prevValueArr[0],
|
||||
@@ -36,7 +39,7 @@ func SetKeyHandler(w http.ResponseWriter, req *http.Request, s Server) error {
|
||||
}
|
||||
|
||||
} else {
|
||||
c = &CreateCommand{
|
||||
c = &store.CreateCommand{
|
||||
Key: key,
|
||||
Value: value,
|
||||
ExpireTime: expireTime,
|
||||
@@ -44,5 +47,5 @@ func SetKeyHandler(w http.ResponseWriter, req *http.Request, s Server) error {
|
||||
}
|
||||
}
|
||||
|
||||
return s.Dispatch(command, w, req)
|
||||
return s.Dispatch(c, w, req)
|
||||
}
|
||||
|
||||
@@ -1,50 +1,15 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/coreos/etcd/server"
|
||||
"github.com/gorilla/mux"
|
||||
"net/http"
|
||||
"github.com/coreos/etcd/store"
|
||||
"github.com/coreos/go-raft"
|
||||
)
|
||||
|
||||
// The Server interface provides all the methods required for the v1 API.
|
||||
type Server interface {
|
||||
CommitIndex() uint64
|
||||
Term() uint64
|
||||
Dispatch(http.ResponseWriter, *http.Request, Command)
|
||||
}
|
||||
|
||||
// Converts an event object into a response object.
|
||||
func eventToResponse(event *store.Event) interface{} {
|
||||
if !event.Dir {
|
||||
response := &store.Response{
|
||||
Action: event.Action,
|
||||
Key: event.Key,
|
||||
Value: event.Value,
|
||||
PrevValue: event.PrevValue,
|
||||
Index: event.Index,
|
||||
TTL: event.TTL,
|
||||
Expiration: event.Expiration,
|
||||
}
|
||||
|
||||
if response.Action == store.Create || response.Action == store.Update {
|
||||
response.Action = "set"
|
||||
if response.PrevValue == "" {
|
||||
response.NewKey = true
|
||||
}
|
||||
}
|
||||
|
||||
return response
|
||||
} else {
|
||||
responses := make([]*store.Response, len(event.KVPairs))
|
||||
|
||||
for i, kv := range event.KVPairs {
|
||||
responses[i] = &store.Response{
|
||||
Action: event.Action,
|
||||
Key: kv.Key,
|
||||
Value: kv.Value,
|
||||
Dir: kv.Dir,
|
||||
Index: event.Index,
|
||||
}
|
||||
}
|
||||
return responses
|
||||
}
|
||||
Store() *store.Store
|
||||
Dispatch(raft.Command, http.ResponseWriter, *http.Request) error
|
||||
}
|
||||
|
||||
@@ -2,8 +2,12 @@ package v1
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/coreos/etcd/store"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
etcdErr "github.com/coreos/etcd/error"
|
||||
"github.com/coreos/etcd/store"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// Watches a given key prefix for changes.
|
||||
@@ -13,7 +17,7 @@ func WatchKeyHandler(w http.ResponseWriter, req *http.Request, s Server) error {
|
||||
key := "/" + vars["key"]
|
||||
|
||||
// Create a command to watch from a given index (default 0).
|
||||
sinceIndex := 0
|
||||
var sinceIndex uint64 = 0
|
||||
if req.Method == "POST" {
|
||||
sinceIndex, err = strconv.ParseUint(string(req.FormValue("index")), 10, 64)
|
||||
if err != nil {
|
||||
@@ -28,9 +32,7 @@ func WatchKeyHandler(w http.ResponseWriter, req *http.Request, s Server) error {
|
||||
}
|
||||
event := <-c
|
||||
|
||||
event, _ := event.(*store.Event)
|
||||
response := eventToResponse(event)
|
||||
b, _ := json.Marshal(response)
|
||||
b, _ := json.Marshal(event.Response())
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(b)
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ func (e *etcdServer) CreateHttpHandler(w http.ResponseWriter, req *http.Request)
|
||||
|
||||
value := req.FormValue("value")
|
||||
|
||||
expireTime, err := durationToExpireTime(req.FormValue("ttl"))
|
||||
expireTime, err := store.TTL(req.FormValue("ttl"))
|
||||
|
||||
if err != nil {
|
||||
return etcdErr.NewError(etcdErr.EcodeTTLNaN, "Create", store.UndefIndex, store.UndefTerm)
|
||||
@@ -124,7 +124,7 @@ func (e *etcdServer) UpdateHttpHandler(w http.ResponseWriter, req *http.Request)
|
||||
|
||||
value := req.Form.Get("value")
|
||||
|
||||
expireTime, err := durationToExpireTime(req.Form.Get("ttl"))
|
||||
expireTime, err := store.TTL(req.Form.Get("ttl"))
|
||||
|
||||
if err != nil {
|
||||
return etcdErr.NewError(etcdErr.EcodeTTLNaN, "Update", store.UndefIndex, store.UndefTerm)
|
||||
@@ -344,6 +344,16 @@ func (e *etcdServer) GetHttpHandler(w http.ResponseWriter, req *http.Request) er
|
||||
|
||||
}
|
||||
|
||||
func getNodePath(urlPath string) string {
|
||||
pathPrefixLen := len("/" + version + "/keys")
|
||||
return urlPath[pathPrefixLen:]
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
// Testing
|
||||
//--------------------------------------
|
||||
|
||||
// TestHandler
|
||||
func TestHttpHandler(w http.ResponseWriter, req *http.Request) {
|
||||
testType := req.URL.Path[len("/test/"):]
|
||||
@@ -358,3 +368,25 @@ func TestHttpHandler(w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
}
|
||||
|
||||
func directSet() {
|
||||
c := make(chan bool, 1000)
|
||||
for i := 0; i < 1000; i++ {
|
||||
go send(c)
|
||||
}
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
<-c
|
||||
}
|
||||
}
|
||||
|
||||
func send(c chan bool) {
|
||||
for i := 0; i < 10; i++ {
|
||||
command := &UpdateCommand{}
|
||||
command.Key = "foo"
|
||||
command.Value = "bar"
|
||||
command.ExpireTime = time.Unix(0, 0)
|
||||
//r.Do(command)
|
||||
}
|
||||
c <- true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user