Server Framework
This guide covers the core server framework in Swit, which provides a comprehensive, production-ready foundation for microservice development with unified lifecycle management, transport coordination, and service registration.
Overview
The Swit server framework (pkg/server
) implements a complete server architecture that handles:
- Transport Management - Unified HTTP and gRPC transport coordination
- Service Registration - Interface-driven service registration patterns
- Dependency Injection - Factory-based dependency management
- Performance Monitoring - Built-in metrics and profiling
- Lifecycle Management - Phased startup and shutdown coordination
Core Architecture
BusinessServerCore Interface
The main server interface provides consistent lifecycle management:
type BusinessServerCore interface {
Start(ctx context.Context) error
Stop(ctx context.Context) error
Shutdown() error
GetHTTPAddress() string
GetGRPCAddress() string
GetTransports() []transport.NetworkTransport
GetTransportStatus() map[string]TransportStatus
GetTransportHealth(ctx context.Context) map[string]map[string]*types.HealthStatus
}
BusinessServerImpl - The Core Implementation
The main server implementation orchestrates all framework components:
// Create a new server instance
config := &server.ServerConfig{
ServiceName: "my-service",
HTTP: server.HTTPConfig{
Port: "8080",
Enabled: true,
},
GRPC: server.GRPCConfig{
Port: "9080",
Enabled: true,
},
}
srv, err := server.NewBusinessServerCore(config, serviceRegistrar, dependencies)
if err != nil {
return fmt.Errorf("failed to create server: %w", err)
}
Service Registration System
BusinessServiceRegistrar Interface
Services implement this interface to register with the server:
type BusinessServiceRegistrar interface {
RegisterServices(registry BusinessServiceRegistry) error
}
Implementation Example
type MyServiceRegistrar struct {
userService *UserService
authService *AuthService
}
func (r *MyServiceRegistrar) RegisterServices(registry server.BusinessServiceRegistry) error {
// Register HTTP handlers
if err := registry.RegisterBusinessHTTPHandler(r.userService); err != nil {
return fmt.Errorf("failed to register user HTTP handler: %w", err)
}
// Register gRPC services
if err := registry.RegisterBusinessGRPCService(r.authService); err != nil {
return fmt.Errorf("failed to register auth gRPC service: %w", err)
}
// Register health checks
healthCheck := &ServiceHealthCheck{
userService: r.userService,
authService: r.authService,
}
if err := registry.RegisterBusinessHealthCheck(healthCheck); err != nil {
return fmt.Errorf("failed to register health check: %w", err)
}
return nil
}
BusinessServiceRegistry Interface
Provides registration methods for different service types:
type BusinessServiceRegistry interface {
RegisterBusinessHTTPHandler(handler BusinessHTTPHandler) error
RegisterBusinessGRPCService(service BusinessGRPCService) error
RegisterBusinessHealthCheck(check BusinessHealthCheck) error
}
HTTP Service Implementation
BusinessHTTPHandler Interface
type BusinessHTTPHandler interface {
RegisterRoutes(router interface{}) error
GetServiceName() string
}
Implementation Example
type UserService struct {
repository *UserRepository
logger *zap.Logger
}
func (s *UserService) RegisterRoutes(router interface{}) error {
ginRouter := router.(*gin.Engine)
v1 := ginRouter.Group("/api/v1")
{
users := v1.Group("/users")
{
users.GET("", s.GetUsers)
users.POST("", s.CreateUser)
users.GET("/:id", s.GetUser)
users.PUT("/:id", s.UpdateUser)
users.DELETE("/:id", s.DeleteUser)
}
}
return nil
}
func (s *UserService) GetServiceName() string {
return "user-service"
}
func (s *UserService) GetUsers(c *gin.Context) {
users, err := s.repository.GetAll()
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{"users": users})
}
gRPC Service Implementation
BusinessGRPCService Interface
type BusinessGRPCService interface {
RegisterGRPC(server interface{}) error
GetServiceName() string
}
Implementation Example
type AuthService struct {
authpb.UnimplementedAuthServiceServer
tokenManager *TokenManager
userRepo *UserRepository
}
func (s *AuthService) RegisterGRPC(server interface{}) error {
grpcServer := server.(*grpc.Server)
authpb.RegisterAuthServiceServer(grpcServer, s)
return nil
}
func (s *AuthService) GetServiceName() string {
return "auth-service"
}
func (s *AuthService) Login(ctx context.Context, req *authpb.LoginRequest) (*authpb.LoginResponse, error) {
// Validate user credentials
user, err := s.userRepo.ValidateCredentials(req.Username, req.Password)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "invalid credentials")
}
// Generate token
token, err := s.tokenManager.GenerateToken(user.ID)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to generate token")
}
return &authpb.LoginResponse{
Token: token,
ExpiresAt: time.Now().Add(24 * time.Hour).Unix(),
User: &authpb.User{
Id: user.ID,
Username: user.Username,
Email: user.Email,
},
}, nil
}
Health Check Implementation
BusinessHealthCheck Interface
type BusinessHealthCheck interface {
Check(ctx context.Context) error
GetServiceName() string
}
Implementation Example
type ServiceHealthCheck struct {
database *sql.DB
redis *redis.Client
services []HealthChecker
}
func (h *ServiceHealthCheck) Check(ctx context.Context) error {
// Check database connection
if err := h.database.PingContext(ctx); err != nil {
return fmt.Errorf("database health check failed: %w", err)
}
// Check Redis connection
if err := h.redis.Ping(ctx).Err(); err != nil {
return fmt.Errorf("redis health check failed: %w", err)
}
// Check dependent services
for _, service := range h.services {
if err := service.HealthCheck(ctx); err != nil {
return fmt.Errorf("service %s health check failed: %w", service.GetName(), err)
}
}
return nil
}
func (h *ServiceHealthCheck) GetServiceName() string {
return "service-health-check"
}
Lifecycle Management
Server Startup Process
The server follows a phased startup process:
- Configuration Validation - Validate all configuration parameters
- Dependency Initialization - Initialize dependency container
- Transport Initialization - Start HTTP and gRPC transports
- Service Registration - Register all services with transports
- Discovery Registration - Register with service discovery
- Performance Monitoring - Start performance monitoring
func (s *BusinessServerImpl) Start(ctx context.Context) error {
// Phase 1: Initialize dependencies
if err := s.initializeDependencies(ctx); err != nil {
return fmt.Errorf("failed to initialize dependencies: %w", err)
}
// Phase 2: Initialize transports
if err := s.initializeTransports(ctx); err != nil {
return fmt.Errorf("failed to initialize transports: %w", err)
}
// Phase 3: Register services
if err := s.registerServices(); err != nil {
return fmt.Errorf("failed to register services: %w", err)
}
// Phase 4: Start discovery registration
if err := s.registerWithDiscovery(ctx); err != nil {
return fmt.Errorf("failed to register with discovery: %w", err)
}
// Phase 5: Start performance monitoring
s.startPerformanceMonitoring(ctx)
return nil
}
Graceful Shutdown
The server supports graceful shutdown with proper resource cleanup:
func (s *BusinessServerImpl) Shutdown() error {
ctx, cancel := context.WithTimeout(context.Background(), s.config.ShutdownTimeout)
defer cancel()
// Deregister from service discovery
if err := s.deregisterFromDiscovery(ctx); err != nil {
s.logger.Error("Failed to deregister from discovery", zap.Error(err))
}
// Stop transports gracefully
if err := s.transportCoordinator.Stop(s.config.ShutdownTimeout); err != nil {
s.logger.Error("Transport shutdown errors", zap.Error(err))
}
// Close dependencies
if s.dependencies != nil {
if err := s.dependencies.Close(); err != nil {
s.logger.Error("Failed to close dependencies", zap.Error(err))
}
}
return nil
}
Performance Monitoring
Built-in Performance Metrics
The server framework includes comprehensive performance monitoring:
type PerformanceMetrics struct {
StartupTime time.Duration // Server startup time
ShutdownTime time.Duration // Server shutdown time
Uptime time.Duration // Current uptime
MemoryUsage uint64 // Current memory usage
GoroutineCount int // Active goroutines
ServiceCount int // Registered services
TransportCount int // Active transports
RequestCount int64 // Request counter
ErrorCount int64 // Error counter
}
Performance Hooks
Add custom performance monitoring:
func CustomPerformanceHook(event string, metrics *server.PerformanceMetrics) {
// Send metrics to external monitoring system
switch event {
case "startup_complete":
prometheus.SetGauge("server_startup_time_seconds", metrics.StartupTime.Seconds())
case "request_processed":
prometheus.IncrementCounter("server_requests_total")
case "memory_threshold_exceeded":
alert.Send("High memory usage detected", metrics.MemoryUsage)
}
}
// Register the hook
srv := server.NewBusinessServerCore(config, registrar, deps)
monitor := srv.GetPerformanceMonitor()
monitor.AddHook(CustomPerformanceHook)
Advanced Usage Patterns
Multi-Service Server
type MultiServiceRegistrar struct {
userService *UserService
authService *AuthService
paymentService *PaymentService
orderService *OrderService
}
func (r *MultiServiceRegistrar) RegisterServices(registry server.BusinessServiceRegistry) error {
// Register all HTTP handlers
httpHandlers := []server.BusinessHTTPHandler{
r.userService,
r.authService,
r.paymentService,
r.orderService,
}
for _, handler := range httpHandlers {
if err := registry.RegisterBusinessHTTPHandler(handler); err != nil {
return fmt.Errorf("failed to register HTTP handler %s: %w",
handler.GetServiceName(), err)
}
}
// Register gRPC services
grpcServices := []server.BusinessGRPCService{
r.authService,
r.paymentService,
}
for _, service := range grpcServices {
if err := registry.RegisterBusinessGRPCService(service); err != nil {
return fmt.Errorf("failed to register gRPC service %s: %w",
service.GetServiceName(), err)
}
}
// Register composite health check
healthCheck := &CompositeHealthCheck{
checks: []server.BusinessHealthCheck{
&DatabaseHealthCheck{},
&RedisHealthCheck{},
&ExternalServiceHealthCheck{},
},
}
return registry.RegisterBusinessHealthCheck(healthCheck)
}
Custom Server Extensions
type EnhancedBusinessServer struct {
*server.BusinessServerImpl
metricsCollector *MetricsCollector
auditLogger *AuditLogger
}
func NewEnhancedBusinessServer(config *server.ServerConfig, registrar server.BusinessServiceRegistrar, deps server.BusinessDependencyContainer) (*EnhancedBusinessServer, error) {
baseServer, err := server.NewBusinessServerCore(config, registrar, deps)
if err != nil {
return nil, err
}
enhanced := &EnhancedBusinessServer{
BusinessServerImpl: baseServer.(*server.BusinessServerImpl),
metricsCollector: NewMetricsCollector(),
auditLogger: NewAuditLogger(),
}
// Add custom monitoring
monitor := enhanced.GetPerformanceMonitor()
monitor.AddHook(enhanced.metricsCollector.CollectMetrics)
monitor.AddHook(enhanced.auditLogger.LogAuditEvent)
return enhanced, nil
}
func (s *EnhancedBusinessServer) Start(ctx context.Context) error {
// Start metrics collection
s.metricsCollector.Start(ctx)
// Start audit logging
s.auditLogger.Start(ctx)
// Start base server
return s.BusinessServerImpl.Start(ctx)
}
Testing the Server Framework
Unit Testing Services
func TestUserServiceRegistration(t *testing.T) {
// Create mock registry
registry := &MockBusinessServiceRegistry{}
// Create service
userService := &UserService{
repository: &MockUserRepository{},
logger: zap.NewNop(),
}
registrar := &MyServiceRegistrar{
userService: userService,
}
// Test registration
err := registrar.RegisterServices(registry)
assert.NoError(t, err)
// Verify registration calls
assert.True(t, registry.HTTPHandlerRegistered)
assert.Equal(t, "user-service", registry.RegisteredServiceName)
}
Integration Testing
func TestServerIntegration(t *testing.T) {
// Create test configuration
config := server.NewServerConfig()
config.HTTP.Port = "0" // Dynamic port
config.GRPC.Port = "0" // Dynamic port
config.Discovery.Enabled = false
config.HTTP.TestMode = true
config.GRPC.TestMode = true
// Create test services
userService := NewTestUserService()
authService := NewTestAuthService()
registrar := &TestServiceRegistrar{
userService: userService,
authService: authService,
}
// Create and start server
srv, err := server.NewBusinessServerCore(config, registrar, nil)
require.NoError(t, err)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
err = srv.Start(ctx)
require.NoError(t, err)
defer srv.Shutdown()
// Test HTTP endpoints
httpAddr := srv.GetHTTPAddress()
resp, err := http.Get(fmt.Sprintf("http://localhost%s/api/v1/users", httpAddr))
require.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
// Test gRPC services
grpcAddr := srv.GetGRPCAddress()
conn, err := grpc.Dial(grpcAddr, grpc.WithInsecure())
require.NoError(t, err)
defer conn.Close()
client := authpb.NewAuthServiceClient(conn)
loginResp, err := client.Login(ctx, &authpb.LoginRequest{
Username: "testuser",
Password: "testpass",
})
require.NoError(t, err)
assert.NotEmpty(t, loginResp.Token)
}
Best Practices
Service Design
- Single Responsibility - Each service should have a clear, single responsibility
- Interface Segregation - Use specific interfaces rather than large, monolithic ones
- Dependency Injection - Use dependency injection for testability
- Error Handling - Implement comprehensive error handling with context
- Logging - Use structured logging throughout services
Performance Optimization
- Resource Management - Properly manage database connections, file handles, etc.
- Context Propagation - Always propagate context for cancellation and timeouts
- Metrics Collection - Monitor key performance indicators
- Memory Management - Avoid memory leaks with proper resource cleanup
- Concurrency - Use goroutines and channels appropriately
Production Readiness
- Health Checks - Implement meaningful health checks for all dependencies
- Graceful Shutdown - Handle shutdown signals properly
- Configuration Validation - Validate configuration at startup
- Error Recovery - Implement proper error recovery mechanisms
- Monitoring Integration - Integrate with monitoring and alerting systems
Common Patterns
Service Factory Pattern
type ServiceFactory struct {
config *Config
db *sql.DB
redis *redis.Client
}
func (f *ServiceFactory) CreateUserService() *UserService {
return &UserService{
repository: &UserRepository{DB: f.db},
cache: &UserCache{Redis: f.redis},
logger: logger.Named("user-service"),
}
}
func (f *ServiceFactory) CreateAuthService() *AuthService {
return &AuthService{
userRepo: &UserRepository{DB: f.db},
tokenManager: &JWTTokenManager{Secret: f.config.JWTSecret},
logger: logger.Named("auth-service"),
}
}
Middleware Integration
type AuthenticatedHTTPHandler struct {
handler server.BusinessHTTPHandler
authService *AuthService
}
func (h *AuthenticatedHTTPHandler) RegisterRoutes(router interface{}) error {
ginRouter := router.(*gin.Engine)
// Add authentication middleware
ginRouter.Use(h.authMiddleware())
// Register actual handler routes
return h.handler.RegisterRoutes(router)
}
func (h *AuthenticatedHTTPHandler) authMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
token := c.GetHeader("Authorization")
if token == "" {
c.AbortWithStatusJSON(401, gin.H{"error": "missing authorization"})
return
}
userID, err := h.authService.ValidateToken(token)
if err != nil {
c.AbortWithStatusJSON(401, gin.H{"error": "invalid token"})
return
}
c.Set("userID", userID)
c.Next()
}
}
This server framework guide provides comprehensive coverage of the core server functionality, service registration patterns, lifecycle management, and best practices for building robust microservices with the Swit framework.