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:
// 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:
// 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:
// 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.
Bu Serideki Tüm Yazılar
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!
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!