lnd: make Ready signal for custom listeners

This allows the caller to know when lnd is ready to accept RPC calls,
which is inmportant for mobile applications where eveything happens in
process.
This commit is contained in:
Johan T. Halseth 2019-11-29 11:35:37 +01:00
parent d59aba35a0
commit dc6c040803
No known key found for this signature in database
GPG Key ID: 15BAADA29DA20D26
2 changed files with 38 additions and 18 deletions

48
lnd.go
View File

@ -59,16 +59,25 @@ var (
networkDir string networkDir string
) )
// ListnerWithSignal is a net.Listner that has an additional Ready channel that
// will be closed when a server starts listening.
type ListenerWithSignal struct {
net.Listener
// Ready will be closed by the server listening on Listener.
Ready chan struct{}
}
// ListenerCfg is a wrapper around custom listeners that can be passed to lnd // ListenerCfg is a wrapper around custom listeners that can be passed to lnd
// when calling its main method. // when calling its main method.
type ListenerCfg struct { type ListenerCfg struct {
// WalletUnlocker can be set to the listener to use for the wallet // WalletUnlocker can be set to the listener to use for the wallet
// unlocker. If nil a regular network listener will be created. // unlocker. If nil a regular network listener will be created.
WalletUnlocker net.Listener WalletUnlocker *ListenerWithSignal
// RPCListener can be set to the listener to use for the RPC server. If // RPCListener can be set to the listener to use for the RPC server. If
// nil a regular network listener will be created. // nil a regular network listener will be created.
RPCListener net.Listener RPCListener *ListenerWithSignal
} }
// rpcListeners is a function type used for closures that fetches a set of RPC // rpcListeners is a function type used for closures that fetches a set of RPC
@ -76,7 +85,8 @@ type ListenerCfg struct {
// with these listeners. If no custom listeners are present, this should return // with these listeners. If no custom listeners are present, this should return
// normal listeners from the RPC endpoints defined in the config, and server // normal listeners from the RPC endpoints defined in the config, and server
// options specifying TLS. // options specifying TLS.
type rpcListeners func() ([]net.Listener, func(), []grpc.ServerOption, error) type rpcListeners func() ([]*ListenerWithSignal, func(), []grpc.ServerOption,
error)
// Main is the true entry point for lnd. This function is required since defers // Main is the true entry point for lnd. This function is required since defers
// created in the top-level scope of a main method aren't executed if os.Exit() // created in the top-level scope of a main method aren't executed if os.Exit()
@ -235,10 +245,10 @@ func Main(lisCfg ListenerCfg) error {
// getListeners is a closure that creates listeners from the // getListeners is a closure that creates listeners from the
// RPCListeners defined in the config. It also returns a cleanup // RPCListeners defined in the config. It also returns a cleanup
// closure and the server options to use for the GRPC server. // closure and the server options to use for the GRPC server.
getListeners := func() ([]net.Listener, func(), []grpc.ServerOption, getListeners := func() ([]*ListenerWithSignal, func(),
error) { []grpc.ServerOption, error) {
var grpcListeners []net.Listener var grpcListeners []*ListenerWithSignal
for _, grpcEndpoint := range cfg.RPCListeners { for _, grpcEndpoint := range cfg.RPCListeners {
// Start a gRPC server listening for HTTP/2 // Start a gRPC server listening for HTTP/2
// connections. // connections.
@ -248,7 +258,11 @@ func Main(lisCfg ListenerCfg) error {
grpcEndpoint) grpcEndpoint)
return nil, nil, nil, err return nil, nil, nil, err
} }
grpcListeners = append(grpcListeners, lis) grpcListeners = append(
grpcListeners, &ListenerWithSignal{
Listener: lis,
Ready: make(chan struct{}),
})
} }
cleanup := func() { cleanup := func() {
@ -262,7 +276,7 @@ func Main(lisCfg ListenerCfg) error {
// walletUnlockerListeners is a closure we'll hand to the wallet // walletUnlockerListeners is a closure we'll hand to the wallet
// unlocker, that will be called when it needs listeners for its GPRC // unlocker, that will be called when it needs listeners for its GPRC
// server. // server.
walletUnlockerListeners := func() ([]net.Listener, func(), walletUnlockerListeners := func() ([]*ListenerWithSignal, func(),
[]grpc.ServerOption, error) { []grpc.ServerOption, error) {
// If we have chosen to start with a dedicated listener for the // If we have chosen to start with a dedicated listener for the
@ -271,8 +285,8 @@ func Main(lisCfg ListenerCfg) error {
// TODO(halseth): any point in adding TLS support for custom // TODO(halseth): any point in adding TLS support for custom
// listeners? // listeners?
if lisCfg.WalletUnlocker != nil { if lisCfg.WalletUnlocker != nil {
return []net.Listener{lisCfg.WalletUnlocker}, func() {}, return []*ListenerWithSignal{lisCfg.WalletUnlocker},
[]grpc.ServerOption{}, nil func() {}, []grpc.ServerOption{}, nil
} }
// Otherwise we'll return the regular listeners. // Otherwise we'll return the regular listeners.
@ -501,8 +515,8 @@ func Main(lisCfg ListenerCfg) error {
// rpcListeners is a closure we'll hand to the rpc server, that will be // rpcListeners is a closure we'll hand to the rpc server, that will be
// called when it needs listeners for its GPRC server. // called when it needs listeners for its GPRC server.
rpcListeners := func() ([]net.Listener, func(), []grpc.ServerOption, rpcListeners := func() ([]*ListenerWithSignal, func(),
error) { []grpc.ServerOption, error) {
// If we have chosen to start with a dedicated listener for the // If we have chosen to start with a dedicated listener for the
// rpc server, we return it directly, and empty server options // rpc server, we return it directly, and empty server options
@ -510,8 +524,8 @@ func Main(lisCfg ListenerCfg) error {
// TODO(halseth): any point in adding TLS support for custom // TODO(halseth): any point in adding TLS support for custom
// listeners? // listeners?
if lisCfg.RPCListener != nil { if lisCfg.RPCListener != nil {
return []net.Listener{lisCfg.RPCListener}, func() {}, return []*ListenerWithSignal{lisCfg.RPCListener},
[]grpc.ServerOption{}, nil func() {}, []grpc.ServerOption{}, nil
} }
// Otherwise we'll return the regular listeners. // Otherwise we'll return the regular listeners.
@ -841,9 +855,13 @@ func waitForWalletPassword(restEndpoints []net.Addr,
for _, lis := range listeners { for _, lis := range listeners {
wg.Add(1) wg.Add(1)
go func(lis net.Listener) { go func(lis *ListenerWithSignal) {
rpcsLog.Infof("password RPC server listening on %s", rpcsLog.Infof("password RPC server listening on %s",
lis.Addr()) lis.Addr())
// Close the ready chan to indicate we are listening.
close(lis.Ready)
wg.Done() wg.Done()
grpcServer.Serve(lis) grpcServer.Serve(lis)
}(lis) }(lis)

View File

@ -9,7 +9,6 @@ import (
"fmt" "fmt"
"io" "io"
"math" "math"
"net"
"net/http" "net/http"
"sort" "sort"
"strings" "strings"
@ -456,7 +455,7 @@ type rpcServer struct {
// listeners is a list of listeners to use when starting the grpc // listeners is a list of listeners to use when starting the grpc
// server. We make it configurable such that the grpc server can listen // server. We make it configurable such that the grpc server can listen
// on custom interfaces. // on custom interfaces.
listeners []net.Listener listeners []*ListenerWithSignal
// listenerCleanUp are a set of closures functions that will allow this // listenerCleanUp are a set of closures functions that will allow this
// main RPC server to clean up all the listening socket created for the // main RPC server to clean up all the listening socket created for the
@ -706,8 +705,11 @@ func (r *rpcServer) Start() error {
// With all the sub-servers started, we'll spin up the listeners for // With all the sub-servers started, we'll spin up the listeners for
// the main RPC server itself. // the main RPC server itself.
for _, lis := range r.listeners { for _, lis := range r.listeners {
go func(lis net.Listener) { go func(lis *ListenerWithSignal) {
rpcsLog.Infof("RPC server listening on %s", lis.Addr()) rpcsLog.Infof("RPC server listening on %s", lis.Addr())
// Close the ready chan to indicate we are listening.
close(lis.Ready)
r.grpcServer.Serve(lis) r.grpcServer.Serve(lis)
}(lis) }(lis)
} }