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'lere bağımlıdı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. Arka uç işlemeyi tasarlama hakkında yönergeler için bkz. Arka plan işleri.
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 isteği ve gerçekleştirilecek eylemi doğrulamalıdır. İ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 yapılan her başarılı çağrı için uç nokta HTTP 200 (Tamam) değerini döndürür. Çalışma devam ederken durum uç noktası bu durumu gösteren bir kaynak döndürür. Durum yanıt gövdesi, istemcinin işlemin geçerli durumunu anlaması için yeterli bilgi içermelidir.
Çalışma tamamlandığında, durum uç noktası tamamlandığını 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.
İstemci bir istek gönderir ve bir HTTP 202 (Kabul Edildi) yanıtı alır.
İstemci, durum uç noktasına bir HTTP GET isteği gönderir. Çalışma beklemede olduğundan bu çağrı HTTP 200 döndürür.
Bir noktada iş tamamlanmıştır ve durum uç noktası kaynağa yeniden yönlendirmek için HTTP 303 (Diğer'e Bakın) 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, bazı uygulamalar ayrı bir durum uç noktası kullanmaz. Bunun yerine, istemci hedef kaynak URL'sini doğrudan yoklar ve kaynak oluşturulana kadar HTTP 404 (Bulunamadı) alır. Kaynak henüz mevcut olmadığından bu yanıt oluşturulur. Ancak geçersiz istek kimlikleri de HTTP 404 döndürdiğinden bu yaklaşım belirsiz olabilir. Bu düzende açıklandığı gibi durum gövdesine sahip HTTP 200 döndüren ayrılmış durum uç noktası bu karışıklığı önler.
HTTP 202 yanıtı, istemcinin nerede anket yaptığı ve ne sıklıkta olduğunu gösterir. Aşağıdaki başlıkları içermelidir.
Başlık Açıklama Notlar Locationİstemcinin yanıt durumu için yoklama yaptığı URL Bu URL, paylaşılan erişim imzası (SAS) belirteci olabilir. Vale Anahtarı deseni, bu konumun erişim kontrolüne ihtiyacı olduğunda iyi çalışır. Model, yanıt sorgulamasının başka bir backend'e taşınması gerektiğinde de geçerlidir. Retry-Afterİşleme için tahmini tamamlanma süresi Bu başlık, yoklama istemcilerinin arka uca çok fazla istek göndermesini önlemeye yardımcı olur. 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.
Durum uç noktası yanıtına aşağıdaki alanları eklemeyi göz önünde bulundurun.
Veri Alanı Açıklama Notlar statusİşlemin Beklemede, Çalışıyor, Başarılı, Başarısız veya İptal Edildi gibi geçerli durumu Tutarlı, belgelenmiş bir terminal ve köksüz değerler kümesi kullanır createdAtİşlemin kabul edildiği saat Müşterilerin eski veya terk edilmiş işlemleri tespit etmesine yardımcı olur lastUpdatedAtDurumun en son güncelleştirilmiş olduğu saat İstemcilerin durdurulmuş ve devam eden işlemleri ayırt etmesine yardımcı olur percentCompleteİsteğe bağlı ilerleme göstergesi Arka uç ilerleme durumunu tahmin ettiğinde kullanışlıdır errorDurum Başarısız olduğunda yapılandırılmış bir hata nesnesi Tutarlılık için RFC 9457 biçimini kullanmayı göz önünde bulundurun. 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 303 (Diğer'e bakın) kullanın. 303, istemciye özgün istek yönteminden bağımsız olarak yeniden yönlendirme URL'sine bir GET isteği göndermesini yönlendirir. Bu davranış, istemcinin özgün işlemi yeniden göndermeyip farklı bir sonuç kaynağı alması nedeniyle, bu desen için doğru anlamsal davranıştır. HTTP 302 (Bulundu) yöntem değişikliğini garanti etmez. Bazı istemciler, yeniden yönlendirme sırasında özgün yöntemi tekrar çalıştırabilir. Bu davranış, yinelenen POST istekleri gibi istenmeyen yan etkilere neden olabilir.
Sunucu isteği başarıyla işledikten sonra, üst bilginin belirttiği kaynak
Location200, 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
Locationhatayı kalıcı hale getirir ve kaynaktan hatayla eşleşen bir 4xx durum kodu döndürür. İstemcilerin hataları program aracılığıyla ayrıştırabilmesi ve işleyebilmesi için RFC 9457 (HTTP API'leri için Sorun Ayrıntıları) gibi yapılandırılmış bir hata biçimi kullanın.Durum kaynağı ve depolanan sonuçlar depolama ve işlem tüketir. Makul bir süre sonra bunları temizlemek için bir bekletme ilkesi tanımlayın. İstemcileri bekletme penceresi hakkında bilgilendirmek için durum yanıtına bir
Expiresüst bilgi ekleyebilirsiniz.Çö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 bkz. Resource Manager eşzamansız işlemler.
Eski istemciler bu düzeni desteklemeyebilir. Bu durumda, zaman uyumsuz işlemeyi özgün istemciden gizlemek için zaman uyumsuz API'nin üzerine bir cephe 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. Logic Apps'te zaman uyumsuz istek-yanıt davranışı.
İstemcilerin uzun süre çalışan bir isteği iptal etmelerine yönelik bir yol sağlamak için durum uç noktası kaynağında delete işlemini kullanıma sunun. Bu istek, bir iptal yönergesini arka uç işleme bileşenine iletmelidir. Arka uç iptali işledikten sonra, durum kaynağını iptal edilen durumu yansıtacak şekilde güncelleştirmelidir. Bu işlem, tamamlanmamış çalışmanın kaynakları süresiz olarak tüketmesini önlemeye yardımcı olur. İşlemin kısmi geri almayı destekleyip desteklemediğini veya telafi işlemi gerektirip gerektirmediğini belirleyin.
İstemcilerin, ilk isteği gönderirken bir istek üst bilgisinde olduğu gibi bir
Idempotency-Keyeşzamanlılık anahtarı sağlamalarını isteyebilirsiniz. Arka uç yinelenen bir anahtar alırsa, ikinci bir iş öğesini sıralamak yerine var olan durum kaynağını döndürmelidir. Bu yaklaşım, istemcinin sunucunun zaten kabul edilmiş bir POST'unu yeniden denemesine neden olan ağ hatalarına karşı koruma sağlar. İstemci kayıp yanıtla hiç alınmamış bir istek arasında ayrım yapamaz olduğundan bu düzende özellikle önemlidir.
Uyarı
Bu düzen, istemcinin düzenli aralıklarla durumu denetlemek için yeni istekler yayınladığı HTTP yoklamasını açıklar. Uzun yoklamada, istemci bir istek gönderir ve sunucu yeni veriler kullanılabilir duruma gelene veya zaman aşımı oluşana kadar bağlantıyı açık tutar. Uzun yoklama, düzenli yoklamalarla karşılaştırıldığında yanıt gecikme süresini azaltır, ancak bağlantı yönetimi ve zaman aşımları konusunda karmaşıklık sağlar.
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 yöntemlerini desteklemeyen iş yükleriyle entegre olursunuz.
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 yoklamasına gerek kalmadan sunucudan istemciye basit, HTTP yerel, tek yönlü bir gönderme kanalı sağlayan Server-Sent Olayları (SSEs) kullanmayı göz önünde bulundurun.
İstemcinin birçok sonuç toplaması gerekir ve bu sonuçların gecikme süresi önemlidir. Bunun yerine bir ileti aracısı kullanmayı göz önünde bulundurun.
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, iş yükünün tasarımında Azure Well-Architected Framework sütunları kapsamındaki hedefleri ve ilkeleri ele almak için Zaman Uyumsuz İstek-Yanıt 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 Functions 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
Bu örnek GitHub üzerinde kullanılabilir.
Uygulama, Azure Service Bus ve Azure Blob Storage ile kimlik doğrulaması yapmak için yönetilen kimlik kullanır ve bu da bağlantı dizelerinin veya hesap anahtarlarının depolanmasını önler. Bağımlılıklar Program.cs kullanılarak DefaultAzureCredential içinde kaydedilir ve birincil yapıcılar aracılığıyla eklenir.
AsyncProcessingWorkAcceptor işlevi
İşlev, AsyncProcessingWorkAcceptor bir istemci uygulamasından çalışmayı kabul eden ve işlenmek üzere sıralayan bir uç nokta uygular:
İşlev bir istek kimliği oluşturur ve bunu kuyruk iletisine meta veri olarak ekler.
HTTP yanıtı, bir durum uç noktasına işaret eden bir
Locationbaşlık ve yoklama aralığı öneren birRetry-Afterbaşlık içerir. İstek kimliği URL yolunda görünür.
public class AsyncProcessingWorkAcceptor(ServiceBusClient _serviceBusClient)
{
[Function("AsyncProcessingWorkAcceptor")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req,
[FromBody] CustomerPOCO customer)
{
if (string.IsNullOrEmpty(customer.id) || string.IsNullOrEmpty(customer.customername))
{
return new BadRequestResult();
}
string requestId = Guid.NewGuid().ToString();
string statusUrl = $"https://{Environment.GetEnvironmentVariable("WEBSITE_HOSTNAME")}/api/RequestStatus/{requestId}";
var messagePayload = JsonConvert.SerializeObject(customer);
var message = new ServiceBusMessage(messagePayload);
message.ApplicationProperties.Add("RequestGUID", requestId);
message.ApplicationProperties.Add("RequestSubmittedAt", DateTime.UtcNow);
message.ApplicationProperties.Add("RequestStatusURL", statusUrl);
var sender = _serviceBusClient.CreateSender("outqueue");
await sender.SendMessageAsync(message);
req.HttpContext.Response.Headers["Retry-After"] = "5";
return new AcceptedResult(statusUrl, null);
}
}
AsyncProcessingBackgroundWorker işlevi
AsyncProcessingBackgroundWorker İş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("AsyncProcessingBackgroundWorker")]
public async Task Run(
[ServiceBusTrigger("outqueue", Connection = "ServiceBusConnection")] ServiceBusReceivedMessage message)
{
// Perform an action against the blob data source for the async readers to check against.
// This is where your service worker processing will be performed.
var requestGuid = message.ApplicationProperties["RequestGUID"].ToString();
string blobName = $"{requestGuid}.blobdata";
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
AsyncOperationStatusChecker işlevi durum uç noktasını uygular. Bu işlev isteğin durumunu denetler:
İstek tamamlanırsa işlev HTTP 303 döndürür (Diğer'e bakın) ve sonuç için istemciyi bir vale anahtarı URL'sine yönlendirir.
İstek beklemedeyse, işlev mevcut 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/{requestId}")] HttpRequest req,
[BlobInput("data/{requestId}.blobdata", Connection = "DataStorage")] BlockBlobClient inputBlob, string requestId)
{
OnCompleteEnum OnComplete = Enum.Parse<OnCompleteEnum>(req.Query["OnComplete"].FirstOrDefault() ?? "Redirect");
OnPendingEnum OnPending = Enum.Parse<OnPendingEnum>(req.Query["OnPending"].FirstOrDefault() ?? "OK");
_logger.LogInformation("Received status request for {RequestId} - OnComplete {OnComplete} - OnPending {OnPending}",
requestId, OnComplete, 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, requestId, req);
}
else
{
// If the blob doesn't exist, the function uses the OnPending parameter to determine the next action.
switch (OnPending)
{
case OnPendingEnum.OK:
{
// Return an HTTP 200 status code.
return new OkObjectResult(new { status = "In progress", Location = rqs });
}
case OnPendingEnum.Synchronous:
{
// Long polling example: hold the connection open and check for completion
// using exponential backoff. Time out after approximately one minute.
int backoff = 250;
while (!await inputBlob.ExistsAsync() && backoff < 64000)
{
_logger.LogInformation("Synchronous mode {RequestId} - retrying in {Backoff} ms", requestId, backoff);
backoff = backoff * 2;
await Task.Delay(backoff);
}
if (await inputBlob.ExistsAsync())
{
_logger.LogInformation("Synchronous mode {RequestId} - completed after {Backoff} ms", requestId, backoff);
return await OnCompleted(OnComplete, inputBlob, requestId, req);
}
else
{
_logger.LogInformation("Synchronous mode {RequestId} - NOT FOUND after timeout {Backoff} ms", requestId, backoff);
return new NotFoundResult();
}
}
default:
{
throw new InvalidOperationException($"Unexpected value: {OnPending}");
}
}
}
}
private async Task<IActionResult> OnCompleted(OnCompleteEnum OnComplete, BlockBlobClient inputBlob, string requestId, HttpRequest req)
{
switch (OnComplete)
{
case OnCompleteEnum.Redirect:
{
// Generate a user delegation SAS URI by using managed identity credentials.
BlobServiceClient blobServiceClient = inputBlob.GetParentBlobContainerClient().GetParentBlobServiceClient();
var userDelegationKey = await blobServiceClient.GetUserDelegationKeyAsync(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddDays(7));
// Return 303 (See Other) to redirect the client to the result resource.
// GenerateUserDelegationSasUri is a custom helper. See the full implementation on GitHub.
req.HttpContext.Response.Headers.Location = GenerateUserDelegationSasUri(inputBlob, userDelegationKey);
return new StatusCodeResult(StatusCodes.Status303SeeOther);
}
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
}
Sonraki Adımlar
- Azure Logic Apps - Zaman uyumsuz istek-yanıt davranışı
- Web API'si tasarımı