Kod izlenecek yol: İşlevler ile sunucusuz uygulama
Sunucusuz modeller, geliştiricilerin kapsamlı kuruluma gerek kalmadan iş mantığına odaklanmasına olanak sağlayarak kodu temel alınan işlem altyapısından soyutlar. Sunucusuz kod maliyetleri azaltır çünkü yalnızca kod yürütme kaynakları ve süresi için ödeme siz ödersiniz.
Sunucusuz olay odaklı model, belirli bir olayın tanımlı bir eylemi tetikleyene durumlara uyar. Örneğin, gelen bir cihaz iletisi almak depolamayı daha sonra kullanmak üzere tetikler veya bir veritabanı güncelleştirmesi biraz daha fazla işleme tetikler.
Microsoft, Azure'daki Azure sunucusuz teknolojilerini keşfetmeye yardımcı olmak için Azure İşlevleri kullanan sunucusuz bir uygulama geliştirmiş ve test Azure İşlevleri. Bu makale, sunucusuz İşlevler çözümü için kodda yol gösterir ve tasarım kararlarını, uygulama ayrıntılarını ve karşılaş olabileceğiniz bazı "satın almaları" açıklar.
Çözümü keşfetme
İki bölümden bir çözüm, kuramsal bir insansız hava aracı ile teslimat sistemini açıklar. Dronlar buluta uçuş durumunu gönderiyor ve bu iletiler daha sonra kullanılmak üzere depolanıyor. Web uygulaması, kullanıcıların cihazların en son durumunu almak için iletileri almalarını sağlar.
Bu çözümün kodunu GitHub.
Bu kılavuzda aşağıdaki teknolojiler hakkında temel bilgi sahibi olduğu kabul edilecektir:
İşlevler veya Event Hubs konusunda uzman olmanız gerekmez ama bunların işlevlerini genel çizgileriyle anlıyor olmalısınız. Başlangıç için kullanabileceğiniz bazı yararlı kaynaklar:
Senaryoyu anlama

Fabrikam bir dron teslim hizmeti için dron filosu yönetiyor. Uygulama iki ana işlevsel alandan oluşuyor:
Olay alımı. Dronlar uçuş sırasında bir bulut uç noktasına durum iletileri gönderiyor. Uygulama bu iletileri alıyor, işliyor ve sonuçları bir arka uç veritabanına (Cosmos DB) yazıyor. Cihazlar iletileri protokol arabirimi (protobuf) biçiminde gönderiyor. Protobuf verimli, kendi kendini açıklayan bir serileştirme biçimidir.
Bu iletiler kısmi güncelleştirmeler içeriyor. Her dron sabit aralıklarla tüm durum alanlarını içeren bir "ana çerçeve" iletisi gönderiyor. Anahtar çerçeveler arasında durum iletileri yalnızca son iletiden bu yana değişen alanları içeriyor. Bu davranış bant genişliğinden ve güçten tasarruf etmesi gereken birçok IoT cihazının tipik davranışıdır.
Web uygulaması. Bir web uygulaması kullanıcıların cihazı bulmasını ve cihazın son bilinen durumunu sorgulamasını sağlıyor. Kullanıcıların uygulamada oturum açmaları ve Azure Active Directory (Azure AD) ile kimlik doğrulaması yapmaları gerekiyor. Uygulama yalnızca uygulamaya erişim yetkisi olan kullanıcıların isteklerine izin veriyor.
Burada web uygulamasının sorgunun sonucunu gösteren bir ekran görüntüsü verilmiştir:

