Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
SI APPLICA A: tutti i livelli di Gestione API
Il servizio Gestione API offre molte capacità per migliorare l'elaborazione di richieste HTTP inviate all'API HTTP. L'esistenza di richieste e risposte è tuttavia temporanea. La richiesta viene effettuata e passa attraverso il servizio Gestione API all'API back-end. L'API elabora la richiesta e una risposta viene restituita al consumer dell'API. Il servizio Gestione API mantiene alcune statistiche importanti sulle API da visualizzare nel dashboard del portale di Azure, ma eventuali altri dettagli verranno eliminati.
Usando i criteri log-to-eventhub nel servizio Gestione API, è possibile inviare qualsiasi dettaglio dalla richiesta e dalla risposta a un Hub eventi di Azure. Esistono diversi motivi per cui potrebbe essere necessario generare eventi dai messaggi HTTP inviati alle API. ad esempio per ottenere audit trail di aggiornamenti, analisi di utilizzo, avvisi relativi alle eccezioni e integrazioni di terze parti.
Questo articolo illustra come acquisire l'intero messaggio di richiesta e risposta HTTP, inviarlo a un hub eventi, quindi inoltrare il messaggio a un servizio di terze parti che fornisce servizi di registrazione e monitoraggio HTTP.
Perché inviare dal servizio Gestione API?
È possibile scrivere middleware HTTP che può essere collegato ai framework API HTTP per acquisire richieste HTTP e risposte e inserirli nei sistemi di registrazione e monitoraggio. Lo svantaggio di questo approccio è che il middleware HTTP deve essere integrato nell'API back-end e deve corrispondere alla piattaforma API. Se sono disponibili più API, ognuna dovrà distribuire il middleware. Spesso esistono motivi per cui non è possibile aggiornare le API back-end.
L'uso del servizio Gestione API di Azure per l'integrazione con l'infrastruttura di registrazione offre una soluzione centralizzata e indipendente dalla piattaforma, È anche scalabile, in parte grazie alle funzionalità di replica geografica di Gestione API di Azure.
Perché inviare a un hub eventi?
È ragionevole chiedere: perché creare un criterio specifico per Hub eventi di Azure? Esistono molte posizioni diverse in cui è possibile registrare le richieste. L'invio delle richieste direttamente alla destinazione finale Questa è un'opzione. Tuttavia, quando si effettuano richieste di registrazione da un servizio gestione API, è necessario considerare il modo in cui la registrazione dei messaggi influisce sulle prestazioni dell'API. Gli incrementi graduali nel carico possono essere gestiti da istanze a disponibilità crescente dei componenti di sistema o sfruttando i vantaggi della replica geografica. I brevi picchi di traffico possono tuttavia provocare un ritardo delle richieste se le richieste all'infrastruttura di registrazione iniziano a rallentare a causa del carico.
Azure Event Hubs è progettato per l'ingresso di enormi volumi di dati, con capacità di gestire un numero di eventi decisamente più elevato rispetto a quello che la maggior parte delle API può elaborare in termini di richieste HTTP. L'Hub eventi è analogo a un buffer avanzato tra il servizio di gestione API e l'infrastruttura che archivia ed elabora i messaggi. Ciò assicura che le prestazioni dell'API non saranno danneggiate dall'infrastruttura di registrazione.
Dopo che i dati sono passati a un hub eventi, vengono mantenuti e attendono che i consumatori dell'hub eventi li elaborino. L'hub eventi prevede requisiti specifici per la modalità di elaborazione, ma è importante assicurarsi che il messaggio venga recapitato correttamente.
Gli hub eventi possono effettuare lo streaming di eventi a più gruppi di consumer. Ciò consente l'elaborazione degli eventi da parte di sistemi diversi. Questo supporta molti scenari di integrazione senza inserire più ritardi nell'elaborazione della richiesta API all'interno del servizio Gestione API, perché deve essere generato un solo evento.
Criteri per l'invio di messaggi application/HTTP
Un hub eventi accetta i dati evento sotto forma di semplice stringa. I contenuti della stringa possono essere definiti dall'utente. Per poter creare un pacchetto di una richiesta HTTP e inviarla a Hub eventi di Azure, è necessario formattare la stringa con le informazioni sulla richiesta o sulla risposta. In situazioni come questa, se esiste un formato esistente che è possibile riutilizzare, potrebbe non essere necessario scrivere codice di analisi personalizzato. Inizialmente, è consigliabile usare har per inviare richieste e risposte HTTP. ma questo formato è ottimizzato per l'archiviazione di una sequenza di richieste HTTP in un formato basato su JSON. Contiene numerosi elementi obbligatori che aggiungono una complessità superflua per lo scenario del passaggio del messaggio HTTP in rete.
Un'opzione alternativa è usare il application/http tipo di media come descritto nella specifica HTTP RFC 7230. Questo tipo di supporto usa lo stesso formato usato per inviare effettivamente messaggi HTTP in transito, ma l'intero messaggio può essere inserito nel corpo di un'altra richiesta HTTP. In questo caso, si usa semplicemente il corpo come messaggio per l'invio a Hub eventi. È praticamente disponibile un parser presente nelle librerie client di Microsoft API Web ASP.NET 2.2 che possono analizzare questo formato e convertirlo in oggetti HttpRequestMessage e HttpResponseMessage nativi.
Per potere creare questo messaggio, è necessario sfruttare le espressioni di criteri basate su C# disponibili in Gestione API di Azure. Ecco la politica, che invia un messaggio di richiesta HTTP ad Azure Event Hubs.
<log-to-eventhub logger-id="myapilogger" partition-id="0">
@{
var requestLine = string.Format("{0} {1} HTTP/1.1\r\n",
context.Request.Method,
context.Request.Url.Path + context.Request.Url.QueryString);
var body = context.Request.Body?.As<string>(true);
if (body != null && body.Length > 1024)
{
body = body.Substring(0, 1024);
}
var headers = context.Request.Headers
.Where(h => h.Key != "Authorization" && h.Key != "Ocp-Apim-Subscription-Key")
.Select(h => string.Format("{0}: {1}", h.Key, String.Join(", ", h.Value)))
.ToArray<string>();
var headerString = (headers.Any()) ? string.Join("\r\n", headers) + "\r\n" : string.Empty;
return "request:" + context.Variables["message-id"] + "\n"
+ requestLine + headerString + "\r\n" + body;
}
</log-to-eventhub>
Dichiarazione di criteri
È necessario evidenziare alcuni aspetti di questa espressione di criteri. Il log-to-eventhub criterio ha un attributo denominato logger-id, che fa riferimento al nome del logger creato all'interno del servizio Gestione API. Per informazioni dettagliate su come configurare un logger di hub eventi nel servizio Gestione API, vedere il documento Come registrare gli eventi in Hub eventi di Azure in Gestione API di Azure. Il secondo attributo è un parametro opzionale che indica all'Hub eventi la partizione in cui archiviare il messaggio. Hub eventi usa le partizioni per abilitare la scalabilità e richiede almeno due partizioni. Il recapito ordinato dei messaggi è garantito solo entro una partizione. Se non si indica all'Hub eventi di Azure la partizione in cui inserire il messaggio, verrà usato un algoritmo round-robin per distribuire il carico. Tuttavia, questo potrebbe causare l'elaborazione di alcuni messaggi non in ordine.
Partizioni
Per garantire che i messaggi vengano recapitati ai consumer in ordine e sfruttare la funzionalità di distribuzione del carico delle partizioni, è possibile inviare messaggi di richiesta HTTP a una partizione e messaggi di risposta HTTP a una seconda partizione. Ciò garantisce una distribuzione di carico uniforme e può garantire che tutte le richieste e tutte le risposte vengano utilizzate in ordine. È possibile che una risposta venga usata prima della richiesta corrispondente, ma questo non è un problema perché è presente un meccanismo diverso per correlare le richieste alle risposte e sappiamo che le richieste vengono sempre prima delle risposte.
Payload HTTP
Dopo la compilazione di requestLine, verificare se il corpo della richiesta deve essere troncato. Il corpo della richiesta viene troncato solo a 1024. Questo potrebbe essere aumentato; Tuttavia, i singoli messaggi dell'hub eventi sono limitati a 256 KB, quindi è probabile che alcuni corpi di messaggi HTTP non si adattino a un singolo messaggio. Quando si esegue la registrazione e l'analisi, è possibile derivare una quantità significativa di informazioni dalla riga e dalle intestazioni della richiesta HTTP. Inoltre, molte richieste API restituiscono solo dati di piccole dimensioni e quindi la perdita di valore delle informazioni troncando dati di grandi dimensioni è piuttosto minima rispetto alla riduzione dei costi di trasferimento, elaborazione e archiviazione per mantenere tutti i contenuti.
Una nota finale sull'elaborazione del corpo è che è necessario passare true al metodo As<string>(), perché leggiamo il contenuto del corpo, ma vogliamo che l'API back-end sia in grado di leggere il corpo. Se si passa true a questo metodo, il corpo verrà sottoposto a buffering, in modo che sia possibile leggerlo una seconda volta. Questo è importante se si dispone di un'API che carica file di grandi dimensioni o usa il polling lungo. In questi casi è consigliabile evitare completamente la lettura del corpo.
Intestazioni HTTP
Le intestazioni HTTP possono essere trasferite nel formato del messaggio sotto forma di semplice coppia chiave/valore. È stato scelto di rimuovere determinati campi sensibili alla sicurezza per evitare inutilmente la perdita di informazioni sulle credenziali. È poco probabile che le chiavi API e altre credenziali vengano usate per finalità analitiche. Se si vuole analizzare l'utente e il prodotto specifico in uso, è possibile ottenerlo dall'oggetto context e aggiungerlo al messaggio.
Metadati del messaggio
Durante la creazione del messaggio completo da inviare all'hub eventi, la prima riga non fa effettivamente parte del messaggio application/http. La prima riga include metadati aggiuntivi che indicano se il messaggio è un messaggio di richiesta o di risposta e l'ID del messaggio, che viene usato per correlare le richieste e l risposte. L'ID del messaggio viene creato mediante un altro criterio, analogo al seguente:
<set-variable name="message-id" value="@(Guid.NewGuid())" />
È possibile creare il messaggio di richiesta, archiviare tale messaggio in una variabile fino a quando non viene restituita la risposta e quindi inviare la richiesta e la risposta come singolo messaggio. Tuttavia, inviando la richiesta e la risposta in modo indipendente e usando un message-id per correlare i due, si ottiene una maggiore flessibilità nelle dimensioni del messaggio, la possibilità di sfruttare i vantaggi di più partizioni mantenendo l'ordine dei messaggi e un arrivo prima delle richieste nel dashboard di registrazione. Potrebbero esserci anche alcuni scenari in cui una risposta valida non viene mai inviata all'hub eventi (probabilmente a causa di un errore di richiesta irreversibile nel servizio Gestione API), ma è ancora presente un record della richiesta.
Il criterio per l'invio del messaggio di risposta HTTP è simile alla richiesta, quindi la configurazione completa del criterio sarà analoga alla seguente:
<policies>
<inbound>
<set-variable name="message-id" value="@(Guid.NewGuid())" />
<log-to-eventhub logger-id="myapilogger" partition-id="0">
@{
var requestLine = string.Format("{0} {1} HTTP/1.1\r\n",
context.Request.Method,
context.Request.Url.Path + context.Request.Url.QueryString);
var body = context.Request.Body?.As<string>(true);
if (body != null && body.Length > 1024)
{
body = body.Substring(0, 1024);
}
var headers = context.Request.Headers
.Where(h => h.Key != "Authorization" && h.Key != "Ocp-Apim-Subscription-Key")
.Select(h => string.Format("{0}: {1}", h.Key, String.Join(", ", h.Value)))
.ToArray<string>();
var headerString = (headers.Any()) ? string.Join("\r\n", headers) + "\r\n" : string.Empty;
return "request:" + context.Variables["message-id"] + "\n"
+ requestLine + headerString + "\r\n" + body;
}
</log-to-eventhub>
</inbound>
<backend>
<forward-request follow-redirects="true" />
</backend>
<outbound>
<log-to-eventhub logger-id="myapilogger" partition-id="1">
@{
var statusLine = string.Format("HTTP/1.1 {0} {1}\r\n",
context.Response.StatusCode,
context.Response.StatusReason);
var body = context.Response.Body?.As<string>(true);
if (body != null && body.Length > 1024)
{
body = body.Substring(0, 1024);
}
var headers = context.Response.Headers
.Select(h => string.Format("{0}: {1}", h.Key, String.Join(", ", h.Value)))
.ToArray<string>();
var headerString = (headers.Any()) ? string.Join("\r\n", headers) + "\r\n" : string.Empty;
return "response:" + context.Variables["message-id"] + "\n"
+ statusLine + headerString + "\r\n" + body;
}
</log-to-eventhub>
</outbound>
</policies>
La politica set-variable crea un valore accessibile sia dalla politica log-to-eventhub nella sezione <inbound> che nella sezione <outbound>.
Ricezione di eventi dall'Hub eventi
Gli eventi di Hub eventi di Azure vengono ricevuti usando il protocollo AMQP. Il team del bus di servizio Microsoft ha reso disponibili le librerie client per semplificare l'utilizzo degli eventi. Sono supportati due approcci diversi, ovvero la modalità consumer diretto e l'uso della classe EventProcessorHost. Gli esempi relativi a questi due approcci sono disponibili nel repository degli esempi Hub eventi. In breve: Direct Consumer offre il controllo completo e EventProcessorHost esegue alcune operazioni di base ma include alcune ipotesi in merito al modo in cui gli eventi vengono elaborati.
EventProcessorHost
In questo esempio viene usato EventProcessorHost per semplicità, ma potrebbe non essere la scelta migliore per questo particolare scenario.
EventProcessorHost esegue le operazioni necessarie per gestire automaticamente eventuali errori di threading relativi a una classe specifica del processore di eventi. In questo scenario, tuttavia, il messaggio viene convertito in un altro formato e passato a un altro servizio usando un metodo asincrono. Non è necessario aggiornare lo stato condiviso e quindi non c'è rischio di problemi di threading. Nella maggior parte degli scenari la scelta migliore è probabilmente costituita da EventProcessorHost , che è sicuramente l'opzione più semplice.
IEventProcessor
Il concetto centrale dell'uso di EventProcessorHost consiste nel creare un'implementazione dell'interfaccia IEventProcessor, che include il metodo ProcessEventAsync. Ecco l'essenza di questo metodo:
async Task IEventProcessor.ProcessEventsAsync(PartitionContext context, IEnumerable<EventData> messages)
{
foreach (EventData eventData in messages)
{
_Logger.LogInfo(string.Format("Event received from partition: {0} - {1}", context.Lease.PartitionId,eventData.PartitionKey));
try
{
var httpMessage = HttpMessage.Parse(eventData.GetBodyStream());
await _MessageContentProcessor.ProcessHttpMessage(httpMessage);
}
catch (Exception ex)
{
_Logger.LogError(ex.Message);
}
}
... checkpointing code snipped ...
}
Un elenco di oggetti EventData viene passato al metodo e viene eseguita l'iterazione dell'elenco. I byte di ogni metodo vengono analizzati in un oggetto HttpMessage e questo oggetto viene passato a un'istanza di IHttpMessageProcessor.
HttpMessage
L'istanza HttpMessage contiene tre parti di dati:
public class HttpMessage
{
public Guid MessageId { get; set; }
public bool IsRequest { get; set; }
public HttpRequestMessage HttpRequestMessage { get; set; }
public HttpResponseMessage HttpResponseMessage { get; set; }
... parsing code snipped ...
}
L'istanza HttpMessage contiene un GUID MessageId che consente di connettere la richiesta HTTP alla risposta HTTP corrispondente e un valore booleano che indica se l'oggetto contiene un'istanza di HttpRequestMessage e HttpResponseMessage. Usando le classi HTTP predefinite da System.Net.Http, è possibile sfruttare i vantaggi del codice di analisi application/http incluso in System.Net.Http.Formatting.
IHttpMessageProcessor
L'istanza HttpMessage viene quindi inoltrata all'implementazione di IHttpMessageProcessor, che è un'interfaccia creata per disaccoppiare la ricezione e l'interpretazione dell'evento dagli Hub eventi di Azure e l'effettiva elaborazione dell'evento.
Inoltro del messaggio HTTP
Abbiamo deciso di inoltrare la richiesta HTTP a Analisi API Moesif per questo esempio. Moesif è un servizio basato sul cloud specializzato in analisi e debug HTTP. Hanno un livello gratuito, quindi è facile da provare. Moesif consente di visualizzare le richieste HTTP in tempo reale attraverso il servizio Gestione API.
L'implementazione IHttpMessageProcessor ha un aspetto analogo al seguente,
public class MoesifHttpMessageProcessor : IHttpMessageProcessor
{
private readonly string RequestTimeName = "MoRequestTime";
private MoesifApiClient _MoesifClient;
private ILogger _Logger;
private string _SessionTokenKey;
private string _ApiVersion;
public MoesifHttpMessageProcessor(ILogger logger)
{
var appId = Environment.GetEnvironmentVariable("APIMEVENTS-MOESIF-APP-ID", EnvironmentVariableTarget.Process);
_MoesifClient = new MoesifApiClient(appId);
_SessionTokenKey = Environment.GetEnvironmentVariable("APIMEVENTS-MOESIF-SESSION-TOKEN", EnvironmentVariableTarget.Process);
_ApiVersion = Environment.GetEnvironmentVariable("APIMEVENTS-MOESIF-API-VERSION", EnvironmentVariableTarget.Process);
_Logger = logger;
}
public async Task ProcessHttpMessage(HttpMessage message)
{
if (message.IsRequest)
{
message.HttpRequestMessage.Properties.Add(RequestTimeName, DateTime.UtcNow);
return;
}
EventRequestModel moesifRequest = new EventRequestModel()
{
Time = (DateTime) message.HttpRequestMessage.Properties[RequestTimeName],
Uri = message.HttpRequestMessage.RequestUri.OriginalString,
Verb = message.HttpRequestMessage.Method.ToString(),
Headers = ToHeaders(message.HttpRequestMessage.Headers),
ApiVersion = _ApiVersion,
IpAddress = null,
Body = message.HttpRequestMessage.Content != null ? System.Convert.ToBase64String(await message.HttpRequestMessage.Content.ReadAsByteArrayAsync()) : null,
TransferEncoding = "base64"
};
EventResponseModel moesifResponse = new EventResponseModel()
{
Time = DateTime.UtcNow,
Status = (int) message.HttpResponseMessage.StatusCode,
IpAddress = Environment.MachineName,
Headers = ToHeaders(message.HttpResponseMessage.Headers),
Body = message.HttpResponseMessage.Content != null ? System.Convert.ToBase64String(await message.HttpResponseMessage.Content.ReadAsByteArrayAsync()) : null,
TransferEncoding = "base64"
};
Dictionary<string, string> metadata = new Dictionary<string, string>();
metadata.Add("ApimMessageId", message.MessageId.ToString());
EventModel moesifEvent = new EventModel()
{
Request = moesifRequest,
Response = moesifResponse,
SessionToken = _SessionTokenKey != null ? message.HttpRequestMessage.Headers.GetValues(_SessionTokenKey).FirstOrDefault() : null,
Tags = null,
UserId = null,
Metadata = metadata
};
Dictionary<string, string> response = await _MoesifClient.Api.CreateEventAsync(moesifEvent);
_Logger.LogDebug("Message forwarded to Moesif");
}
private static Dictionary<string, string> ToHeaders(HttpHeaders headers)
{
IEnumerable<KeyValuePair<string, IEnumerable<string>>> enumerable = headers.GetEnumerator().ToEnumerable();
return enumerable.ToDictionary(p => p.Key, p => p.Value.GetEnumerator()
.ToEnumerable()
.ToList()
.Aggregate((i, j) => i + ", " + j));
}
}
MoesifHttpMessageProcessor si avvale di una libreria di API C# per Moesif che facilita il push di dati di eventi HTTP nel proprio servizio. Per inviare dati HTTP all'API dell'agente di raccolta Moesif, sono necessari un account e un ID applicazione. Per ottenere un ID applicazione Moesif, creare un account nel sito Web di Moesif e quindi passare al menu in alto a destra e selezionare Installazione app.
Esempio completo
Il codice sorgente e i test per l'esempio sono disponibili su GitHub. Per eseguire l'esempio, è necessario disporre di un servizio Gestione API, un hub eventi connesso e un account di archiviazione.
L'esempio è solo una semplice applicazione console che ascolta gli eventi provenienti dall'hub eventi, li converte in un oggetto Moesif EventRequestModel e EventResponseModel quindi li inoltra all'API dell'agente di raccolta Moesif.
Nell'immagine animata seguente è possibile visualizzare una richiesta inviata a un'API nel portale per sviluppatori, l'applicazione console che mostra il messaggio ricevuto, elaborato e inoltrato e quindi la richiesta e la risposta visualizzati nel flusso di eventi.
Summary
Il servizio Gestione API di Azure è la posizione ideale per acquisire il traffico HTTP verso e dalle API. Hub eventi di Azure è una soluzione a scalabilità elevata e costi ridotti per l'acquisizione e l'inserimento del traffico in sistemi di elaborazione secondari per operazioni di registrazione e monitoraggio e per altre analisi avanzate. Per connettersi a sistemi di monitoraggio del traffico di terze parti come Moesif basta scrivere qualche decina di righe di codice.
Contenuti correlati
- Altre informazioni sull'Hub eventi di Azure
- Altre informazioni sull'integrazione di Gestione API e Hub eventi