Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Serverless Architecture

Overview

Serverless Architecture là mô hình trong đó developers không cần quản lý servers. Cloud provider tự động cung cấp, scale, và quản lý compute resources. Bạn chỉ tập trung vào code và trả tiền cho thời gian compute thực sự được sử dụng.

Key Concepts

1. Function as a Service (FaaS)

// AWS Lambda function
public class FunctionHandler
{
    public async Task<APIGatewayProxyResponse> Handle(
        APIGatewayProxyRequest request, 
        ILambdaContext context)
    {
        context.Logger.LogLine("Processing request");
        
        var response = new
        {
            message = "Hello from serverless!",
            timestamp = DateTime.UtcNow
        };
        
        return new APIGatewayProxyResponse
        {
            StatusCode = 200,
            Body = JsonSerializer.Serialize(response),
            Headers = new Dictionary<string, string>
            {
                { "Content-Type", "application/json" }
            }
        };
    }
}

2. Serverless Compute Platforms

  • AWS Lambda
  • Azure Functions
  • Google Cloud Functions
  • Alibaba Cloud Function Compute

Architecture Pattern

┌─────────────────────────────────────────────────────┐
│                    Client Apps                       │
│   (Web, Mobile, IoT)                                │
└─────────────────────────┬───────────────────────────┘
                          │
                          ▼
┌─────────────────────────────────────────────────────┐
│              API Gateway                             │
│   (Authentication, Rate Limiting, Routing)           │
└─────────────────────────┬───────────────────────────┘
                          │
         ┌────────────────┼────────────────┐
         ▼                ▼                ▼
   ┌──────────┐     ┌──────────┐     ┌──────────┐
   │ Function │     │ Function │     │ Function │
   │  (Auth)  │     │ (Order)  │     │ (Payment)│
   └────┬─────┘     └────┬─────┘     └────┬─────┘
        │                │                │
        └────────────────┼────────────────┘
                         │
         ┌───────────────┼───────────────┐
         ▼               ▼               ▼
   ┌──────────┐   ┌──────────┐   ┌──────────┐
   │   S3     │   │   DynamoDB│   │   SNS    │
   │ (Files)  │   │   (DB)    │   │  (Events)│
   └──────────┘   └──────────┘   └──────────┘

AWS Lambda Example

// Order Processing Function
public class OrderProcessor
{
    private readonly IOrderRepository _orderRepository;
    private readonly IEmailService _emailService;
    
    public OrderProcessor(IOrderRepository orderRepository, IEmailService emailService)
    {
        _orderRepository = orderRepository;
        _emailService = emailService;
    }
    
    [LambdaFunction(Role = "order-processor-role")]
    public async Task HandleOrderCreated(
        [SQSTrigger("order-queue")] OrderMessage message,
        ILambdaContext context)
    {
        context.Logger.LogLine($"Processing order: {message.OrderId}");
        
        var order = await _orderRepository.GetAsync(message.OrderId);
        
        if (order != null)
        {
            await _emailService.SendConfirmationAsync(order);
            context.Logger.LogLine("Order processed successfully");
        }
    }
}

// S3 Event Trigger
public class ImageProcessor
{
    [LambdaFunction(Role = "image-processor-role")]
    public async Task ProcessImage(
        [S3Trigger("my-bucket")] S3EventNotification event,
        ILambdaContext context)
    {
        foreach (var record in event.Records)
        {
            var key = record.S3.Object.Key;
            context.Logger.LogLine($"Processing image: {key}");
            
            // Process image (resize, compress, etc.)
            await ProcessImageAsync(key);
        }
    }
}

Azure Functions Example

// Timer-triggered function
public class ScheduledTask
{
    [FunctionName("DailyCleanup")]
    public async Task Run(
        [TimerTrigger("0 0 2 * * *")] TimerInfo timer,
        ILogger log)
    {
        log.LogInformation($"Function executed at: {DateTime.Now}");
        
        // Cleanup old records
        await CleanupOldRecordsAsync();
    }
}

// HTTP-triggered function with CosmosDB binding
public class ProductFunctions
{
    [FunctionName("GetProducts")]
    public async Task<IActionResult> GetProducts(
        [HttpTrigger(AuthorizationLevel.Function, "get", Route = "products")] 
        HttpRequest req,
        [CosmosDB("db", "products", ConnectionStringSetting = "CosmosDB")] 
        IEnumerable<Product> products,
        ILogger log)
    {
        log.LogInformation("Fetching products from CosmosDB");
        return new OkObjectResult(products);
    }
    