Uygulamayı tasarlama
Fabrikam uygulama iş mantığını gerçekleştirmek için Azure İşlevleri'ni kullanmaya karar vermişti. Azure İşlevleri, bir Hizmet Olarak İşlev" (FaaS) örneğidir. Bu bilgi işlem modelinde işlev, buluta dağıtılan ve barındırma ortamında çalışan bir kod parçasıdır. Bu barındırma ortamı kodu çalıştıran sunucuları tamamen soyutlar.
Neden sunucusuz bir yaklaşım seçmeli?
İşlevler'le sunucusuz bir mimari, olay odaklı mimarinin bir örneğidir. İşlev kodu, işlevin dışında olan bir olay tarafından tetiklenir. Bu durumda insansız hava aracından gelen bir ileti veya istemci uygulamasından gelen HTTP isteği. İşlev uygulamasıyla, tetikleyici için hiçbir kod yazmanız gerekmez. Yalnızca tetikleyiciye yanıt olarak çalıştırılan kodu yazarsınız. Başka bir deyişle mesajlaşma gibi altyapıyla ilgili konuları işleyen birçok kod yazmak yerine iş mantığınıza odaklanabilirsiniz.
Sunucusuz mimari kullanmanın operasyon açısından da bazı avantajları vardır:
- Sunucuları yönetmek gerekmez.
- İşlem kaynakları gerektiğinde dinamik olarak ayrılır.
- Yalnızca kodunuzu yürütmek için kullanılan işlem kaynakları için ücretlendirilirsiniz.
- İşlem kaynakları trafiğe göre isteğe bağlı olarak ölçeklendirilir.
Mimari
Aşağıdaki diyagramda uygulamanın üst düzey mimarisi gösterilir:
Bir veri akışında oklar, Cihazlar'dan Event Hubs ve işlev uygulamasını tetikleyen iletileri gösterir. Uygulamanın bir okunda depolama kuyruğuna gönderilen iletilerden biri okun, diğer ok ise Azure veritabanına Cosmos gösterir. Başka bir veri akışında oklar, istemci web uygulamasının blob depolama statik web barındırmadan statik dosyaları alan istemci web uygulamasını bir CDN. Başka bir ok, istemci HTTP isteğinin API Management. Bu API Management, azure veritabanı verilerini tetikleyen ve okuyan işlev uygulamasını Cosmos gösterir. Azure AD aracılığıyla kimlik doğrulamasını gösteren başka bir ok daha. Kullanıcı azure AD'de de oturum alar.
Olay alımı:
- Dron iletileri Azure Event Hubs tarafından alınır.
- Event Hubs ileti verilerini içeren bir olay akışı oluşturur.
- Bu olaylar, bunları işlemek için bir Azure İşlevleri uygulamasını tetikler.
- Sonuçlar Cosmos DB'de depolanır.
Web uygulaması:
- Statik dosyalar Blob depolama alanından CDN tarafından sunulur.
- Kullanıcı Azure AD'yi kullanarak web uygulamasında oturum açar.
- Azure API Management, REST API uç noktasını kullanıma sunan bir ağ geçidi işlevi görür.
- İstemciden gelen HTTP istekleri Cosmos DB'den okuyan ve sonucu döndüren bir Azure İşlevleri uygulamasını tetikler.
Bu uygulama, yukarıda açıkladığımız iki işlev bloğuna karşılık gelen iki referans mimarisine dayanır:
Bu makaleleri okuyarak üst düzey mimari, çözümde kullanılan Azure hizmetleri ve ayrıca ölçeklenebilirlik, güvenlik ve güvenilirlikle ilgili önemli noktalar hakkında daha fazla bilgi edinebilirsiniz.
Dron telemetri işlevi
Başlangıç olarak Event Hubs'dan gelen dron iletilerinin işlendiği işleve bakalım. İşlev RawTelemetryFunction adlı bir sınıfta tanımlanıyor:
namespace DroneTelemetryFunctionApp
{
public class RawTelemetryFunction
{
private readonly ITelemetryProcessor telemetryProcessor;
private readonly IStateChangeProcessor stateChangeProcessor;
private readonly TelemetryClient telemetryClient;
public RawTelemetryFunction(ITelemetryProcessor telemetryProcessor, IStateChangeProcessor stateChangeProcessor, TelemetryClient telemetryClient)
{
this.telemetryProcessor = telemetryProcessor;
this.stateChangeProcessor = stateChangeProcessor;
this.telemetryClient = telemetryClient;
}
}
...
}
Bu sınıfın çeşitli bağımlılıkları var ve bunlar bağımlılık alımı kullanılarak oluşturucuya alınıyor:
ITelemetryProcessorveIStateChangeProcessorarabirimleri iki yardımcı nesne tanımlıyor. Daha sonra göreceğimiz gibi işin büyük bölümünü bu nesneler yapıyor.TelemetryClient, Application Insights SDK'sının bir parçasıdır. Application Insights'a özel uygulama ölçümleri göndermek için kullanılır.
Daha sonra bağımlılık alımının nasıl yapılandırıldığını göreceğiz. Şimdilik bu bağımlılıkların var olduğunu varsaymamız yeterli.
Event Hubs tetikleyicisini yapılandırma
İşlevdeki mantık RunAsync adlı zaman uyumsuz bir yöntem olarak uygulanır. Yöntem imzası şöyledir:
[FunctionName("RawTelemetryFunction")]
[StorageAccount("DeadLetterStorage")]
public async Task RunAsync(
[EventHubTrigger("%EventHubName%", Connection = "EventHubConnection", ConsumerGroup ="%EventHubConsumerGroup%")]EventData[] messages,
[Queue("deadletterqueue")] IAsyncCollector<DeadLetterMessage> deadLetterMessages,
ILogger logger)
{
// implementation goes here
}
Yöntem aşağıdaki parametreleri alır:
messagesbir dizi olay hub'ı iletisidir.deadLetterMessages, teslim edilmeyen iletileri depolamak için kullanılan bir Azure Depolama Kuyruğudur.logging, uygulama günlüklerine yazmak için bir günlük arabirimi sağlar. Bu günlükler Azure İzleyici'ye gönderilir.
messages parametresindeki EventHubTrigger özniteliği tetikleyiciyi yapılandırır. Özniteliğin özellikleri bir olay hub'ı adı, bağlantı dizesi ve tüketici grubu belirtir. (Tüketici grubu Event Hubs olay akışının yalıtılmış bir görünümüdür. Bu soyutlama aynı olay hub'ının birden çok tüketicisi olmasını sağlar.)
Bazı öznitelik özelliklerindeki yüzde işaretlerine (%) dikkat edin. Bunlar özelliğin bir uygulama ayarının adını belirttiğine ve asıl değerin çalışma zamanında uygulama ayarından alındığına işaret eder. Aksi takdirde, yüzde işaretleri olmazsa özellik değişmez değeri verir.
Connection özelliği özel bir durumdur. Bu özellik her zaman uygulama ayarı adı belirtir, hiçbir zaman değişmez değer belirtmez; dolayısıyla yüzde işareti gerekli değildir. Bu ayrım yapılır çünkü bağlantı dizesi bir gizli dizidir ve kaynak koduna hiçbir zaman iade edilmemesi gerekir.
Diğer iki özellik (olay hub'ı adı ve tüketici grubu) bağlantı dizesi gibi hassas veriler olmasa da, bunları sabit kodlama yerine uygulama ayarlarına koymak yine de daha iyi olacaktır. Bu şekilde, uygulamayı yeniden derlemeden bu özellikler güncelleştirilebilir.
Bu tetikleyiciyi yapılandırma hakkında daha fazla bilgi için bkz. Azure İşlevleri için Azure Event Hubs bağlamaları.
İleti işleme mantığı
Burada toplu iletileri işleyen RawTelemetryFunction.RunAsync yönteminin nasıl uygulandığı gösterilmiştir:
[FunctionName("RawTelemetryFunction")]
[StorageAccount("DeadLetterStorage")]
public async Task RunAsync(
[EventHubTrigger("%EventHubName%", Connection = "EventHubConnection", ConsumerGroup ="%EventHubConsumerGroup%")]EventData[] messages,
[Queue("deadletterqueue")] IAsyncCollector<DeadLetterMessage> deadLetterMessages,
ILogger logger)
{
telemetryClient.GetMetric("EventHubMessageBatchSize").TrackValue(messages.Length);
foreach (var message in messages)
{
DeviceState deviceState = null;
try
{
deviceState = telemetryProcessor.Deserialize(message.Body.Array, logger);
try
{
await stateChangeProcessor.UpdateState(deviceState, logger);
}
catch (Exception ex)
{
logger.LogError(ex, "Error updating status document", deviceState);
await deadLetterMessages.AddAsync(new DeadLetterMessage { Exception = ex, EventData = message, DeviceState = deviceState });
}
}
catch (Exception ex)
{
logger.LogError(ex, "Error deserializing message", message.SystemProperties.PartitionKey, message.SystemProperties.SequenceNumber);
await deadLetterMessages.AddAsync(new DeadLetterMessage { Exception = ex, EventData = message });
}
}
}
İşlev çağrıldığında messages parametresi olay hub'ından bir dizi ileti içerir. İletilerin toplu olarak işlenmesi genel olarak bir kerede bir ileti okumaktan daha iyi performans gösterir. Bununla birlikte işlevin dayanıklı olduğundan ve hatalarla özel durumları düzgün işlediğinden emin olmalısınız. Aksi takdirde, işlev toplu işin ortasında işlenmeyen bir özel durum oluşturursa kalan iletileri kaybedebilirsiniz. Bu konu Hata işleme bölümünde daha ayrıntılı ele alınmıştır.
Ama özel durum işlemeyi yoksayarsanız her iletinin işleme mantığı gayet basittir:
- Cihaz durumu değişikliğini içeren iletiyi seri durumdan çıkarmak için
ITelemetryProcessor.Deserializeyöntemini çağırın. - Durum değişikliğini işlemek için
IStateChangeProcessor.UpdateStateyöntemini çağırın.
Şimdi Deserialize yöntemiyle başlayarak bu iki yönteme daha yakından bakalım.
Deserialize yöntemi
TelemetryProcess.Deserialize yöntemi ileti yükünü içeren bir bayt dizisi alır. Bu yükü seri durumdan çıkarır ve dronun durumunu gösteren bir DeviceState nesnesi döndürür. Durum, yalnızca son bilinen durumdan sonraki değişikliği içeren bir kısmi güncelleştirmeyi temsil ediyor olabilir. Dolayısıyla yöntemin seri durumdan çıkarılmış null alanlarını işlemesi gerekir.
public class TelemetryProcessor : ITelemetryProcessor
{
private readonly ITelemetrySerializer<DroneState> serializer;
public TelemetryProcessor(ITelemetrySerializer<DroneState> serializer)
{
this.serializer = serializer;
}
public DeviceState Deserialize(byte[] payload, ILogger log)
{
DroneState restored = serializer.Deserialize(payload);
log.LogInformation("Deserialize message for device ID {DeviceId}", restored.DeviceId);
var deviceState = new DeviceState();
deviceState.DeviceId = restored.DeviceId;
if (restored.Battery != null)
{
deviceState.Battery = restored.Battery;
}
if (restored.FlightMode != null)
{
deviceState.FlightMode = (int)restored.FlightMode;
}
if (restored.Position != null)
{
deviceState.Latitude = restored.Position.Value.Latitude;
deviceState.Longitude = restored.Position.Value.Longitude;
deviceState.Altitude = restored.Position.Value.Altitude;
}
if (restored.Health != null)
{
deviceState.AccelerometerOK = restored.Health.Value.AccelerometerOK;
deviceState.GyrometerOK = restored.Health.Value.GyrometerOK;
deviceState.MagnetometerOK = restored.Health.Value.MagnetometerOK;
}
return deviceState;
}
}
Bu yöntem ham iletiyi seri durumdan çıkarmak için başka bir yardımcı arabirim (ITelemetrySerializer<T>) kullanır. Sonra da sonuçlar, üzerinde çalışması daha kolay olan POCO modeline dönüştürülür. Bu tasarım mantığı işleme sürecini serileştirmeyi uygulama ayrıntılarından yalıtmaya yardımcı olur. ITelemetrySerializer<T> arabirimi bir paylaşılan kitaplıkta tanımlanır. Bu, cihaz simülatörü tarafından simülasyon cihazı olaylarını oluşturmak ve bunları Event Hubs'a göndermek için de kullanılır.
using System;
namespace Serverless.Serialization
{
public interface ITelemetrySerializer<T>
{
T Deserialize(byte[] message);
ArraySegment<byte> Serialize(T message);
}
}
UpdateState yöntemi
StateChangeProcessor.UpdateState yöntemi durum değişikliklerine uygulanır. Her dronun son bilinen durumu Cosmos DB'de bir JSON belgesi olarak depolanır. Dronlar kısmi güncelleştirmeler gönderdiğinden, uygulama bir güncelleştirme aldığında doğrudan belgenin üzerine yazamaz. Bunun yerine önceki durumu getirmesi, alanları birleştirmesi ve ardından bir upsert işlemi yapması gerekir.
public class StateChangeProcessor : IStateChangeProcessor
{
private IDocumentClient client;
private readonly string cosmosDBDatabase;
private readonly string cosmosDBCollection;
public StateChangeProcessor(IDocumentClient client, IOptions<StateChangeProcessorOptions> options)
{
this.client = client;
this.cosmosDBDatabase = options.Value.COSMOSDB_DATABASE_NAME;
this.cosmosDBCollection = options.Value.COSMOSDB_DATABASE_COL;
}
public async Task<ResourceResponse<Document>> UpdateState(DeviceState source, ILogger log)
{
log.LogInformation("Processing change message for device ID {DeviceId}", source.DeviceId);
DeviceState target = null;
try
{
var response = await client.ReadDocumentAsync(UriFactory.CreateDocumentUri(cosmosDBDatabase, cosmosDBCollection, source.DeviceId),
new RequestOptions { PartitionKey = new PartitionKey(source.DeviceId) });
target = (DeviceState)(dynamic)response.Resource;
// Merge properties
target.Battery = source.Battery ?? target.Battery;
target.FlightMode = source.FlightMode ?? target.FlightMode;
target.Latitude = source.Latitude ?? target.Latitude;
target.Longitude = source.Longitude ?? target.Longitude;
target.Altitude = source.Altitude ?? target.Altitude;
target.AccelerometerOK = source.AccelerometerOK ?? target.AccelerometerOK;
target.GyrometerOK = source.GyrometerOK ?? target.GyrometerOK;
target.MagnetometerOK = source.MagnetometerOK ?? target.MagnetometerOK;
}
catch (DocumentClientException ex)
{
if (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
target = source;
}
}
var collectionLink = UriFactory.CreateDocumentCollectionUri(cosmosDBDatabase, cosmosDBCollection);
return await client.UpsertDocumentAsync(collectionLink, target);
}
}
Bu kod IDocumentClient arabirimini kullanarak Cosmos DB'den belge getirir. Belge varsa, yeni durum değerleri mevcut belgeyle birleştirilir. Aksi takdirde yeni bir belge oluşturulur. Her iki durum da UpsertDocumentAsync yöntemi tarafından işlenir.
Bu kod belgenin zaten mevcut olduğu ve birleştirilebileceği durum için iyileştirilir. Belirli bir drondan ilk telemetri iletisi geldiğinde ReadDocumentAsync yöntemi özel durum oluşturur çünkü söz konu dron için belge yoktur. İlk iletiden sonra belge sağlanacaktır.
Bu sınıfın Cosmos DB için IDocumentClient eklemek üzere bağımlılık eklemeyi ve yapılandırma ayarlarıyla bir IOptions<T> kullandığına dikkat edin. Bağımlılık eklemenin nasıl ayarlandığını daha sonra göreceğiz.
Not
Azure İşlevleri Cosmos DB için çıkış bağlamayı destekler. Bu bağlama işlev uygulamasının hiç kod kullanmadan belgeleri Cosmos DB'ye yazmasını sağlar. Öte yandan çıkış bağlama gereken özel upsert mantığından dolayı buradaki senaryoda çalışmaz.
Hata işleme
Daha önce de belirtildiği gibi RawTelemetryFunction işlev uygulaması iletileri toplu olarak bir döngüde işler. Başka bir deyişle işlevin tüm özel durumları düzgün işlemesi ve toplu işin kalan bölümünü işlemeye devam etmesi gerekir. Aksi takdirde iletiler bırakılabilir.
İleti işlenirken bir özel durumla karşılaşılırsa, işlev iletiyi teslim edilmeyen ileti kuyruğuna yerleştirir:
catch (Exception ex)
{
logger.LogError(ex, "Error deserializing message", message.SystemProperties.PartitionKey, message.SystemProperties.SequenceNumber);
await deadLetterMessages.AddAsync(new DeadLetterMessage { Exception = ex, EventData = message });
}
Teslim edilmeyen ileti kuyruğu, depolama kuyruğuna bir çıkış bağlaması kullanılarak tanımlanır:
[FunctionName("RawTelemetryFunction")]
[StorageAccount("DeadLetterStorage")] // App setting that holds the connection string
public async Task RunAsync(
[EventHubTrigger("%EventHubName%", Connection = "EventHubConnection", ConsumerGroup ="%EventHubConsumerGroup%")]EventData[] messages,
[Queue("deadletterqueue")] IAsyncCollector<DeadLetterMessage> deadLetterMessages, // output binding
ILogger logger)
Burada Queue özniteliği çıkış bağlamasını ve StorageAccount özniteliği de depolama hesabının bağlantı dizesini barındıran uygulama ayarının adını belirtir.
Dağıtım İpucu: Depolama hesabını oluşturan Kaynak Yöneticisi şablonunda, otomatik olarak bağlantı dizesiyle bir uygulama ayarı doldurabilirsiniz. Bu, ListKeys işlevini kullanmaktır.
İşte şablonun kuyruk için depolama hesabını oluşturan bölümü:
{
"name": "[variables('droneTelemetryDeadLetterStorageQueueAccountName')]",
"type": "Microsoft.Storage/storageAccounts",
"location": "[resourceGroup().location]",
"apiVersion": "2017-10-01",
"sku": {
"name": "[parameters('storageAccountType')]"
},
İşte şablonun işlev uygulamasını oluşturan bölümü:
{
"apiVersion": "2015-08-01",
"type": "Microsoft.Web/sites",
"name": "[variables('droneTelemetryFunctionAppName')]",
"location": "[resourceGroup().location]",
"tags": {
"displayName": "Drone Telemetry Function App"
},
"kind": "functionapp",
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
...
],
"properties": {
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
"siteConfig": {
"appSettings": [
{
"name": "DeadLetterStorage",
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('droneTelemetryDeadLetterStorageQueueAccountName'), ';AccountKey=', listKeys(variables('droneTelemetryDeadLetterStorageQueueAccountId'),'2015-05-01-preview').key1)]"
},
...
Bu DeadLetterStorage adlı, değeri listKeys işlevi kullanılarak doldurulan bir uygulama ayarını tanımlar. İşlev uygulaması kaynağının depolama hesabı kaynağına bağımlı yapılması önemlidir (dependsOn öğesine bakın). Bu sayede depolama hesabının önce oluşturulması ve bağlantı dizesinin sağlanması garanti edilir.
Bağımlılık eklemeyi ayarlama
Aşağıdaki kod RawTelemetryFunction işlevi için bağımlılık eklemeyi ayarlar:
[assembly: FunctionsStartup(typeof(DroneTelemetryFunctionApp.Startup))]
namespace DroneTelemetryFunctionApp
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddOptions<StateChangeProcessorOptions>()
.Configure<IConfiguration>((configSection, configuration) =>
{
configuration.Bind(configSection);
});
builder.Services.AddTransient<ITelemetrySerializer<DroneState>, TelemetrySerializer<DroneState>>();
builder.Services.AddTransient<ITelemetryProcessor, TelemetryProcessor>();
builder.Services.AddTransient<IStateChangeProcessor, StateChangeProcessor>();
builder.Services.AddSingleton<IDocumentClient>(ctx => {
var config = ctx.GetService<IConfiguration>();
var cosmosDBEndpoint = config.GetValue<string>("CosmosDBEndpoint");
var cosmosDBKey = config.GetValue<string>("CosmosDBKey");
return new DocumentClient(new Uri(cosmosDBEndpoint), cosmosDBKey);
});
}
}
}
.NET için yazılmış Azure İşlevleri ASP.NET Core bağımlılık ekleme çerçevesini kullanabilir. Temel fikir derlemeniz için bir başlatma yöntemi bildirmenizdir. Yöntem, DI için bağımlılıkları bildirirken kullanılan bir IFunctionsHostBuilder arabirimi alır. Bunu yapmak için Services nesnesinde Add* yöntemini çağırırsınız. Bağımlılığı eklerken yaşam süresini belirtirsiniz:
- Geçici nesneler her istendiğinde oluşturulur.
- Kapsamlı nesneler her işlev yürütmesi için bir kez oluşturulur.
- Tekil nesneler işlev konağının yaşam süresi içinde işlev yürütmeleri arasında yeniden kullanılır.
Bu örnekte TelemetryProcessor ve StateChangeProcessor nesneleri geçici olarak bildirilmiştir. Bu basit, durum bilgisi olmayan hizmetlere uygundur. Öte yandan en iyi performansı elde etmek için DocumentClient sınıfının tekil olması gerekir. Daha fazla bilgi için bkz. Azure Cosmos DB ve .NET için performans ipuçları.
RawTelemetryFunction için yeniden koda dönerseniz, orada DI kurulum kodunda gösterilmeyen başka bir bağımlılık görürsünüz. Bu bağımlılık, uygulama ölçümlerini günlüğe kaydetmek için kullanılan sınıfıdır. İşlevler'in çalışma zamanı bu sınıfı DI kapsayıcısına otomatik olarak kaydeder, bu nedenle sizin açıkça kaydetmeniz gerekmez.
Azure İşlevleri'ndeki DI hakkında daha fazla bilgi edinmek için aşağıdaki makalelere bakın:
DI'de yapılandırma ayarlarını geçirme
Bazen nesnenin bazı yapılandırma değerleriyle başlatılması gerekir. Genellikle bu ayarlar uygulama ayarlarından veya (gizli diziler söz konusu olduğunda) Azure Key Vault'tan gelmelidir.
Bu uygulamada iki örnek vardır. İlkinde, DocumentClient sınıfı bir Cosmos DB hizmeti uç noktası ve anahtarı alır. Bu nesne için uygulama DI kapsayıcısı tarafından çağrılacak bir lambda kaydeder. Bu lambda yapılandırma değerlerini okumak için IConfiguration arabirimini kullanır:
builder.Services.AddSingleton<IDocumentClient>(ctx => {
var config = ctx.GetService<IConfiguration>();
var cosmosDBEndpoint = config.GetValue<string>("CosmosDBEndpoint");
var cosmosDBKey = config.GetValue<string>("CosmosDBKey");
return new DocumentClient(new Uri(cosmosDBEndpoint), cosmosDBKey);
});
İkinci örnek StateChangeProcessor sınıfıdır. Bu nesne için seçenek deseni olarak adlandırılan bir yaklaşım kullanırız. Şöyle çalışır:
Yapılandırma ayarlarınızı içeren bir
Tsınıfı tanımlayın. Bu örnekte, Cosmos DB veritabanı adı ve koleksiyon adı.public class StateChangeProcessorOptions { public string COSMOSDB_DATABASE_NAME { get; set; } public string COSMOSDB_DATABASE_COL { get; set; } }Tsınıfını DI için bir seçenek sınıfı olarak ekleyin.builder.Services.AddOptions<StateChangeProcessorOptions>() .Configure<IConfiguration>((configSection, configuration) => { configuration.Bind(configSection); });Yapılandırılmakta olan sınıfın oluşturucusuna bir
IOptions<T>parametresi ekleyin.public StateChangeProcessor(IDocumentClient client, IOptions<StateChangeProcessorOptions> options)
DI sistemi seçenek sınıfını yapılandırma değerleriyle otomatik olarak doldurur ve bunu oluşturucuya geçirir.
Bu yaklaşımın çeşitli avantajları vardır:
- Sınıf, yapılandırma değerlerinin kaynağından ayrılır.
- Ortam değişkenleri veya JSON yapılandırma dosyaları gibi farklı yapılandırma kaynakları kolayca ayarlanır.
- Birim testi basitleştirilir.
- Yalnızca skaler değerler geçirmekle karşılaştırıldığında hataya daha az eğilimli olan türü güçlü bir şekilde belirtilmiş bir seçenek sınıfı kullanılır.
GetStatus işlevi
Bu çözümdeki diğer İşlevler uygulaması, dronun son bilinen durumunu almak için basit bir REST API kullanır. Bu işlev GetStatusFunction adlı bir sınıfta tanımlanır. Burada işlevin tam kodu verilmiştir:
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
using System.Security.Claims;
using System.Threading.Tasks;
namespace DroneStatusFunctionApp
{
public static class GetStatusFunction
{
public const string GetDeviceStatusRoleName = "GetStatus";
[FunctionName("GetStatusFunction")]
public static IActionResult Run(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = null)]HttpRequest req,
[CosmosDB(
databaseName: "%COSMOSDB_DATABASE_NAME%",
collectionName: "%COSMOSDB_DATABASE_COL%",
ConnectionStringSetting = "COSMOSDB_CONNECTION_STRING",
Id = "{Query.deviceId}",
PartitionKey = "{Query.deviceId}")] dynamic deviceStatus,
ClaimsPrincipal principal,
ILogger log)
{
log.LogInformation("Processing GetStatus request.");
if (!principal.IsAuthorizedByRoles(new[] { GetDeviceStatusRoleName }, log))
{
return new UnauthorizedResult();
}
string deviceId = req.Query["deviceId"];
if (deviceId == null)
{
return new BadRequestObjectResult("Missing DeviceId");
}
if (deviceStatus == null)
{
return new NotFoundResult();
}
else
{
return new OkObjectResult(deviceStatus);
}
}
}
}
Bu işlev HTTP GET isteğini işlemek için bir HTTP tetikleyicisi kullanır. İşlev istenen belgeyi getirmek için Cosmos DB giriş bağlayıcısı kullanır. Bu bağlayıcının, işlev içinde gerçekleştirilen yetkilendirme mantığından önce çalıştırılacağına dikkat edilmelidir. Yetkisiz bir kullanıcı belge isteğinde bulunursa, işlev bağlaması yine de belgeyi getirir. Ardından yetkilendirme kodu bir 401 hatası döndürür, dolayısıyla kullanıcı belgeyi göremez. Bu davranışın kabul edilir olup olmadığı, sizin gereksinimlerinize bağlıdır. Örneğin bu yaklaşım hassas veriler için veri erişiminin denetlenmesini zorlaştırabilir.
Kimlik doğrulaması ve yetkilendirme
Web uygulaması kullanıcıların kimliğini doğrulamak için Azure AD kullanır. Bu tarayıcıda çalışan tek sayfalı bir uygulama (SPA) olduğundan örtük onay verme akışı uygun olur:
- Web uygulaması kullanıcıyı kimlik sağlayıcısına (bu örnekte Azure AD) yönlendirir.
- Kullanıcı kimlik bilgilerini girer.
- Kimlik sağlayıcısı bir erişim belirteciyle geriye web uygulamasına yönlendirir.
- Web uygulaması isteği web API'sine gönderir ve erişim belirtecini Yetkilendirme üst bilgisine ekler.

