Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Arka uç işlemenin zaman uyumsuz olarak çalışması gerektiğinde ancak ön ucun net bir yanıta ihtiyacı olduğunda arka uç işlemesini ön uç ana bilgisayarından ayırın.
Bağlam ve sorun
Modern uygulama geliştirmede istemci uygulamaları genellikle iş mantığı sağlamak ve işlevsellik oluşturmak için uzak API'leri kullanır. Birçok uygulama bir web tarayıcısında kod çalıştırır ve diğer ortamlar da istemci kodunu barındırır. API'ler doğrudan uygulamayla ilişkilendirilebilir veya dış hizmetten paylaşılan hizmetler olarak çalışabilir. ÇOĞU API çağrısı HTTP veya HTTPS kullanır ve REST semantiğini izler.
Çoğu durumda, bir istemci uygulamasının API'leri yaklaşık 100 milisaniye (ms) veya daha kısa sürede yanıt verir. Yanıt gecikme süresini birçok faktör etkileyebilir:
- Uygulamanın barındırma yığını
- Güvenlik bileşenleri
- Arayanın ve arka ucun göreli coğrafi konumu
- Ağ altyapısı
- Mevcut yük
- İstek yükünün boyutu
- İşleme kuyruk uzunluğu
- Arka ucun isteği işleme süresi
Bu faktörler yanıta gecikme süresi ekleyebilir. Arka ucun ölçeğini genişleterek bazı faktörleri azaltabilirsiniz. Ağ altyapısı gibi diğer faktörler uygulama geliştiricisinin denetimi dışındadır. Çoğu API, yanıtın aynı bağlantı üzerinden döndürülmesi için yeterince hızlı yanıt verir. Uygulama kodu, zaman uyumsuz işleme görünümü vermek için engelleyici olmayan bir şekilde zaman uyumlu API çağrısı yapabilir. Giriş ve çıkış (G/Ç) bağlı işlemler için bu yaklaşımı öneririz.
Bazı senaryolarda arka uç, uzun süren ve birkaç saniye sürebilen işlemler gerçekleştirir. Diğer senaryolarda arka uç, uzun süre çalışan arka plan çalışmalarını dakikalar veya uzun süreler boyunca yapar. Böyle durumlarda, yanıt göndermeden önce çalışmanın bitmesini bekleyemezsiniz. Bu durum, zaman uyumlu istek-yanıt desenleri için sorun oluşturabilir.
Bazı mimariler, istek ve yanıt aşamalarını ayırmak için bir ileti aracısı kullanarak bu sorunu çözer. Birçok sistem bu ayrımı Queue-Based Yük Dengeleme düzeniyle başarır. Bu ayrım, istemci işleminin ve arka uç API'sinin bağımsız olarak ölçeklendirilmesini sağlar. İstemci başarı bildirimini gerektirdiğinde ve bu adım da eşzamansız hale gelmesi gerektiğinden, ekstra karmaşıklık ortaya çıkar.
İstemci uygulamaları için geçerli olan konuların çoğu, mikro hizmet mimarisinde olduğu gibi dağıtılmış sistemlerdeki sunucudan sunucuya REST API çağrıları için de geçerlidir.
Çözüm
Bu sorunun bir çözümü HTTP yoklamasını kullanmaktır. Geri çağırma uç noktaları kullanılamadığında veya uzun süre çalışan bağlantılar çok fazla karmaşıklık kattığında yoklama, istemci tarafı kodu için iyi çalışır. Geri çağrılar mümkün olsa bile, gerektirdikleri ek kitaplıklar ve hizmetler karmaşıklığı artırabilir.
Aşağıdaki adımlar çözümü açıklar:
İstemci uygulaması, arka uçta uzun süre çalışan bir işlemi tetikleyebilmek için API'ye zaman uyumlu bir çağrı yapar.
API mümkün olan en kısa sürede zaman uyumlu bir şekilde yanıt verir. İşleme isteğini aldığını kabul etmek için bir HTTP 202 (Kabul Edildi) durum kodu döndürür.
Uyarı
API, uzun süre çalışan işlemi başlatmadan önce gerçekleştirilecek isteği ve eylemi doğrular. İstek geçerli değilse HTTP 400 (Hatalı İstek) gibi bir hata koduyla hemen yanıtlayın.
Yanıt, istemcinin uzun süreli işlemin sonucunu kontrol etmek için yoklayabileceği bir uç noktaya yönlendiren bir konum referansı içerir.
API, işlemeyi ileti kuyruğu gibi başka bir bileşene boşaltıyor.
Durum uç noktasına başarılı bir çağrı için uç nokta HTTP 200 (Tamam) döndürür. Çalışma devam ederken uç nokta bu durumu gösteren bir kaynak döndürür. çalışma tamamlandığında, uç nokta tamamlanmayı gösteren veya başka bir kaynak URL'sine yönlendiren bir kaynak döndürür. Örneğin, zaman uyumsuz işlem yeni bir kaynak oluşturursa, durum uç noktası bu kaynağın URL'sine yönlendirilir.
Aşağıdaki diyagramda tipik bir akış gösterilmektedir.
Zaman uyumsuz HTTP istekleri için istek ve yanıt akışını gösteren diyagram.
İstemci, API uç noktası, durum uç noktası ve kaynak URI'sini gösteren sıralı diyagram. İstemci, API uç noktasına HTTP 202 döndüren bir POST isteği gönderir. İstemci daha sonra durum uç noktasına yinelenen GET istekleri gönderir. İlk yanıt HTTP 200,sonraki bir yanıt ise HTTP 302 (Bulundu) döndürür. İstemci, kaynak URI'sine bir GET isteğiyle yeniden yönlendirmeyi izler ve bu da HTTP 200 döndürür. Diyagram, sorgulama ve tamamlanan kaynağa son yönlendirme içeren zaman uyumsuz bir istek modelini gösterir.
İstemci bir istek gönderir ve bir HTTP 202 yanıtı alır.
İstemci, durum uç noktasına bir HTTP GET isteği gönderir. Görev beklemede, bu nedenle bu çağrı HTTP 200 döndürür.
Çalışma tamamlanmıştır ve durum uç noktası kaynağa yeniden yönlendirmek için HTTP 302 (Bulundu) değerini döndürür.
İstemci, belirtilen URL'deki kaynağı getirir.
Sorunlar ve dikkat edilmesi gerekenler
Bu düzenin nasıl uygulaneceğine karar velarken aşağıdaki noktaları göz önünde bulundurun:
Bu düzeni HTTP üzerinden uygulamak için birden çok yol vardır ve yukarı akış hizmetleri her zaman aynı semantiği kullanmaz. Örneğin çoğu hizmet, uzak işlem tamamlanmadığında HTTP 202 yerine GET yönteminden HTTP 404 (Bulunamadı) döndürür. Standart REST semantiğine göre, http 404 doğru yanıttır çünkü çağrının sonucu henüz mevcut değildir.
HTTP 202 yanıtı, istemcinin nerede anket yaptığı ve ne sıklıkta olduğunu gösterir. Şu başlıkları içerir.
Başlık Açıklama Notlar Locationİstemcinin yanıt durumu için yoklama yaptığı URL Bu URL paylaşılan erişim imzası belirteci olabilir. Vale Anahtarı düzeni , bu konumun erişim denetimine ihtiyacı olduğunda düzgün çalışır. Model, yanıt sorgulamasının başka bir backend'e taşınması gerektiğinde de geçerlidir. Retry-Afterİşlemenin ne zaman tamamlanacağına yönelik bir tahmin Bu başlık, yoklama istemcilerinin arka uca çok fazla istek göndermesini engeller. Bu yanıtı tasarlarken beklenen istemci davranışını göz önünde bulundurun. Denetlediğiniz bir istemci bu yanıt değerlerini tam olarak izleyebilir. Kod içermeyen veya Azure Logic Apps gibi düşük kodlu araçlar kullanılarak oluşturulan istemciler de dahil olmak üzere başkalarının oluşturduğu istemciler, HTTP 202 için kendi işlemelerini uygulayabilir.
Kullandığınız temel hizmetlere bağlı olarak yanıt üst bilgilerini veya yükünü ayarlamak için bir işlem ara sunucusu kullanmanız gerekebilir.
Tamamlandıktan sonra durum uç noktası yeniden yönlendiriliyorsa, http 302 veya HTTP 303 (Bkz. Diğer), desteklediğiniz semantiklere bağlı olarak geçerli dönüş kodlarıdır.
Sunucu isteği işledikten sonra üst bilginin belirttiği kaynak 200, 201 (Oluşturuldu) veya 204 (İçerik Yok) gibi bir HTTP durum kodu döndürür.
İşleme sırasında bir hata oluşursa, üst bilginin belirttiği kaynak URL'sinde hatayı kalıcı hale getirir ve bu kaynaktan hatayla eşleşen bir 4xx durum kodu döndürür.
Çözümlerin tümü bu düzeni aynı şekilde uygulamaz ve bazı hizmetler ek veya alternatif üst bilgiler içerir. Örneğin, Azure Resource Manager bu desenin değiştirilmiş bir değişkenini kullanır. Daha fazla bilgi için Kaynak Yöneticisi zaman uyumsuz işlemler bölümüne başvurun.
Eski istemciler bu düzeni desteklemeyebilir. Bu durumda, zaman uyumsuz işlemeyi özgün istemciden gizlemek için zaman uyumsuz API'nin üzerine bir işlem ara sunucusu yerleştirmeniz gerekebilir. Örneğin, Logic Apps bu deseni yerel olarak destekler ve zaman uyumsuz API ile zaman uyumlu çağrılar yapan bir istemci arasında tümleştirme katmanı olarak kullanabilirsiniz. Daha fazla bilgi için bkz. Uzun süre çalışan görevleri web kancası eylem düzeniyle gerçekleştirme.
Bazı senaryolarda, istemcilerin uzun süre çalışan bir isteği iptal edebilmesi için bir yol sağlamak isteyebilirsiniz. Bu durumda, arka uç hizmetinin bir tür iptal yönergesini desteklemesi gerekir.
Bu desen ne zaman kullanılır?
Bu düzeni aşağıdaki durumlarda kullanın:
Tarayıcı uygulamaları gibi istemci tarafı kodlarla çalışırsınız ve bu kısıtlamalar geri çağırma uç noktalarının sağlanmasını zor hale getirir veya uzun süre çalışan bağlantılar çok fazla karmaşıklık ekler.
Yalnızca HTTP protokolünü kullanan bir hizmeti çağırırsınız ve istemci tarafındaki güvenlik duvarı kısıtlamaları nedeniyle dönüş hizmeti geri çağırma gönderemez.
WebSockets veya web kancaları gibi modern geri çağırma mekanizmalarını desteklemeyen eski mimarilerle tümleşirsiniz.
Bu düzen aşağıdaki durumlarda uygun olmayabilir:
Bunun yerine Azure Event Grid gibi zaman uyumsuz bildirimler için oluşturulmuş bir hizmeti kullanabilirsiniz.
Yanıtların istemciye gerçek zamanlı olarak akışı yapılmalıdır.
İstemcinin birçok sonuç toplaması gerekir ve bu sonuçların gecikme süresi önemlidir. Bunun yerine bir "service bus" deseni düşünün.
WebSockets veya SignalR gibi sunucu tarafı kalıcı ağ bağlantıları kullanılabilir. Bu bağlantıları, sonucu çağırana bildirmek için kullanabilirsiniz.
Ağ tasarımı, asenkron geri aramalar veya web kancalarını almak için açık portları destekler.
İş yükü tasarımı
Bir mimar, Azure Well-Architected Framework sütunları kapsamındaki hedefleri ve ilkeleri ele almak için çalışma yükü tasarımında Asenkron İstek-Cevap desenini nasıl kullanabileceğini değerlendirmelidir.
| Ana Direk | Bu desen sütun hedeflerini nasıl destekler? |
|---|---|
| Performans Verimliliği , ölçeklendirme, veri ve kod iyileştirmeleri aracılığıyla iş yükünüzün talepleri verimli bir şekilde karşılamasını sağlar. | Anında yanıt gerektirmeyen işlemler için istek ve yanıt aşamalarını birbirinden ayrıştırarak yanıt verme hızını ve ölçeklenebilirliği geliştirirsiniz. Zaman uyumsuz bir yaklaşım eşzamanlılığı artırır ve kapasite kullanılabilir hale geldikçe sunucu zamanlamasının çalışmasını sağlar. PE:05 Ölçeklendirme ve bölümleme PE:07 Kod ve altyapı |
Her tasarım kararında olduğu gibi, bu düzenin ortaya çıkarabileceği diğer sütunların hedeflerine karşı dengeleri göz önünde bulundurun.
Example
Aşağıdaki kod, bu deseni uygulamak için Azure İşlevleri kullanan bir uygulamadan alıntıları gösterir. Bu çözümün üç işlevi vardır:
- Zaman uyumsuz API uç noktası
- Durum uç noktası
- Kuyruğa alınmış iş öğelerini alan ve bunları çalıştıran bir arka uç işlevi
İşlevler'de Zaman Uyumsuz İstek Yanıtı deseninin yapısının diyagramı.
1. adımda, bir istemci bir API çağırır. 2. adımda API kuyruğa bir ileti yerleştirir. 3. adımda API, istemciye bir durum uç noktası döndürür. 4. adımda çalışan iletiyi kuyruktan alır. 5. adımda çalışan iletiyi işler ve sonucu blob depolamaya yazar. 6. adımda istemci durum uç noktasını çağırır. 7. adımda durum uç noktası blob depolamadaki sonucu denetler.
Bu örnek GitHub üzerinde kullanılabilir.
AsyncProcessingWorkAcceptor işlevi
İşlev, bir istemci uygulamasından işlemi kabul eden ve işleme alacak şekilde sıraya koyan bir uç nokta sağlar.
İşlev bir istek kimliği oluşturur ve bunu kuyruk iletisine meta veri olarak ekler.
HTTP yanıtı, durum uç noktasına işaret eden bir üst bilgi içerir. İstek kimliği URL yolunda görünür.
public class AsyncProcessingWorkAcceptor(ServiceBusClient _serviceBusClient)
{
[Function("AsyncProcessingWorkAcceptor")]
public async Task<IActionResult> RunAsync([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req, [FromBody] CustomerPOCO customer)
{
if (string.IsNullOrEmpty(customer.id) || string.IsNullOrEmpty(customer.customername))
{
return new BadRequestResult();
}
var reqid = Guid.NewGuid().ToString();
string scheme = Environment.GetEnvironmentVariable("AZURE_FUNCTIONS_ENVIRONMENT") == "Development" ? "http" : "https";
var rqs = $"{scheme}://{Environment.GetEnvironmentVariable("WEBSITE_HOSTNAME")}/api/RequestStatus/{reqid}";
var messagePayload = JsonConvert.SerializeObject(customer);
var message = new ServiceBusMessage(messagePayload);
message.ApplicationProperties.Add("RequestGUID", reqid);
message.ApplicationProperties.Add("RequestSubmittedAt", DateTime.Now);
message.ApplicationProperties.Add("RequestStatusURL", rqs);
var sender = _serviceBusClient.CreateSender("outqueue");
await sender.SendMessageAsync(message);
return new AcceptedResult(rqs, $"Request Accepted for Processing{Environment.NewLine}ProxyStatus: {rqs}");
}
}
AsyncProcessingBackgroundWorker işlevi
İşlev işlemi kuyruktan okur, ileti yüküne göre işler ve sonucu bir depolama hesabına yazar.
public class AsyncProcessingBackgroundWorker(BlobContainerClient _blobContainerClient)
{
[Function(nameof(AsyncProcessingBackgroundWorker))]
public async Task Run([ServiceBusTrigger("outqueue", Connection = "ServiceBusConnection")] ServiceBusReceivedMessage message)
{
var requestGuid = message.ApplicationProperties["RequestGUID"].ToString();
string blobName = $"{requestGuid}.blobdata";
await _blobContainerClient.CreateIfNotExistsAsync();
var blobClient = _blobContainerClient.GetBlobClient(blobName);
using (MemoryStream memoryStream = new MemoryStream())
using (StreamWriter writer = new StreamWriter(memoryStream))
{
writer.Write(message.Body.ToString());
writer.Flush();
memoryStream.Position = 0;
await blobClient.UploadAsync(memoryStream, overwrite: true);
}
}
}
AsyncOperationStatusChecker işlevi
İşlev, durum uç noktasını gerçekleştirir. Bu işlev isteğin durumunu denetler:
İstek tamamlanırsa işlev yanıta bir vale anahtarı döndürür veya çağrıyı hemen vale anahtarı URL'sine yönlendirir.
İstek beklemedeyse, işlev geçerli durumu içeren bir HTTP 200 kodu döndürür.
public class AsyncOperationStatusChecker(ILogger<AsyncOperationStatusChecker> _logger)
{
[Function("AsyncOperationStatusChecker")]
public async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "RequestStatus/{thisGUID}")] HttpRequest req,
[BlobInput("data/{thisGUID}.blobdata", Connection = "DataStorage")] BlockBlobClient inputBlob, string thisGUID)
{
OnCompleteEnum OnComplete = Enum.Parse<OnCompleteEnum>(req.Query["OnComplete"].FirstOrDefault() ?? "Redirect");
OnPendingEnum OnPending = Enum.Parse<OnPendingEnum>(req.Query["OnPending"].FirstOrDefault() ?? "OK");
_logger.LogInformation($"C# HTTP trigger function processed a request for status on {thisGUID} - OnComplete {OnComplete} - OnPending {OnPending}");
// Check whether the blob exists.
if (await inputBlob.ExistsAsync())
{
// If the blob exists, the function uses the OnComplete parameter to determine the next action.
return await OnCompleted(OnComplete, inputBlob, thisGUID);
}
else
{
// If the blob doesn't exist, the function uses the OnPending parameter to determine the next action.
string scheme = Environment.GetEnvironmentVariable("AZURE_FUNCTIONS_ENVIRONMENT") == "Development" ? "http" : "https";
string rqs = $"{scheme}://{Environment.GetEnvironmentVariable("WEBSITE_HOSTNAME")}/api/RequestStatus/{thisGUID}";
switch (OnPending)
{
case OnPendingEnum.OK:
{
// Return an HTTP 200 status code.
return new OkObjectResult(new { status = "In progress", Location = rqs });
}
case OnPendingEnum.Synchronous:
{
// Back off and retry. Time out if the back-off period reaches one minute.
int backoff = 250;
while (!await inputBlob.ExistsAsync() && backoff < 64000)
{
_logger.LogInformation($"Synchronous mode {thisGUID}.blob - retrying in {backoff} ms");
backoff = backoff * 2;
await Task.Delay(backoff);
}
if (await inputBlob.ExistsAsync())
{
_logger.LogInformation($"Synchronous Redirect mode {thisGUID}.blob - completed after {backoff} ms");
return await OnCompleted(OnComplete, inputBlob, thisGUID);
}
else
{
_logger.LogInformation($"Synchronous mode {thisGUID}.blob - NOT FOUND after timeout {backoff} ms");
return new NotFoundResult();
}
}
default:
{
throw new InvalidOperationException($"Unexpected value: {OnPending}");
}
}
}
}
private async Task<IActionResult> OnCompleted(OnCompleteEnum OnComplete, BlockBlobClient inputBlob, string thisGUID)
{
switch (OnComplete)
{
case OnCompleteEnum.Redirect:
{
// The typical way to generate a shared access signature token in code requires the storage account key.
// If you need to use a managed identity to control access to your storage accounts in code, which is a recommended best practice, you should do so when possible.
// In this scenario, you don't have a storage account key, so you need to find another way to generate the shared access signatures.
// To generate shared access signatures, use a user delegation shared access signature. This approach lets you sign the shared access signature by using Microsoft Entra ID credentials instead of the storage account key.
BlobServiceClient blobServiceClient = inputBlob.GetParentBlobContainerClient().GetParentBlobServiceClient();
var userDelegationKey = await blobServiceClient.GetUserDelegationKeyAsync(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddDays(7));
// Redirect the shared access signature uniform resource identifier (URI) to blob storage.
return new RedirectResult(inputBlob.GenerateSASURI(userDelegationKey));
}
case OnCompleteEnum.Stream:
{
// Download the file and return it directly to the caller.
// For larger files, use a stream to minimize RAM usage.
return new OkObjectResult(await inputBlob.DownloadContentAsync());
}
default:
{
throw new InvalidOperationException($"Unexpected value: {OnComplete}");
}
}
}
}
public enum OnCompleteEnum
{
Redirect,
Stream
}
public enum OnPendingEnum
{
OK,
Synchronous
}
Aşağıdaki sınıf, durum denetleyicisinin sonuç blobu için kullanıcı temsilcisi paylaşılan erişim imzası tekdüzen kaynak tanımlayıcısı (URI) oluşturmak için kullandığı bir uzantı yöntemi sağlar.
public static class CloudBlockBlobExtensions
{
public static string GenerateSASURI(this BlockBlobClient inputBlob, UserDelegationKey userDelegationKey)
{
BlobServiceClient blobServiceClient = inputBlob.GetParentBlobContainerClient().GetParentBlobServiceClient();
BlobSasBuilder blobSasBuilder = new BlobSasBuilder()
{
BlobContainerName = inputBlob.BlobContainerName,
BlobName = inputBlob.Name,
Resource = "b",
StartsOn = DateTimeOffset.UtcNow,
ExpiresOn = DateTimeOffset.UtcNow.AddMinutes(10)
};
blobSasBuilder.SetPermissions(BlobSasPermissions.Read);
var blobUriBuilder = new BlobUriBuilder(inputBlob.Uri)
{
Sas = blobSasBuilder.ToSasQueryParameters(userDelegationKey, blobServiceClient.AccountName)
};
// Generate the shared access signature on the blob, which sets the constraints directly on the signature.
Uri sasUri = blobUriBuilder.ToUri();
// Return the URI string for the container, including the shared access signature token.
return sasUri.ToString();
}
}
Sonraki Adımlar
- Uzun süre çalışan görevler için yoklama eylem desenini kullanın
- Asenkron HTTP API düzeni
İlgili kaynaklar
- Web API'si tasarımı
- Ön Uçlar için Arka Uçlar düzeni
- Vale Anahtarı deseni