Go Programming

Go Mikroservislerinde Kimlik Doğrulama ve Günlükleme için gRPC İnterceptor'larının Uygulanması

Dağıtık sistemlerin modern manzarasında mikroservisler, verimli ve güvenli iletişime heavily bağımlıdır. Google'ın gRPCsi yüksek performansı, güçlü tiplemesi ve HTTP/2 için yerel desteği nedeniyle iç hizmetler arası iletişim için varsayılan standart haline gelmiştir. Ancak, sağlam bir mikroservis mimarisi kurmak yalnızca protobuf mesajları tanımlamaktan daha fazlasını gerektirir. Güvenlik, gözlemlenebilirlik ve hata yönetimi gibi çapraz kesen endişelerin tüm hizmetlerde tutarlı bir şekilde ele alınmasını gerektirir.

Burada gRPC İnterceptor'ları devreye girer. Ara yazılım olarak hareket eden interceptor'lar, gRPC sunucu ve istemci yaşam döngüsüne bağlanmanıza, çekirdek iş mantığını karmaşıklaştırmadan kimlik doğrulama, günlükleme, metrikler ve yeniden deneme için mantık enjekte etmenize olanak tanır. Bu yazıda, Go'da iki kritik interceptor'ı nasıl uygulayacağımızı inceleyeceğiz: biri kimlik doğrulama, diğeri de detaylı istek günlüklemesi için.

gRPC İnterceptor'larını Anlamak

gRPC'deki interceptor'lar, RPC çağrılarının etrafında sarmalayan işlevlerdir. Gerçek işleyicinin çalışmasından önce (sunucu tarafında) veya istemci bir istek yaptıktan sonra (istemci tarafında) çalışırlar. Bu desen, Gin veya Echo gibi HTTP çerçevelerindeki ara yazılıma benzer ancak RPC'nin taşıma katmanında çalışır.

Sunucu tarafı unary RPC'ler (standart istek-yanıt) için Go'daki interceptor imzası şöyledir:

func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
    // İşleyici öncesi mantık
    resp, err := handler(ctx, req)
    // İşleyici sonrası mantık
    return resp, err
}

Birkaç interceptor'ı zincirleyerek güçlü bir boru hattı oluşturabilirsiniz. Örneğin, bir günlükleme interceptor'ının bir kimlik doğrulama interceptor'ını sarmaladığı, bunun da asıl iş mantığınızı sarmaladığı bir senaryo olabilir.

Bir Kimlik Doğrulama İnterceptor'ı Uygulama

Güvenlik hayati önem taşır. Yaygın bir desen, korunan uç noktalara erişime izin vermeden önce gRPC isteğinin meta verilerinde geçirilen bir JWT'nin (JSON Web Token) geçerliliğini doğrulamaktır. Aşağıdaki interceptor, token'ı Authorization başlığından nasıl çıkaracağını, imzasını doğrulayacak ve aşağı akış işleyicileri için kullanıcı iddialarını bağlama enjekte edeceğini gösterir.

func AuthInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
    md, ok := metadata.FromIncomingContext(ctx)
    if !ok {
        return nil, status.Error(codes.Unauthenticated, "meta veri eksik")
    }

    tokens := md.Get("authorization")
    if len(tokens) == 0 {
        return nil, status.Error(codes.Unauthenticated, "token eksik")
    }

    token := tokens[0]
    // validateJWT'nin iddiaları veya bir hata döndürdüğünü varsayalım
    claims, err := validateJWT(token)
    if err != nil {
        return nil, status.Error(codes.Unauthenticated, "geçersiz token")
    }

    // Kullanıcı kimliğini bağlama enjekte et
    ctx = context.WithValue(ctx, "userID", claims.UserID)
    return handler(ctx, req)
}

Bu yaklaşım, her korunan uç noktanın kimliği doğrulanmış kullanıcıyı ctx.Value("userID") çağırarak basitçe alabilmesini sağlar ve iş mantığını temiz ve odaklı tutar.

Bir İstek Günlükleme İnterceptor'ı Uygulama

Gözlemlenebilirlik, güvenlik kadar önemlidir. Bir mikroservis mimarisinde, isteklerin farklı hizmetler arasındaki akışını izlemek, hata ayıklama ve performans izleme için hayati önem taşır. Bir günlükleme interceptor'ı, başlangıç zamanını, yöntem adını, istek yükünü (temizlenmiş) ve yanıt durumunu yakalayabilir.

func LoggingInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
    start := time.Now()
    log.Printf("Başlatıldı %s", info.FullMethod)

    resp, err := handler(ctx, req)
    if err != nil {
        log.Printf("Tamamlandı %s %v içinde hata ile: %v", info.FullMethod, time.Since(start), err)
        return resp, err
    }

    log.Printf("Tamamlandı %s %v içinde", info.FullMethod, time.Since(start))
    return resp, nil
}

Bu basit interceptor, uç noktalarınızın yanıt vermesi için ne kadar süre harcadığına dair anında içgörü sağlar ve başarısız istekleri hızlıca belirlemenize yardımcı olur. Üretim ortamlarında, muhtemelen Zap veya Logrus gibi yapılandırılmış günlükleme kütüphaneleriyle entegrasyon yaparak metrikleri Prometheus'a göndererek bunu geliştireceksiniz.

Hepsini Bir Araya Getirme

Bu interceptor'ları kullanmak için, gRPC sunucunuzu başlatırken onları kaydedersiniz. Bunları grpc.NewServer()'a seçenek olarak geçebilirsiniz. Sıra önemlidir: listedeki ilk interceptor ilk çalışır, yani ikinciyi sarmalar ve böyle devam eder.

server := grpc.NewServer(
    grpc.UnaryInterceptor(LoggingInterceptor),
    grpc.UnaryInterceptor(AuthInterceptor),
)

Sonuç

Go'da gRPC interceptor'ları uygulamak, sürdürülebilir ve güvenli mikroservisler oluşturmak için en iyi uygulamadır. Kimlik doğrulama ve günlükleme gibi endişeleri yeniden kullanılabilir interceptor'lara ayırarak, çekirdek uygulama mantığınızı temiz tutar ve tüm hizmetlerinizde tutarlı davranışı sağlarsınız. API'nizi JWT'lerle güvence altına almanız veya detaylı günlüklerle performansı izlemeniz fark etmez, interceptor'lar çapraz kesen endişeleri etkili bir şekilde ele almak için gereken esnek ve güçlü mekanizmayı sağlar.

Share: