AWS CDK Link Kısaltıcı Bölüm 2: Temel Fonksiyonlar & API Geliştirme

Yönlendirme motoru, analytics toplama ve API Gateway konfigürasyonu. Günlük milyonlarca yönlendirmeyi işlemenin gerçek performans optimizasyonları ve debugging stratejileri.

AWS CDK Link Kısaltıcı Bölüm 2: Temel Fonksiyonlar & API Geliştirme#

İşte oradaydık, yatırımcılara yaptığımız board sunumunun tam ortasında, yeni pazarlama kampanyası tracking sistemimizi gösteriyorduk. Kısaltılmış linklerden birine tıkladım... ve bekledim. Ve bekledim. Redirect 3 saniye sürdü. Oda sessizleşti. CEO bana o bakışı attı.

İşte o zaman öğrendim ki link kısaltıcı kurmak sadece kısa kodlar generate etmek değil—ölçekte zarif bir şekilde çalışabilen bir redirect motoru kurmaktır. O biraz garip sunumdan sonra (yine de fonlamayı aldık), redirect sistemimizi proper caching, analytics ve error handling ile yeniden kurduk.

Bölüm 1'de temeli kurduk. Şimdi bu şeyi uçuran gerçek business mantığını kuralım.

Redirect Motoru: Hızın Önemli Olduğu Yer#

Redirect handler link kısaltıcının kalbidir. Her milisaniye önemlidir çünkü kullanıcılar anında redirect bekler. İşte production'da test edilmiş implementasyonumuz:

TypeScript
// lambda/redirect.ts
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
import { DynamoDBClient, GetItemCommand } from '@aws-sdk/client-dynamodb';
import { unmarshall } from '@aws-sdk/util-dynamodb';

const dynamodb = new DynamoDBClient({
  region: process.env.AWS_REGION,
  // Daha iyi performans için connection pooling
  maxAttempts: 3,
  requestHandler: {
    connectionTimeout: 1000,
    requestTimeout: 2000,
  }
});

interface AnalyticsEvent {
  shortCode: string;
  timestamp: number;
  userAgent?: string;
  referer?: string;
  ip?: string;
  country?: string;
}

export const handler = async (
  event: APIGatewayProxyEvent
): Promise<APIGatewayProxyResult> => {
  const startTime = Date.now();
  const shortCode = event.pathParameters?.shortCode;
  
  if (!shortCode) {
    return createErrorResponse(400, 'Short code gerekli');
  }

  try {
    // DynamoDB'den URL'i al
    const result = await dynamodb.send(new GetItemCommand({
      TableName: process.env.LINKS_TABLE_NAME!,
      Key: { shortCode: { S: shortCode } },
      ProjectionExpression: 'originalUrl, expiresAt, clickCount',
    }));

    if (!result.Item) {
      // Analytics için 404'leri takip et
      await trackAnalytics({
        shortCode,
        timestamp: Date.now(),
        userAgent: event.headers['User-Agent'],
        referer: event.headers['Referer'],
        ip: event.requestContext.identity?.sourceIp,
      }, 'NOT_FOUND');
      
      return createErrorResponse(404, 'Link bulunamadı');
    }

    const item = unmarshall(result.Item);
    
    // Expiration kontrolü
    if (item.expiresAt && Date.now() > item.expiresAt) {
      return createErrorResponse(410, 'Link süresi dolmuş');
    }

    // Analytics'i asenkron olarak takip et (redirect'i bloklaması)
    trackAnalytics({
      shortCode,
      timestamp: Date.now(),
      userAgent: event.headers['User-Agent'],
      referer: event.headers['Referer'],
      ip: event.requestContext.identity?.sourceIp,
    }, 'SUCCESS').catch(error => {
      console.error('Analytics tracking başarısız:', error);
      // Analytics başarısız olursa redirect'i başarısız etme
    });

    // Performans metriklerini logla
    const responseTime = Date.now() - startTime;
    console.log(`Redirect ${responseTime}ms'de işlendi: ${shortCode}`);

    return {
      statusCode: 301,
      headers: {
        Location: item.originalUrl,
        'Cache-Control': 'public, max-age=300', // 5 dakika
        'X-Response-Time': `${responseTime}ms`,
      },
      body: '',
    };

  } catch (error) {
    console.error('Redirect hatası:', error);
    
    return createErrorResponse(500, 'Internal server hatası');
  }
};

Analytics: Business Intelligence Katmanı#

Analytics link kısaltıcımızı sadece kolaylıktan öte değerli kıldı. İşte tık verilerini nasıl topluyoruz ve saklıyoruz:

TypeScript
// lambda/analytics.ts
import { DynamoDBClient, PutItemCommand, UpdateItemCommand } from '@aws-sdk/client-dynamodb';
import { marshall } from '@aws-sdk/util-dynamodb';

const dynamodb = new DynamoDBClient({ region: process.env.AWS_REGION });

