The App struct is the central component in Kratos that manages the entire application lifecycle. It coordinates the startup and shutdown of all services, handles signal management, and integrates with service registries.
The App Struct
The App struct is defined in app.go:
type App struct {
opts options
ctx context . Context
cancel context . CancelFunc
mu sync . Mutex
instance * registry . ServiceInstance
}
Key Fields
Contains all configuration options including servers, registrar, signals, and lifecycle hooks.
Context for managing application lifecycle and cancellation propagation.
The service instance registered with the service registry.
Mutex for thread-safe access to the instance field.
Creating an Application
Use the New() function to create an application with functional options:
func New ( opts ... Option ) * App {
o := options {
ctx : context . Background (),
sigs : [] os . Signal { syscall . SIGTERM , syscall . SIGQUIT , syscall . SIGINT },
registrarTimeout : 10 * time . Second ,
}
if id , err := uuid . NewUUID (); err == nil {
o . id = id . String ()
}
for _ , opt := range opts {
opt ( & o )
}
if o . logger != nil {
log . SetLogger ( o . logger )
}
ctx , cancel := context . WithCancel ( o . ctx )
return & App {
ctx : ctx ,
cancel : cancel ,
opts : o ,
}
}
Configuration Options
Kratos uses the functional options pattern for configuration:
Basic Options
With Servers
With Registry
With Lifecycle Hooks
app := kratos . New (
kratos . ID ( "service-001" ),
kratos . Name ( "user-service" ),
kratos . Version ( "v1.0.0" ),
kratos . Metadata ( map [ string ] string {
"region" : "us-west" ,
}),
)
Available Options
Here are all the configuration options available:
Option Description Default ID(id)Service instance ID Auto-generated UUID Name(name)Service name Empty string Version(version)Service version Empty string Metadata(md)Service metadata nil Server(srv...)Transport servers Empty slice Registrar(r)Service registrar nil RegistrarTimeout(t)Registry timeout 10 seconds StopTimeout(t)Graceful stop timeout 0 (no timeout) Signal(sigs...)OS signals to handle SIGTERM, SIGQUIT, SIGINT Logger(logger)Logger instance Default logger Context(ctx)Root context Background context
Application Lifecycle
The Run Method
The Run() method orchestrates the entire application lifecycle:
func ( a * App ) Run () error {
instance , err := a . buildInstance ()
if err != nil {
return err
}
a . mu . Lock ()
a . instance = instance
a . mu . Unlock ()
sctx := NewContext ( a . ctx , a )
eg , ctx := errgroup . WithContext ( sctx )
wg := sync . WaitGroup {}
// Execute beforeStart hooks
for _ , fn := range a . opts . beforeStart {
if err = fn ( sctx ); err != nil {
return err
}
}
// Start all servers concurrently
octx := NewContext ( a . opts . ctx , a )
for _ , srv := range a . opts . servers {
server := srv
// Goroutine for graceful stop
eg . Go ( func () error {
<- ctx . Done ()
stopCtx := context . WithoutCancel ( octx )
if a . opts . stopTimeout > 0 {
var cancel context . CancelFunc
stopCtx , cancel = context . WithTimeout ( stopCtx , a . opts . stopTimeout )
defer cancel ()
}
return server . Stop ( stopCtx )
})
// Goroutine for server start
wg . Add ( 1 )
eg . Go ( func () error {
wg . Done ()
return server . Start ( octx )
})
}
wg . Wait ()
// Register with service registry
if a . opts . registrar != nil {
rctx , rcancel := context . WithTimeout ( ctx , a . opts . registrarTimeout )
defer rcancel ()
if err = a . opts . registrar . Register ( rctx , instance ); err != nil {
return err
}
}
// Execute afterStart hooks
for _ , fn := range a . opts . afterStart {
if err = fn ( sctx ); err != nil {
return err
}
}
// Wait for shutdown signal
c := make ( chan os . Signal , 1 )
signal . Notify ( c , a . opts . sigs ... )
eg . Go ( func () error {
select {
case <- ctx . Done ():
return nil
case <- c :
return a . Stop ()
}
})
if err = eg . Wait (); err != nil && ! errors . Is ( err , context . Canceled ) {
return err
}
// Execute afterStop hooks
err = nil
for _ , fn := range a . opts . afterStop {
err = fn ( sctx )
}
return err
}
Lifecycle Stages
Build Instance
Creates a ServiceInstance with endpoints from all servers. func ( a * App ) buildInstance () ( * registry . ServiceInstance , error ) {
endpoints := make ([] string , 0 , len ( a . opts . endpoints ))
for _ , e := range a . opts . endpoints {
endpoints = append ( endpoints , e . String ())
}
if len ( endpoints ) == 0 {
for _ , srv := range a . opts . servers {
if r , ok := srv .( transport . Endpointer ); ok {
e , err := r . Endpoint ()
if err != nil {
return nil , err
}
endpoints = append ( endpoints , e . String ())
}
}
}
return & registry . ServiceInstance {
ID : a . opts . id ,
Name : a . opts . name ,
Version : a . opts . version ,
Metadata : a . opts . metadata ,
Endpoints : endpoints ,
}, nil
}
Execute BeforeStart Hooks
Runs all registered beforeStart hooks sequentially. If any hook returns an error, the application stops.
Start Servers
Launches all transport servers concurrently using errgroup. Each server runs in its own goroutine.
Register Service
Registers the service instance with the configured service registry (if provided).
Execute AfterStart Hooks
Runs all registered afterStart hooks sequentially after successful startup.
Wait for Signal
Blocks until receiving an OS signal (SIGTERM, SIGINT, SIGQUIT by default) or context cancellation.
Graceful Shutdown
Stops all servers with optional timeout and deregisters from service registry.
Execute AfterStop Hooks
Runs all registered afterStop hooks for final cleanup.
The Stop Method
Graceful shutdown is handled by the Stop() method:
func ( a * App ) Stop () ( err error ) {
sctx := NewContext ( a . ctx , a )
// Execute beforeStop hooks
for _ , fn := range a . opts . beforeStop {
err = fn ( sctx )
}
a . mu . Lock ()
instance := a . instance
a . mu . Unlock ()
// Deregister from service registry
if a . opts . registrar != nil && instance != nil {
ctx , cancel := context . WithTimeout ( NewContext ( a . ctx , a ), a . opts . registrarTimeout )
defer cancel ()
if err = a . opts . registrar . Deregister ( ctx , instance ); err != nil {
return err
}
}
// Cancel context to trigger server shutdown
if a . cancel != nil {
a . cancel ()
}
return err
}
The Stop() method triggers context cancellation, which signals all servers to shut down. Each server should implement graceful shutdown in its Stop() method.
Concurrency Model
Kratos uses Go’s concurrency primitives effectively:
Error Groups
Servers run in an errgroup.Group which provides:
Concurrent execution
Automatic error propagation
Context cancellation on first error
eg , ctx := errgroup . WithContext ( sctx )
Wait Groups
Ensures all servers have started before proceeding to registration:
wg . Add ( 1 )
eg . Go ( func () error {
wg . Done () // Signal that server start has begun
return server . Start ( octx )
})
wg . Wait () // Wait for all servers to begin starting
Context Management
The App provides context utilities for accessing application information:
// NewContext returns a new Context that carries app info
func NewContext ( ctx context . Context , s AppInfo ) context . Context {
return context . WithValue ( ctx , appKey {}, s )
}
// FromContext returns the AppInfo value stored in ctx
func FromContext ( ctx context . Context ) ( s AppInfo , ok bool ) {
s , ok = ctx . Value ( appKey {}).( AppInfo )
return
}
AppInfo Interface
type AppInfo interface {
ID () string
Name () string
Version () string
Metadata () map [ string ] string
Endpoint () [] string
}
Complete Example
Complete Application Setup
package main
import (
" context "
" log "
" github.com/go-kratos/kratos/v2 "
" github.com/go-kratos/kratos/v2/transport/http "
" github.com/go-kratos/kratos/v2/transport/grpc "
)
func main () {
httpSrv := http . NewServer (
http . Address ( ":8000" ),
)
grpcSrv := grpc . NewServer (
grpc . Address ( ":9000" ),
)
app := kratos . New (
kratos . Name ( "helloworld" ),
kratos . Version ( "v1.0.0" ),
kratos . Server (
httpSrv ,
grpcSrv ,
),
kratos . BeforeStart ( func ( ctx context . Context ) error {
log . Println ( "Application starting..." )
return nil
}),
kratos . AfterStart ( func ( ctx context . Context ) error {
log . Println ( "Application started successfully" )
return nil
}),
kratos . BeforeStop ( func ( ctx context . Context ) error {
log . Println ( "Application stopping..." )
return nil
}),
kratos . AfterStop ( func ( ctx context . Context ) error {
log . Println ( "Application stopped" )
return nil
}),
)
if err := app . Run (); err != nil {
log . Fatal ( err )
}
}
Best Practices
Always use lifecycle hooks
Use BeforeStart and AfterStop hooks for resource initialization and cleanup instead of doing it in main.
Configure StopTimeout to ensure graceful shutdown completes before forced termination.
Lifecycle hooks can return errors. Ensure proper error handling to prevent startup with incomplete initialization.
Store version, environment, and other relevant information in service metadata for debugging and routing.
Next Steps
Architecture Overview Understand the overall framework architecture
Dependency Injection Learn how to wire components together