Mikroservis mimarisi, büyük ve monolitik uygulamaları tek parça halinde geliştirmek yerine, sistemi parçalara ayrıştırmayı hedefler. Her servis kendi veritabanına, yaşam döngüsüne ve dağıtım sürecine sahip olacak şekilde tasarlanır.
Bu noktada sistem sadece büyük değil aynı zamanda dinamik hale gelir. Trafik arttığında auto-scaling mekanizmaları devreye girer. Bazı servisler zamanla bölünür. Sonuç olarak backend tarafında sürekli değişen ve büyüyen bir servis topolojisi oluşur.
API Gateway Pattern
Eğer mobil veya web istemcileri bu mikroservislerin her biriyle doğrudan iletişim kurmaya çalışsaydı “client” tarafında ciddi problemler ortaya çıkardı. İstemcinin hangi servisin nerede olduğunu bilmesi, servis adresleri değiştiğinde güncellenmesi, farklı servislerin farklı kimlik doğrulama veya veri formatları kullanması gibi konular yönetilemez hale gelirdi.
API Gateway ise bize client ile microservice arasına konumlanan tek bir erişim noktası sunar. İstemci artık sadece kullanılacak “gateway” adresini bilir. Arka tarafta kaç servis olduğu, bu servislerin nerede çalıştığı veya nasıl ölçeklendiği tamamen gizlenir. Bu anlamda API Gateway, backend karmaşıklığını istemciden saklayan bir Facade pattern gibi davranır.

Görsel: microsoft.com
Teknik olarak API Gateway de bir microservice ve container içinde çalışır ve ölçeklenebilir. Ancak klasik servislerden farkı business logic barındırmamasıdır. Gateway’in odak noktası routing, güvenlik, protokol dönüşümü ve trafik yönetimi gibi altyapısal (cross-cutting concerns) konulardır.
API Gateway ile ilgili en sık yapılan mimari hata, onu merkezi bir bileşene dönüştürmektir. Buna “Overambitious API gateway problemi” denilir. Gateway içine ek geliştirmeler, hesaplamalar veya domain yapınıza özgü karar mekanizmaları eklendiğinde bu servis hızla monolitik bir yapıya evrilir. Bu durum deploy sürelerini uzatır, test edilebilirliği düşürür ve sistemin tamamı için tek bir failure point yaratır.
API Gateway üzerindeki en önemli görevlerinden biri, kimlik doğrulama (authentication) ve yetkilendirme (authorization) süreçlerini merkezi olarak ele almaktır. Bu yönetim gateway üzerindedir. Bu yaklaşım sayesinde yetkisiz veya hatalı istekler microservice metotlarına ulaşmadan gateway seviyesinde engellenir.
Bu yapının en güçlü yanlarından birisi protocol translation olmasıdır. Yani gateway bir REST isteğini gRPC çağrısına, event tabanlı bir mesaj kuyruğuna (Kafka, RabbitMQ gibi) veya internal bir RPC mekanizmasına dönüştürebilir. Bu da performansınızı arttırır.
Ocelot ile API Gateway
Ocelot .NET sisteminde API Gateway ihtiyacını karşılamak için ortaya çıkmış açık kaynaklı ve lightweight bir kütüphanedir. Microservice mimarilerinde istemci ile backend servisleri arasına merkezi bir katman koymak isterken, sıfırdan bir gateway yazmak ciddi bir operasyonel ve bakım maliyeti doğurabilir. Ocelot bu noktada, routing, authentication entegrasyonu, rate limiting, caching, load balancing ve request/response transformation gibi API arayüzü için olabilecek temel ihtiyaçlarını hazır olarak sunar. ASP.NET Core üzerine inşa edilmiş olması, mevcut .NET projeleriyle doğal uyum sağlar ve öğrenme eğrisini ciddi ölçüde düşürür.
Ocelot’un tercih edilme nedenlerinden biri “overengineering” tuzağına düşmeden işini yapmasıdır. Kubernetes, Service Mesh veya diğer gateway çözümlerine kıyasla, küçük ve orta ölçekli microservice mimarilerinde hızlıca devreye alınabilir. Yapılandırması JSON tabanlıdır. Bu dosya üzerinden backend servis adresleri, upstream–downstream eşleşmeleri ve güvenlik kuralları kod yazmadan yönetilebilir.