async function trackAnalytics(
  event: AnalyticsEvent, 
  eventType: 'SUCCESS' | 'NOT_FOUND' = 'SUCCESS'
): Promise<void> {
  const timestamp = Date.now();
  const analyticsItem = {
    shortCode: event.shortCode,
    timestamp,
    eventType,
    userAgent: event.userAgent || 'unknown',
    referer: event.referer || 'direct',
    ip: hashIP(event.ip || ''), // Privacy-first yaklaşım
    country: await getCountryFromIP(event.ip),
    // Efficient query'ler için saatlik partition
    hourPartition: `${event.shortCode}#${Math.floor(timestamp / (1000 * 60 * 60))}`,
  };

  // Analytics tablosunda sakla
  await dynamodb.send(new PutItemCommand({
    TableName: process.env.ANALYTICS_TABLE_NAME!,
    Item: marshall(analyticsItem),
  }));

  // Ana kayıttaki tık sayısını güncelle (sadece başarılı tıklar için)
  if (eventType === 'SUCCESS') {
    await dynamodb.send(new UpdateItemCommand({
      TableName: process.env.LINKS_TABLE_NAME!,
      Key: { shortCode: { S: event.shortCode } },
      UpdateExpression: 'ADD clickCount :inc SET lastClickAt = :timestamp',
      ExpressionAttributeValues: {
        ':inc': { N: '1' },
        ':timestamp': { N: timestamp.toString() },
      },
    }));
  }
}

API Gateway: Ön Kapı#

İşte milyonlarca isteği kırmadan handle eden CDK konfigürasyonumuz:

TypeScript
// lib/api-stack.ts
import * as cdk from 'aws-cdk-lib';
import * as apigateway from 'aws-cdk-lib/aws-apigateway';

export class ApiStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props: ApiStackProps) {
    super(scope, id, props);

    // Custom domain ile API Gateway
    const api = new apigateway.RestApi(this, 'LinkShortenerApi', {
      restApiName: 'Link Shortener Servisi',
      description: 'Production link kısaltıcı API',
      
      // Performans optimizasyonları
      minimumCompressionSize: 1024,
      binaryMediaTypes: ['*/*'],
      
      // CORS konfigürasyonu
      defaultCorsPreflightOptions: {
        allowOrigins: apigateway.Cors.ALL_ORIGINS,
        allowMethods: ['GET', 'POST', 'OPTIONS'],
        allowHeaders: [
          'Content-Type',
          'X-Amz-Date',
          'Authorization',
          'X-Api-Key',
          'X-Amz-Security-Token',
        ],
        maxAge: cdk.Duration.hours(1),
      },

      // Request validation
      requestValidator: new apigateway.RequestValidator(this, 'RequestValidator', {
        restApi: api,
        validateRequestBody: true,
        validateRequestParameters: true,
      }),
    });

    // Redirect route ekle: GET /{shortCode}
    const redirectIntegration = new apigateway.LambdaIntegration(props.redirectHandler, {
      proxy: true,
      allowTestInvoke: false, // Performans için test invoke'u devre dışı bırak
    });

    api.root.addResource('{shortCode}').addMethod('GET', redirectIntegration, {
      requestParameters: {
        'method.request.path.shortCode': true,
      },
    });
  }
}

Production'dan Performans Dersleri#

50M+ redirect handle ettikten sonra, gerçekten önemli olan performans pattern'leri:

1. Connection Pooling İstek Başına 50ms Kurtarır#

Yukarıdaki DynamoDB client konfigürasyonu connection pooling içerir. Onsuz, her Lambda cold start yeni connection'lar yaratır, 50-100ms latency ekler.

2. Asenkron Analytics Kullanıcıları Bloklamaz#

Başlangıçta analytics'i senkron olarak takip ediyorduk. Kötü fikir. Kullanıcılar analytics başarısız olursa umursamazlar, ama redirect'ler yavaşsa kesinlikle umursarlar.

3. DynamoDB Projection'ları Önemli#

GetItem çağrılarımızda ProjectionExpression kullanmak response boyutlarını %60 azalttı.

Bölüm 3'te, servisini spam vektörü olmaktan koruyan güvenlik özelliklerini ekleyeceğiz.

AWS CDK Link Kısaltıcı: Sıfırdan Production'a

AWS CDK, Node.js Lambda ve DynamoDB ile production-grade bir link kısaltma servisi kurulumu hakkında 5 bölümlük kapsamlı seri. Gerçek production hikayeleri, performans optimizasyonu ve maliyet yönetimi dahil.

İlerleme2/5 yazı tamamlandı
Loading...

Yorumlar (0)

Sohbete katıl

Düşüncelerini paylaşmak ve toplulukla etkileşim kurmak için giriş yap

Henüz yorum yok

Bu yazı hakkında ilk düşüncelerini paylaşan sen ol!

Related Posts