github.com/koko1123/flow-go-1@v0.29.6/engine/access/rpc/engine.go (about) 1 package rpc 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "net" 8 "net/http" 9 "sync" 10 "time" 11 12 grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus" 13 lru "github.com/hashicorp/golang-lru" 14 accessproto "github.com/onflow/flow/protobuf/go/flow/access" 15 "github.com/rs/zerolog" 16 "google.golang.org/grpc" 17 "google.golang.org/grpc/credentials" 18 19 "github.com/koko1123/flow-go-1/engine" 20 "github.com/koko1123/flow-go-1/engine/access/rest" 21 "github.com/koko1123/flow-go-1/engine/access/rpc/backend" 22 "github.com/koko1123/flow-go-1/engine/common/rpc" 23 "github.com/koko1123/flow-go-1/model/flow" 24 "github.com/koko1123/flow-go-1/module" 25 "github.com/koko1123/flow-go-1/state/protocol" 26 "github.com/koko1123/flow-go-1/storage" 27 "github.com/koko1123/flow-go-1/utils/grpcutils" 28 ) 29 30 // Config defines the configurable options for the access node server 31 // A secure GRPC server here implies a server that presents a self-signed TLS certificate and a client that authenticates 32 // the server via a pre-shared public key 33 type Config struct { 34 UnsecureGRPCListenAddr string // the non-secure GRPC server address as ip:port 35 SecureGRPCListenAddr string // the secure GRPC server address as ip:port 36 StateStreamListenAddr string // the state stream GRPC server address as ip:port 37 TransportCredentials credentials.TransportCredentials // the secure GRPC credentials 38 HTTPListenAddr string // the HTTP web proxy address as ip:port 39 RESTListenAddr string // the REST server address as ip:port (if empty the REST server will not be started) 40 CollectionAddr string // the address of the upstream collection node 41 HistoricalAccessAddrs string // the list of all access nodes from previous spork 42 MaxMsgSize int // GRPC max message size 43 MaxExecutionDataMsgSize int // GRPC max message size for block execution data 44 ExecutionClientTimeout time.Duration // execution API GRPC client timeout 45 CollectionClientTimeout time.Duration // collection API GRPC client timeout 46 ConnectionPoolSize uint // size of the cache for storing collection and execution connections 47 MaxHeightRange uint // max size of height range requests 48 PreferredExecutionNodeIDs []string // preferred list of upstream execution node IDs 49 FixedExecutionNodeIDs []string // fixed list of execution node IDs to choose from if no node node ID can be chosen from the PreferredExecutionNodeIDs 50 } 51 52 // Engine exposes the server with a simplified version of the Access API. 53 // An unsecured GRPC server (default port 9000), a secure GRPC server (default port 9001) and an HTTP Web proxy (default 54 // port 8000) are brought up. 55 type Engine struct { 56 unit *engine.Unit 57 log zerolog.Logger 58 backend *backend.Backend // the gRPC service implementation 59 unsecureGrpcServer *grpc.Server // the unsecure gRPC server 60 secureGrpcServer *grpc.Server // the secure gRPC server 61 httpServer *http.Server 62 restServer *http.Server 63 config Config 64 chain flow.Chain 65 66 addrLock sync.RWMutex 67 unsecureGrpcAddress net.Addr 68 secureGrpcAddress net.Addr 69 restAPIAddress net.Addr 70 } 71 72 // NewBuilder returns a new RPC engine builder. 73 func NewBuilder(log zerolog.Logger, 74 state protocol.State, 75 config Config, 76 collectionRPC accessproto.AccessAPIClient, 77 historicalAccessNodes []accessproto.AccessAPIClient, 78 blocks storage.Blocks, 79 headers storage.Headers, 80 collections storage.Collections, 81 transactions storage.Transactions, 82 executionReceipts storage.ExecutionReceipts, 83 executionResults storage.ExecutionResults, 84 chainID flow.ChainID, 85 transactionMetrics module.TransactionMetrics, 86 accessMetrics module.AccessMetrics, 87 collectionGRPCPort uint, 88 executionGRPCPort uint, 89 retryEnabled bool, 90 rpcMetricsEnabled bool, 91 apiRatelimits map[string]int, // the api rate limit (max calls per second) for each of the Access API e.g. Ping->100, GetTransaction->300 92 apiBurstLimits map[string]int, // the api burst limit (max calls at the same time) for each of the Access API e.g. Ping->50, GetTransaction->10 93 ) (*RPCEngineBuilder, error) { 94 95 log = log.With().Str("engine", "rpc").Logger() 96 97 if config.MaxMsgSize == 0 { 98 config.MaxMsgSize = grpcutils.DefaultMaxMsgSize 99 } 100 101 // create a GRPC server to serve GRPC clients 102 grpcOpts := []grpc.ServerOption{ 103 grpc.MaxRecvMsgSize(config.MaxMsgSize), 104 grpc.MaxSendMsgSize(config.MaxMsgSize), 105 } 106 107 var interceptors []grpc.UnaryServerInterceptor // ordered list of interceptors 108 // if rpc metrics is enabled, first create the grpc metrics interceptor 109 if rpcMetricsEnabled { 110 interceptors = append(interceptors, grpc_prometheus.UnaryServerInterceptor) 111 } 112 113 if len(apiRatelimits) > 0 { 114 // create a rate limit interceptor 115 rateLimitInterceptor := rpc.NewRateLimiterInterceptor(log, apiRatelimits, apiBurstLimits).UnaryServerInterceptor 116 // append the rate limit interceptor to the list of interceptors 117 interceptors = append(interceptors, rateLimitInterceptor) 118 } 119 120 // add the logging interceptor, ensure it is innermost wrapper 121 interceptors = append(interceptors, rpc.LoggingInterceptor(log)...) 122 123 // create a chained unary interceptor 124 chainedInterceptors := grpc.ChainUnaryInterceptor(interceptors...) 125 grpcOpts = append(grpcOpts, chainedInterceptors) 126 127 // create an unsecured grpc server 128 unsecureGrpcServer := grpc.NewServer(grpcOpts...) 129 130 // create a secure server server by using the secure grpc credentials that are passed in as part of config 131 grpcOpts = append(grpcOpts, grpc.Creds(config.TransportCredentials)) 132 secureGrpcServer := grpc.NewServer(grpcOpts...) 133 134 // wrap the unsecured server with an HTTP proxy server to serve HTTP clients 135 httpServer := NewHTTPServer(unsecureGrpcServer, config.HTTPListenAddr) 136 137 var cache *lru.Cache 138 cacheSize := config.ConnectionPoolSize 139 if cacheSize > 0 { 140 // TODO: remove this fallback after fixing issues with evictions 141 // It was observed that evictions cause connection errors for in flight requests. This works around 142 // the issue by forcing hte pool size to be greater than the number of ENs + LNs 143 if cacheSize < backend.DefaultConnectionPoolSize { 144 log.Warn().Msg("connection pool size below threshold, setting pool size to default value ") 145 cacheSize = backend.DefaultConnectionPoolSize 146 } 147 var err error 148 cache, err = lru.NewWithEvict(int(cacheSize), func(_, evictedValue interface{}) { 149 store := evictedValue.(*backend.CachedClient) 150 store.Close() 151 log.Debug().Str("grpc_conn_evicted", store.Address).Msg("closing grpc connection evicted from pool") 152 if accessMetrics != nil { 153 accessMetrics.ConnectionFromPoolEvicted() 154 } 155 }) 156 if err != nil { 157 return nil, fmt.Errorf("could not initialize connection pool cache: %w", err) 158 } 159 } 160 161 connectionFactory := &backend.ConnectionFactoryImpl{ 162 CollectionGRPCPort: collectionGRPCPort, 163 ExecutionGRPCPort: executionGRPCPort, 164 CollectionNodeGRPCTimeout: config.CollectionClientTimeout, 165 ExecutionNodeGRPCTimeout: config.ExecutionClientTimeout, 166 ConnectionsCache: cache, 167 CacheSize: cacheSize, 168 AccessMetrics: accessMetrics, 169 Log: log, 170 } 171 172 backend := backend.New(state, 173 collectionRPC, 174 historicalAccessNodes, 175 blocks, 176 headers, 177 collections, 178 transactions, 179 executionReceipts, 180 executionResults, 181 chainID, 182 transactionMetrics, 183 connectionFactory, 184 retryEnabled, 185 config.MaxHeightRange, 186 config.PreferredExecutionNodeIDs, 187 config.FixedExecutionNodeIDs, 188 log, 189 backend.DefaultSnapshotHistoryLimit, 190 ) 191 192 eng := &Engine{ 193 log: log, 194 unit: engine.NewUnit(), 195 backend: backend, 196 unsecureGrpcServer: unsecureGrpcServer, 197 secureGrpcServer: secureGrpcServer, 198 httpServer: httpServer, 199 config: config, 200 chain: chainID.Chain(), 201 } 202 203 builder := NewRPCEngineBuilder(eng) 204 if rpcMetricsEnabled { 205 builder.WithMetrics() 206 } 207 208 return builder, nil 209 } 210 211 // Ready returns a ready channel that is closed once the engine has fully 212 // started. The RPC engine is ready when the gRPC server has successfully 213 // started. 214 func (e *Engine) Ready() <-chan struct{} { 215 e.unit.Launch(e.serveUnsecureGRPC) 216 e.unit.Launch(e.serveSecureGRPC) 217 e.unit.Launch(e.serveGRPCWebProxy) 218 if e.config.RESTListenAddr != "" { 219 e.unit.Launch(e.serveREST) 220 } 221 return e.unit.Ready() 222 } 223 224 // Done returns a done channel that is closed once the engine has fully stopped. 225 // It sends a signal to stop the gRPC server, then closes the channel. 226 func (e *Engine) Done() <-chan struct{} { 227 return e.unit.Done( 228 e.unsecureGrpcServer.GracefulStop, 229 e.secureGrpcServer.GracefulStop, 230 func() { 231 err := e.httpServer.Shutdown(context.Background()) 232 if err != nil { 233 e.log.Error().Err(err).Msg("error stopping http server") 234 } 235 }, 236 func() { 237 if e.restServer != nil { 238 err := e.restServer.Shutdown(context.Background()) 239 if err != nil { 240 e.log.Error().Err(err).Msg("error stopping http REST server") 241 } 242 } 243 }) 244 } 245 246 // SubmitLocal submits an event originating on the local node. 247 func (e *Engine) SubmitLocal(event interface{}) { 248 e.unit.Launch(func() { 249 err := e.process(event) 250 if err != nil { 251 e.log.Error().Err(err).Msg("could not process submitted event") 252 } 253 }) 254 } 255 256 func (e *Engine) UnsecureGRPCAddress() net.Addr { 257 e.addrLock.RLock() 258 defer e.addrLock.RUnlock() 259 return e.unsecureGrpcAddress 260 } 261 262 func (e *Engine) SecureGRPCAddress() net.Addr { 263 e.addrLock.RLock() 264 defer e.addrLock.RUnlock() 265 return e.secureGrpcAddress 266 } 267 268 func (e *Engine) RestApiAddress() net.Addr { 269 e.addrLock.RLock() 270 defer e.addrLock.RUnlock() 271 return e.restAPIAddress 272 } 273 274 // process processes the given ingestion engine event. Events that are given 275 // to this function originate within the expulsion engine on the node with the 276 // given origin ID. 277 func (e *Engine) process(event interface{}) error { 278 switch entity := event.(type) { 279 case *flow.Block: 280 e.backend.NotifyFinalizedBlockHeight(entity.Header.Height) 281 return nil 282 default: 283 return fmt.Errorf("invalid event type (%T)", event) 284 } 285 } 286 287 // serveUnsecureGRPC starts the unsecure gRPC server 288 // When this function returns, the server is considered ready. 289 func (e *Engine) serveUnsecureGRPC() { 290 291 e.log.Info().Str("grpc_address", e.config.UnsecureGRPCListenAddr).Msg("starting grpc server on address") 292 293 l, err := net.Listen("tcp", e.config.UnsecureGRPCListenAddr) 294 if err != nil { 295 e.log.Err(err).Msg("failed to start the grpc server") 296 return 297 } 298 299 // save the actual address on which we are listening (may be different from e.config.UnsecureGRPCListenAddr if not port 300 // was specified) 301 e.addrLock.Lock() 302 e.unsecureGrpcAddress = l.Addr() 303 e.addrLock.Unlock() 304 305 e.log.Debug().Str("unsecure_grpc_address", e.unsecureGrpcAddress.String()).Msg("listening on port") 306 307 err = e.unsecureGrpcServer.Serve(l) // blocking call 308 if err != nil { 309 e.log.Fatal().Err(err).Msg("fatal error in unsecure grpc server") 310 } 311 } 312 313 // serveSecureGRPC starts the secure gRPC server 314 // When this function returns, the server is considered ready. 315 func (e *Engine) serveSecureGRPC() { 316 317 e.log.Info().Str("secure_grpc_address", e.config.SecureGRPCListenAddr).Msg("starting grpc server on address") 318 319 l, err := net.Listen("tcp", e.config.SecureGRPCListenAddr) 320 if err != nil { 321 e.log.Err(err).Msg("failed to start the grpc server") 322 return 323 } 324 325 e.addrLock.Lock() 326 e.secureGrpcAddress = l.Addr() 327 e.addrLock.Unlock() 328 329 e.log.Debug().Str("secure_grpc_address", e.secureGrpcAddress.String()).Msg("listening on port") 330 331 err = e.secureGrpcServer.Serve(l) // blocking call 332 if err != nil { 333 e.log.Fatal().Err(err).Msg("fatal error in secure grpc server") 334 } 335 } 336 337 // serveGRPCWebProxy starts the gRPC web proxy server 338 func (e *Engine) serveGRPCWebProxy() { 339 log := e.log.With().Str("http_proxy_address", e.config.HTTPListenAddr).Logger() 340 341 log.Info().Msg("starting http proxy server on address") 342 343 err := e.httpServer.ListenAndServe() 344 if errors.Is(err, http.ErrServerClosed) { 345 return 346 } 347 if err != nil { 348 e.log.Err(err).Msg("failed to start the http proxy server") 349 } 350 } 351 352 // serveREST starts the HTTP REST server 353 func (e *Engine) serveREST() { 354 355 e.log.Info().Str("rest_api_address", e.config.RESTListenAddr).Msg("starting REST server on address") 356 357 r, err := rest.NewServer(e.backend, e.config.RESTListenAddr, e.log, e.chain) 358 if err != nil { 359 e.log.Err(err).Msg("failed to initialize the REST server") 360 return 361 } 362 e.restServer = r 363 364 l, err := net.Listen("tcp", e.config.RESTListenAddr) 365 if err != nil { 366 e.log.Err(err).Msg("failed to start the REST server") 367 return 368 } 369 370 e.addrLock.Lock() 371 e.restAPIAddress = l.Addr() 372 e.addrLock.Unlock() 373 374 e.log.Debug().Str("rest_api_address", e.restAPIAddress.String()).Msg("listening on port") 375 376 err = e.restServer.Serve(l) // blocking call 377 if err != nil { 378 if errors.Is(err, http.ErrServerClosed) { 379 return 380 } 381 e.log.Error().Err(err).Msg("fatal error in REST server") 382 } 383 }