From 4e4c17a41ae39ce63ebcfc9a36c9412444093ef2 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Thu, 18 Jan 2018 17:33:20 +0100 Subject: [PATCH] wait for http server and graceful shutdown --- blockbook.go | 66 ++++++++++++++++++++++++++++++++++++------------- server/https.go | 19 +++++++------- 2 files changed, 59 insertions(+), 26 deletions(-) diff --git a/blockbook.go b/blockbook.go index aaf335ec..b7f96de9 100644 --- a/blockbook.go +++ b/blockbook.go @@ -1,11 +1,16 @@ package main import ( + "context" "flag" "log" + "os" + "os/signal" "sync" + "syscall" "time" + "blockbook/bitcoin" "blockbook/db" "blockbook/server" @@ -15,16 +20,16 @@ import ( type Blockchain interface { GetBestBlockHash() (string, error) GetBlockHash(height uint32) (string, error) - GetBlockHeader(hash string) (*BlockHeader, error) - GetBlock(hash string) (*Block, error) + GetBlockHeader(hash string) (*bitcoin.BlockHeader, error) + GetBlock(hash string) (*bitcoin.Block, error) } type Index interface { GetBestBlockHash() (string, error) GetBlockHash(height uint32) (string, error) GetTransactions(address string, lower uint32, higher uint32, fn func(txid string) error) error - ConnectBlock(block *Block) error - DisconnectBlock(block *Block) error + ConnectBlock(block *bitcoin.Block) error + DisconnectBlock(block *bitcoin.Block) error } var ( @@ -49,14 +54,14 @@ var ( dryRun = flag.Bool("dryrun", false, "do not index blocks, only download") parse = flag.Bool("parse", false, "use in-process block parsing") - httpServer = flag.Bool("http", true, "run http server (default true)") + startHTTPServer = flag.Bool("httpserver", true, "run http server (default true)") ) func main() { flag.Parse() if *repair { - if err := RepairRocksDB(*dbPath); err != nil { + if err := db.RepairRocksDB(*dbPath); err != nil { log.Fatal(err) } return @@ -66,15 +71,15 @@ func main() { defer profile.Start().Stop() } - rpc := NewBitcoinRPC( + rpc := bitcoin.NewBitcoinRPC( *rpcURL, *rpcUser, *rpcPass, time.Duration(*rpcTimeout)*time.Second) if *parse { - rpc.Parser = &BitcoinBlockParser{ - Params: GetChainParams()[0], + rpc.Parser = &bitcoin.BitcoinBlockParser{ + Params: bitcoin.GetChainParams()[0], } } @@ -84,15 +89,19 @@ func main() { } defer db.Close() - if *httpServer { - s, err := server.New(db) - if err != nil { - log.Fatalf("https: %s", err) - } - err = s.Run() + var httpServer *server.HttpServer + + if *startHTTPServer { + httpServer, err = server.New(db) if err != nil { log.Fatalf("https: %s", err) } + go func() { + err = httpServer.Run() + if err != nil { + log.Fatalf("https: %s", err) + } + }() } if *resync { @@ -126,6 +135,29 @@ func main() { } } } + + if httpServer != nil { + waitForSignalAndShutdown(httpServer, 5*time.Second) + } +} + +func waitForSignalAndShutdown(s *server.HttpServer, timeout time.Duration) { + stop := make(chan os.Signal, 1) + + signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM) + + <-stop + + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + log.Printf("\nShutdown with timeout: %s\n", timeout) + + if err := s.Shutdown(ctx); err != nil { + log.Printf("Error: %v\n", err) + } else { + log.Println("Server stopped") + } } func printResult(txid string) error { @@ -165,7 +197,7 @@ func resyncIndex(chain Blockchain, index Index) error { header, err := chain.GetBlockHeader(local) forked := false if err != nil { - if e, ok := err.(*RPCError); ok && e.Message == "Block not found" { + if e, ok := err.(*bitcoin.RPCError); ok && e.Message == "Block not found" { forked = true } else { return err @@ -310,7 +342,7 @@ func isBlockConnected( } type blockResult struct { - block *Block + block *bitcoin.Block err error } diff --git a/server/https.go b/server/https.go index 5312a540..a129b1a4 100644 --- a/server/https.go +++ b/server/https.go @@ -2,6 +2,7 @@ package server import ( "blockbook/db" + "context" "encoding/json" "fmt" "net/http" @@ -11,16 +12,16 @@ import ( "github.com/gorilla/mux" ) -type server struct { +type HttpServer struct { https *http.Server db *db.RocksDB } -func New(db *db.RocksDB) (*server, error) { +func New(db *db.RocksDB) (*HttpServer, error) { https := &http.Server{ Addr: ":8333", } - s := &server{ + s := &HttpServer{ https: https, db: db, } @@ -36,22 +37,22 @@ func New(db *db.RocksDB) (*server, error) { return s, nil } -func (s *server) Run() error { - fmt.Printf("http server starting") +func (s *HttpServer) Run() error { + fmt.Printf("http server starting on port %s", s.https.Addr) return s.https.ListenAndServe() } -func (s *server) Close() error { +func (s *HttpServer) Close() error { fmt.Printf("http server closing") return s.https.Close() } -func (s *server) Shutdown() error { +func (s *HttpServer) Shutdown(ctx context.Context) error { fmt.Printf("http server shutdown") - return s.https.Shutdown() + return s.https.Shutdown(ctx) } -func (s *server) Info(w http.ResponseWriter, r *http.Request) { +func (s *HttpServer) Info(w http.ResponseWriter, r *http.Request) { type info struct { Version string `json:"version"` }