    [FunctionName("CreateProduct")]
    public async Task<IActionResult> CreateProduct(
        [HttpTrigger(AuthorizationLevel.Function, "post", Route = "products")] 
        HttpRequest req,
        [CosmosDB("db", "products", ConnectionStringSetting = "CosmosDB")] 
        out dynamic document,
        ILogger log)
    {
        var requestBody = await new StreamReader(req.Body).ReadToEndAsync();
        document = JsonSerializer.Deserialize<dynamic>(requestBody);
        return new CreatedResult($"/api/products/{document.id}", document);
    }
}

Infrastructure as Code

# serverless.yml (Serverless Framework)
service: my-serverless-app

provider:
  name: aws
  runtime: dotnet6
  memorySize: 256
  timeout: 30
  environment:
    TABLE_NAME: ${self:service}-${opt:stage, 'dev'}

functions:
  hello:
    handler: CsharpHandlers::Hello.Handler
    events:
      - http:
          path: /hello
          method: get
  
  createOrder:
    handler: CsharpHandlers::Orders.Create
    events:
      - http:
          path: /orders
          method: post
      - sqs:
          arn: !GetAtt OrderQueue.Arn

resources:
  Resources:
    OrderQueue:
      Type: AWS::SQS::Queue
      Properties:
        QueueName: ${self:service}-orders-queue

plugins:
  - serverless-dynamodb-plugin
  - serverless-offline

Benefits

BenefitDescription
No Server ManagementNo OS, patching, or capacity planning
Auto-scalingAutomatic scaling from 0 to thousands
Pay per usePay only for compute time used
Faster time to marketFocus on business logic
Reduced operational overheadCloud provider handles infrastructure
Built-in high availabilityMultiple AZ redundancy

Challenges

ChallengeDescription
Cold startsInitial invocation latency
Vendor lock-inPlatform-specific APIs
Testing complexityHard to test locally
StatelessnessMust handle state externally
DebuggingDistributed tracing complexity
SecurityNeed to secure function configurations

Best Practices

1. Stateless Functions

// Don't store state in function
// Use external services instead
public class FunctionHandler
{
    public async Task<Response> Handle(Request request)
    {
        // Get state from cache/database
        var cache = await _cacheService.GetAsync(request.UserId);
        
        // Process
        var result = await _service.ProcessAsync(cache);
        
        return result;
    }
}

2. Proper Configuration

public class FunctionHandler
{
    public async Task<Response> Handle(Request request)
    {
        var maxRetries = Environment.GetEnvironmentVariable("MAX_RETRIES")
            ?? "3";
        
        var connectionString = Environment.GetEnvironmentVariable("DB_CONNECTION");
        
        // Use configuration safely
    }
}

3. Idempotency

public async Task Handle(OrderCreatedEvent evt, ILambdaContext context)
{
    // Check if already processed
    var isProcessed = await _redis.SetIfNotExistsAsync(
        $"order:{evt.OrderId}:processed",
        TimeSpan.FromDays(1));
    
    if (!isProcessed)
    {
        context.Logger.LogLine("Order already processed, skipping");
        return;
    }
    
    // Process order
    await ProcessOrderAsync(evt);
}

4. Error Handling

public async Task Handle(Request request, ILambdaContext context)
{
    try
    {
        await ProcessAsync(request);
    }
    catch (Exception ex)
    {
        context.Logger.LogError(ex, "Error processing request");
        
        // Send to dead letter queue
        await _dlq.SendAsync(new DeadLetterMessage
        {
            Request = request,
            Error = ex.Message,
            Timestamp = DateTime.UtcNow
        });
        
        throw; // Re-throw for retry
    }
}

Use Cases

Use CaseDescription
Web backendsAPIs và web applications
Data processingETL, batch processing
Real-time file processingImage resizing, video transcoding
IoT backendsIoT data ingestion
ChatbotsNLP và message handling
Scheduled tasksCron jobs, cleanup tasks
Event-driven appsEvent handling, notifications

Comparison

AspectServerlessTraditional
Server managementProviderSelf
ScalingAutomaticManual/auto
PricingPer executionAlways on
DeploymentFunctionsContainers/servers
LatencyCold startsAlways ready
ComplexityDistributedMonolithic

References