wait for http server and graceful shutdown

This commit is contained in:
Martin Boehm 2018-01-18 17:33:20 +01:00
parent bbcadfd646
commit 4e4c17a41a
2 changed files with 59 additions and 26 deletions

View File

@ -1,11 +1,16 @@
package main package main
import ( import (
"context"
"flag" "flag"
"log" "log"
"os"
"os/signal"
"sync" "sync"
"syscall"
"time" "time"
"blockbook/bitcoin"
"blockbook/db" "blockbook/db"
"blockbook/server" "blockbook/server"
@ -15,16 +20,16 @@ import (
type Blockchain interface { type Blockchain interface {
GetBestBlockHash() (string, error) GetBestBlockHash() (string, error)
GetBlockHash(height uint32) (string, error) GetBlockHash(height uint32) (string, error)
GetBlockHeader(hash string) (*BlockHeader, error) GetBlockHeader(hash string) (*bitcoin.BlockHeader, error)
GetBlock(hash string) (*Block, error) GetBlock(hash string) (*bitcoin.Block, error)
} }
type Index interface { type Index interface {
GetBestBlockHash() (string, error) GetBestBlockHash() (string, error)
GetBlockHash(height uint32) (string, error) GetBlockHash(height uint32) (string, error)
GetTransactions(address string, lower uint32, higher uint32, fn func(txid string) error) error GetTransactions(address string, lower uint32, higher uint32, fn func(txid string) error) error
ConnectBlock(block *Block) error ConnectBlock(block *bitcoin.Block) error
DisconnectBlock(block *Block) error DisconnectBlock(block *bitcoin.Block) error
} }
var ( var (
@ -49,14 +54,14 @@ var (
dryRun = flag.Bool("dryrun", false, "do not index blocks, only download") dryRun = flag.Bool("dryrun", false, "do not index blocks, only download")
parse = flag.Bool("parse", false, "use in-process block parsing") 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() { func main() {
flag.Parse() flag.Parse()
if *repair { if *repair {
if err := RepairRocksDB(*dbPath); err != nil { if err := db.RepairRocksDB(*dbPath); err != nil {
log.Fatal(err) log.Fatal(err)
} }
return return
@ -66,15 +71,15 @@ func main() {
defer profile.Start().Stop() defer profile.Start().Stop()
} }
rpc := NewBitcoinRPC( rpc := bitcoin.NewBitcoinRPC(
*rpcURL, *rpcURL,
*rpcUser, *rpcUser,
*rpcPass, *rpcPass,
time.Duration(*rpcTimeout)*time.Second) time.Duration(*rpcTimeout)*time.Second)
if *parse { if *parse {
rpc.Parser = &BitcoinBlockParser{ rpc.Parser = &bitcoin.BitcoinBlockParser{
Params: GetChainParams()[0], Params: bitcoin.GetChainParams()[0],
} }
} }
@ -84,15 +89,19 @@ func main() {
} }
defer db.Close() defer db.Close()
if *httpServer { var httpServer *server.HttpServer
s, err := server.New(db)
if err != nil { if *startHTTPServer {
log.Fatalf("https: %s", err) httpServer, err = server.New(db)
}
err = s.Run()
if err != nil { if err != nil {
log.Fatalf("https: %s", err) log.Fatalf("https: %s", err)
} }
go func() {
err = httpServer.Run()
if err != nil {
log.Fatalf("https: %s", err)
}
}()
} }
if *resync { 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 { func printResult(txid string) error {
@ -165,7 +197,7 @@ func resyncIndex(chain Blockchain, index Index) error {
header, err := chain.GetBlockHeader(local) header, err := chain.GetBlockHeader(local)
forked := false forked := false
if err != nil { 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 forked = true
} else { } else {
return err return err
@ -310,7 +342,7 @@ func isBlockConnected(
} }
type blockResult struct { type blockResult struct {
block *Block block *bitcoin.Block
err error err error
} }

View File

@ -2,6 +2,7 @@ package server
import ( import (
"blockbook/db" "blockbook/db"
"context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
@ -11,16 +12,16 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )
type server struct { type HttpServer struct {
https *http.Server https *http.Server
db *db.RocksDB db *db.RocksDB
} }
func New(db *db.RocksDB) (*server, error) { func New(db *db.RocksDB) (*HttpServer, error) {
https := &http.Server{ https := &http.Server{
Addr: ":8333", Addr: ":8333",
} }
s := &server{ s := &HttpServer{
https: https, https: https,
db: db, db: db,
} }
@ -36,22 +37,22 @@ func New(db *db.RocksDB) (*server, error) {
return s, nil return s, nil
} }
func (s *server) Run() error { func (s *HttpServer) Run() error {
fmt.Printf("http server starting") fmt.Printf("http server starting on port %s", s.https.Addr)
return s.https.ListenAndServe() return s.https.ListenAndServe()
} }
func (s *server) Close() error { func (s *HttpServer) Close() error {
fmt.Printf("http server closing") fmt.Printf("http server closing")
return s.https.Close() return s.https.Close()
} }
func (s *server) Shutdown() error { func (s *HttpServer) Shutdown(ctx context.Context) error {
fmt.Printf("http server shutdown") 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 { type info struct {
Version string `json:"version"` Version string `json:"version"`
} }