Görsel: microsoft.com
Projenize kurmak için:
Install-Package OcelotSonraki adımda, Ocelot gateway yapılandırmamızı tutacağımız ocelot.json dosyasını projemize ekliyoruz. Bu dosya yönlendirme kurallarını, hedef servis adreslerini ve güvenlik politikalarını tanımlayacağımız temel ayar dosyasıdır.
Modern .NET 8+ standartlarında anlaşılır bir Minimal API tarzı Ocelot kurulumu yapalım. Bu örnekte bir e-ticaret sistemindeki “Ürünler” ve “Siparişler” servislerini tek bir gateway üzerinden yönettiğimizi varsayalım.
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
var builder = WebApplication.CreateBuilder(args);
// JWT ekliyoruz
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer("GatewayKey", options => (GatewayKey)
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "Issuer",
ValidAudience = "Audience",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("SecretKey!"))
};
});
// ocelot.json dosyasını okuyoruz
builder.Configuration.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true);
// Ek paketlere boğulmadan en yalın haliyle ekliyoruz
builder.Services.AddOcelot(builder.Configuration);
var app = builder.Build();
// Gelen tüm istekleri Ocelot'a veriyoruz
await app.UseOcelot();
app.Run();ocelot.json içeriği böyle olabilir:
{
"Routes": [
{
"UpstreamPathTemplate": "/products/{everything}",
"UpstreamHttpMethod": [ "Get" ],
"DownstreamPathTemplate": "/api/products/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "product-service-api",
"Port": 5001
}
],
"AuthenticationOptions": {
"AuthenticationProviderKey": "GatewayKey",
"AllowedScopes": []
}
}
],
"GlobalConfiguration": {
"BaseUrl": "http://localhost:8099"
}
}Bu dosyaya göre neler oluyor? Client uygulamasından GET http://localhost:8099/products/161 adresine bir istek gönderilir. Ocelot isteği yakalar ve listedeki rotalarla karşılaştırır. {everything} kısmının 161 olduğunu anlar ve bunu alt taraftaki şablona yerleştirir. Sonuç olarak gateway kendi içinde GET http://product-service-api:5001/api/products/161 adresine bir istek atar, cevabı alır ve istemciye geri döner. Yani kullanıcı arka tarafta 5001 portunda çalışan bir “product-service-api” olduğunu asla bilmez.
Service Discovery
Teknik olarak service discovery kullanmadan da, gelen istekleri backend servislerine sabit URL’ler üzerinden yönlendirmek mümkündür ancak bu doğru bir yaklaşım olmaz. Çünkü her servis yeniden deploy edildiğinde adresleri değişebilir ve bu değişikliklerin Ocelot konfigürasyonu ile sürekli senkron tutulması gerekir. Bu durum karmaşıklığı artırır. Ocelot, Eureka, Consul ve Kubernetes tabanlı service discovery mekanizmalarını da destekler.
Consul kullanımını daha çok tercih ettiğim için basit bir örneğini göstereceğim.
dotnet add package Ocelot
dotnet add package Ocelot.Provider.ConsulProgram.cs:
builder.Services
.AddOcelot()
.AddConsul();
var app = builder.Build();
await app.UseOcelot();AddConsul() service discovery provider aktif eder. Ocelot artık backend adresleri Consul’dan alabilir. Ocelot.json dosyasında:
"GlobalConfiguration": {
"ServiceDiscoveryProvider": {
"Host": "localhost",
"Port": 8500,
"Type": "Consul"
}
}Daha fazla service discovery bilgisi: https://ocelot.readthedocs.io/en/latest/features/servicediscovery.html
Caching ve Rate Limiting
Ocelot, API Gateway seviyesinde hazır gelen basit bir caching mekanizması sunar. Varsayılan olarak bu özellik kapalıdır. Aktif hale getirmek için Ocelot.Cache.CacheManager paketinin projeye eklenmesi ve yapılandırılması gerekir.
.AddOcelot()
.AddCacheManager(x => { x.WithDictionaryHandle(); })Bu yaklaşımda cache, gateway instance belleğinde tutulur ve özellikle sık çağrılan, nadiren değişen GET istekleri için backend servislerin üzerindeki yükü ciddi şekilde azaltır.
Ocelot, rate limiting özelliğini destekler. Bu özellik backend servislerin aşırı yüklenmesini engellemek için kullanılır. Bir route için rate limiting aktif hale getirmek adına aşağıdaki JSON konfigürasyonu eklemek yeterlidir:
"RateLimitOptions": {
"ClientWhitelist": [],
"EnableRateLimiting": true,
"Period": "1s",
"PeriodTimespan": 1,
"Limit": 1
}Ocelot’un daha farklı birçok özelliği mevcut. İhtiyacınıza göre dokümantasyona gözatabilirsiniz.
Load balancing: https://ocelot.readthedocs.io/en/latest/features/loadbalancer.html
Transformation: https://ocelot.readthedocs.io/en/latest/features/headerstransformation.html
Sonuç
.NET tarafında YARP çıkana kadar Ocelot rakipsizdi. Bugün bile Ocelot microservice ekosistemine sunduğu hazır middleware çözümleriyle oldukça popüler.
YARP daha düşük seviyeli ve esnek bir yaklaşıma sahiptir. Yüksek performanslı bir reverse proxy çekirdeği sunar ancak Ocelot’un sunduğu pek çok özelliği (rate limiting, caching, authentication, service discovery gibi) doğrudan hazır halde vermez. Bunun yerine bu yetenekleri geliştiricinin kendisinin eklemesini bekler. Bu da YARP’ı özel ihtiyaçları olan, gateway davranışını tamamen kontrol etmek isteyen kimseler için daha uygun hale getirir.
Hızlı kurulum, standart gateway özellikleri ve düşük bakım önceliğiniz ise Ocelot daha doğru bir tercihtir. Buna karşılık maksimum performans ve esnek bir gateway hedefleniyorsa YARP öne çıkar. Pratikte birçok ekip orta ölçekli sistemlerde Ocelot ile başlayıp ihtiyaçlar karmaşıklaştıkça YARP’a geçiş yapmayı tercih etmektedir.



Yorum bırakın