Sıfır kodla kullanıcıların kimliğini doğrulamak için bir İşlev uygulaması yapılandırılabilir. Daha fazla bilgi için bkz. Azure App Service’de kimlik doğrulama ve yetkilendirme.
Öte yandan yetkilendirme için genel olarak bir iş mantığı gerekir. Azure AD beyana dayalı kimlik doğrulamayı destekler. Bu modelde kullanıcının kimliği kimlik sağlayıcısından gelen bir dizi beyanla gösterilir. Beyan kullanıcı hakkında herhangi bir bilgi parçası, örneğin adı veya e-posta adresi olabilir.
Erişim belirteci, kullanıcı beyanlarının bir alt kümesini içerir. Kullanıcının atandığı uygulama rolleri de bunlar arasındadır.
İşlevin principal parametresi, erişim belirtecindeki beyanları içeren bir principal nesnesidir. Her beyan, beyan türü ile beyan değerinden oluşan bir anahtar/değer çiftidir. Uygulama, isteği yetkilendirmek için bunları kullanır.
Aşağıdaki uzantı yöntemi ClaimsPrincipal nesnesinin bir rol kümesi içerip içermediğini test eder. Belirtilen rollerden herhangi biri eksikse false döndürür. Bu yöntem false döndürürse işlev HTTP 401 (Yetkisiz) döndürür.
namespace DroneStatusFunctionApp
{
public static class ClaimsPrincipalAuthorizationExtensions
{
public static bool IsAuthorizedByRoles(
this ClaimsPrincipal principal,
string[] roles,
ILogger log)
{
var principalRoles = new HashSet<string>(principal.Claims.Where(kvp => kvp.Type == "roles").Select(kvp => kvp.Value));
var missingRoles = roles.Where(r => !principalRoles.Contains(r)).ToArray();
if (missingRoles.Length > 0)
{
log.LogWarning("The principal does not have the required {roles}", string.Join(", ", missingRoles));
return false;
}
return true;
}
}
}
Bu uygulamada kimlik doğrulaması ve yetkilendirme hakkında daha fazla bilgi için referans mimarisinin Güvenlikle ilgili dikkat edilmesi gerekenler bölümüne bakın.
Sonraki adımlar
Bu başvuru çözümünün nasıl çalıştığını öğrendiniz mi, benzer çözümlere yönelik en iyi yöntemleri ve önerileri öğrenin.
- Sunucusuz olay alımı çözümü için bkz. Azure İşlevleri kullanarak sunucusuz olay işleme.
- Sunucusuz web uygulaması için bkz. Azure'da sunucusuz web uygulaması.
Azure İşlevleri yalnızca bir Azure işlem seçeneğidir. İşlem teknolojisi seçme hakkında yardım için bkz. Uygulamanıza bir Azure işlem hizmeti seçme.
İlgili kaynaklar
- Hem şirket içinde hem de bulutta sunucusuz çözümler geliştirme hakkında ayrıntılı bilgi için, Sunucusuz uygulamalar: Mimari, desenler ve Azure uygulaması makalelerini okuyun.
- Olay odaklı mimari stili hakkında daha fazla bilgi edinin.