Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
DOTYCZY: Wszystkie warstwy usługi API Management
Usługa API Management udostępnia wiele funkcji w celu zwiększenia przetwarzania żądań HTTP wysyłanych do interfejsu API HTTP. Jednak istnienie żądań i odpowiedzi jest przejściowe. Żądanie jest wykonywane i przepływa przez usługę API Management do interfejsu API zaplecza. Interfejs API przetwarza żądanie i odpowiedź przepływa z powrotem do odbiorcy interfejsu API. Usługa zarządzania interfejsami API przechowuje ważne statystyki dotyczące interfejsów API do wyświetlania na pulpicie nawigacyjnym w portalu Azure, ale poza tym szczegóły nie są już dostępne.
Korzystając z log-to-eventhub polityk w usłudze API Management, możesz wysłać wszelkie szczegóły z żądania i odpowiedzi do Azure Event Hubs. Istnieje kilka powodów, dla których możesz chcieć wygenerować zdarzenia z komunikatów HTTP wysyłanych do interfejsów API. Niektóre przykłady obejmują ścieżkę audytową aktualizacji, analizę użycia, powiadomienia o wyjątkach i integracje zewnętrzne.
W tym artykule pokazano, jak przechwycić całe żądanie HTTP i komunikat odpowiedzi, wysłać go do centrum zdarzeń, a następnie przekazać ten komunikat do usługi innej firmy, która zapewnia usługi rejestrowania i monitorowania HTTP.
Dlaczego warto wysyłać z usługi API Management?
Możesz napisać oprogramowanie pośredniczące HTTP, które może podłączyć się do struktur interfejsu API HTTP w celu przechwytywania żądań HTTP i odpowiedzi oraz podawania ich do systemów rejestrowania i monitorowania. Wadą tego podejścia jest to, że oprogramowanie pośredniczące HTTP musi zostać zintegrowane z interfejsem API zaplecza i musi być zgodne z platformą interfejsu API. Jeśli istnieje wiele interfejsów API, każdy z nich musi wdrożyć oprogramowanie pośredniczące. Często istnieją powody, dla których nie można zaktualizować interfejsów API zaplecza.
Korzystanie z usługi Azure API Management do integracji z infrastrukturą rejestrowania zapewnia scentralizowane i niezależne od platformy rozwiązanie. Jest również skalowalna, częściowo ze względu na możliwości replikacji geograficznej usługi Azure API Management.
Dlaczego warto wysłać do centrum zdarzeń?
Warto zapytać: dlaczego warto utworzyć zasady specyficzne dla usługi Azure Event Hubs? Istnieje wiele różnych miejsc, w których można rejestrować żądania. Dlaczego nie tylko wysyłać żądania bezpośrednio do końcowego miejsca docelowego? Jest to opcja. Jednak podczas rejestrowania żądań z usługi API Management należy wziąć pod uwagę, jak komunikaty rejestrowania wpływają na wydajność interfejsu API. Stopniowy wzrost obciążenia można obsłużyć przez zwiększenie dostępnych wystąpień składników systemu lub wykorzystanie replikacji geograficznej. Jednak krótkie skoki ruchu mogą spowodować opóźnienie żądań, jeśli żądania związane z infrastrukturą rejestrowania spowalniają pod wpływem obciążenia.
Usługa Azure Event Hubs jest przeznaczona do ściągnięcia ogromnych ilości danych z pojemnością do obsługi znacznie większej liczby zdarzeń niż liczba żądań HTTP, które przetwarza większość interfejsów API. Centrum zdarzeń działa jako rodzaj zaawansowanego buforu między usługą API Management a infrastrukturą, która przechowuje i przetwarza komunikaty. Gwarantuje to, że wydajność interfejsu API nie ucierpi z powodu infrastruktury rejestrowania.
Po przekazaniu danych do centrum zdarzeń, są one przechowywane i czekają na przetworzenie przez użytkowników centrum zdarzeń. Hub zdarzeń nie interesuje się, jak wiadomość jest przetwarzana, chodzi tylko o to, aby upewnić się, że komunikat został pomyślnie dostarczony.
Usługa Event Hubs umożliwia przesyłanie strumieniowe zdarzeń do wielu grup odbiorców. Dzięki temu zdarzenia mogą być przetwarzane przez różne systemy. Obsługuje to wiele scenariuszy integracji bez wprowadzania większej liczby opóźnień w przetwarzaniu żądania interfejsu API w usłudze API Management, ponieważ należy wygenerować tylko jedno zdarzenie.
Zasady wysyłania komunikatów aplikacyjnych/HTTP
Centrum zdarzeń akceptuje dane zdarzeń jako prosty ciąg. Zawartość tego ciągu zależy od Ciebie. Aby móc spakować żądanie HTTP i wysłać je do usługi Azure Event Hubs, musisz sformatować ciąg przy użyciu informacji o żądaniu lub odpowiedzi. W takich sytuacjach, jeśli istnieje istniejący format, który można użyć ponownie, być może nie trzeba będzie pisać własnego kodu analizy. Początkowo można rozważyć użycie narzędzia HAR do wysyłania żądań HTTP i odpowiedzi. Jednak ten format jest zoptymalizowany pod kątem przechowywania sekwencji żądań HTTP w formacie opartym na formacie JSON. Zawierał wiele obowiązkowych elementów, które dodały niepotrzebną złożoność scenariusza przekazywania komunikatu HTTP przez przewody.
Alternatywną opcją jest użycie typu nośnika application/http zgodnie z opisem w specyfikacji HTTP RFC 7230. Ten typ nośnika używa dokładnie tego samego formatu, który jest używany do rzeczywistego wysyłania komunikatów HTTP za pośrednictwem przewodu, ale cały komunikat można umieścić w treści innego żądania HTTP. W naszym przypadku używamy treści jako komunikatu do wysyłania do usługi Event Hubs. Na szczęście, w bibliotekach Microsoft ASP.NET Web API 2.2 Client znajduje się analizator, który może analizować ten format i konwertować go na obiekty natywne HttpRequestMessage i HttpResponseMessage.
Aby móc utworzyć ten komunikat, musimy skorzystać z wyrażeń zasad opartych na języku C# w usłudze Azure API Management. Oto polityka, która wysyła żądanie HTTP do usługi 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>
Deklaracja zasad
Istnieje kilka konkretnych kwestii, które warto wspomnieć o tym wyrażeniu zasad. Zasady log-to-eventhub mają atrybut o nazwie logger-id, który odwołuje się do nazwy rejestratora utworzonego w usłudze API Management. Szczegółowe informacje na temat konfigurowania rejestratora centrum zdarzeń można znaleźć w usłudze API Management w dokumencie Jak rejestrować zdarzenia w usłudze Azure Event Hubs w usłudze Azure API Management. Drugi atrybut to opcjonalny parametr, który instruuje usługę Event Hubs, w której partycji ma być przechowywany komunikat. Usługa Event Hubs używa partycji, aby umożliwić skalowalność i wymaga co najmniej dwóch. Uporządkowana dostawa komunikatów jest gwarantowana jedynie w obrębie partycji. Jeśli nie poinstruujemy usługi Azure Event Hubs, w której partycji ma być umieszczany komunikat, używa algorytmu działania okrężnego do dystrybucji obciążenia. Może to jednak spowodować przetworzenie niektórych komunikatów poza kolejnością.
Partycje
Aby zapewnić, że komunikaty są dostarczane do użytkowników w kolejności i korzystają z możliwości dystrybucji obciążenia partycji, możemy wysyłać komunikaty żądań HTTP do jednej partycji i komunikatów odpowiedzi HTTP do drugiej partycji. Zapewnia to równomierną dystrybucję obciążenia i gwarantuje, że wszystkie żądania oraz odpowiedzi będą przetwarzane w kolejności. Istnieje możliwość, aby odpowiedź była otrzymana przed odpowiednim żądaniem, ale nie jest to problem, ponieważ mamy inny mechanizm korelowania żądań z odpowiedziami i jest wiadome, że żądania są składane przed odpowiedziami.
Ładunki HTTP
Po utworzeniu elementu requestLinesprawdź, czy treść żądania powinna zostać obcięta. Treść żądania jest obcięta tylko do 1024. Można to zwiększyć; jednak pojedyncze komunikaty centrum zdarzeń są ograniczone do 256 KB, więc prawdopodobnie niektóre treści komunikatów HTTP nie będą mieścić się w jednym komunikacie. Podczas rejestrowania i analizy można uzyskać znaczną ilość informacji tylko z wiersza i nagłówków żądania HTTP. Ponadto wiele żądań API zwraca tylko małe treści, a więc utrata wartości informacji przez obcinanie dużych treści jest dość minimalna w porównaniu do zmniejszenia kosztów transferu, przetwarzania i magazynowania, aby przechowywać całą zawartość.
Jedną z ostatnich uwag dotyczących przetwarzania treści jest to, że musimy przekazać true do As<string>() metody, ponieważ odczytujemy zawartość treści, ale chcemy, aby interfejs API zaplecza mógł odczytać treść. Przekazując wartość true do tej metody, powodujemy buforację treści, aby można było ją odczytać po raz drugi. Jest to ważne, jeśli masz interfejs API, który przekazuje duże pliki lub używa długiego sondowania. W takich przypadkach najlepiej całkowicie unikać analizowania zawartości.
Nagłówki HTTP
Nagłówki HTTP można przenosić do formatu komunikatu w prostym formacie pary klucz/wartość. Zdecydowaliśmy się usunąć niektóre pola związane z bezpieczeństwem, aby zapobiec niepotrzebnemu wyciekowi informacji o poświadczeniach. Jest mało prawdopodobne, aby klucze interfejsu API i inne poświadczenia były używane do celów analitycznych. Jeśli chcemy przeanalizować użytkownika i konkretny produkt, którego używają, możemy pobrać go z context obiektu i dodać go do komunikatu.
Metadane komunikatu
Podczas kompilowania kompletnego komunikatu do wysłania do centrum zdarzeń linia frontu nie jest w rzeczywistości częścią komunikatu application/http . Pierwszy wiersz to dodatkowe metadane składające się z tego, czy komunikat jest żądaniem, czy komunikatem odpowiedzi i identyfikatorem komunikatu, który służy do korelowania żądań z odpowiedziami. Identyfikator komunikatu jest tworzony przy użyciu innych zasad, które wyglądają następująco:
<set-variable name="message-id" value="@(Guid.NewGuid())" />
Możemy utworzyć komunikat żądania, zapisać go w zmiennej do momentu zwrócenia odpowiedzi, a następnie wysłać żądanie i odpowiedź jako pojedynczy komunikat. Jednak wysyłając żądanie i odpowiedź niezależnie i używając message-id w celu powiązania ich ze sobą, uzyskujemy nieco większą elastyczność w rozmiarze komunikatu, możliwość korzystania z wielu partycji przy zachowaniu kolejności komunikatów oraz szybsze dotarcie żądania do naszego pulpitu nawigacyjnego rejestrowania. Mogą również wystąpić pewne scenariusze, w których prawidłowa odpowiedź nigdy nie jest wysyłana do centrum zdarzeń (prawdopodobnie z powodu błędu żądania krytycznego w usłudze API Management), ale nadal mamy rekord żądania.
Zasady wysyłania komunikatu HTTP odpowiedzi wyglądają podobnie do żądania, więc kompletna konfiguracja zasad wygląda następująco:
<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>
Polityka set-variable tworzy wartość dostępną zarówno dla polityki log-to-eventhub w sekcji <inbound>, jak i w sekcji <outbound>.
Odbieranie zdarzeń z usługi Event Hubs
Zdarzenia z usługi Azure Event Hubs są odbierane przy użyciu protokołu AMQP. Zespół Microsoft Service Bus udostępnił biblioteki klienckie, aby ułatwić obsługę zdarzeń. Obsługiwane są dwa różne podejścia: jedno polega na byciu bezpośrednim konsumentem, a drugie na korzystaniu z klasy EventProcessorHost. Przykłady tych dwóch podejść można znaleźć w repozytorium przykładów usługi Event Hubs. Krótsza wersja różnic: Direct Consumer daje Ci pełną kontrolę, a EventProcessorHost zajmuje się częścią infrastruktury, ale opiera się na pewnych założeniach dotyczących sposobu przetwarzania tych zdarzeń.
EventProcessorHost
W tym przykładzie używamy EventProcessorHost dla uproszczenia, jednak EventProcessorHost może nie być najlepszym wyborem dla tego konkretnego scenariusza.
EventProcessorHost Wykonuje ciężką pracę, upewniając się, że nie musisz martwić się o problemy wątkowe w określonej klasie procesora zdarzeń. Jednak w naszym scenariuszu konwertujemy komunikat na inny format i przekazujemy go do innej usługi przy użyciu metody asynchronicznej. Nie ma potrzeby aktualizowania stanu udostępnionego i dlatego nie ma ryzyka problemów z wątkami. W przypadku większości scenariuszy EventProcessorHost jest prawdopodobnie najlepszym wyborem i z pewnością jest to łatwiejsza opcja.
IEventProcessor
Centralną koncepcją podczas używania EventProcessorHost jest utworzenie implementacji interfejsu IEventProcessor , która zawiera metodę ProcessEventAsync. Oto istota tej metody:
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 ...
}
Lista obiektów EventData jest przekazywana do metody i iterujemy przez tę listę. Bajty każdej z metod są przetwarzane na obiekt HttpMessage, a ten obiekt jest przekazywany do instancji IHttpMessageProcessor.
HttpMessage
Wystąpienie HttpMessage zawiera trzy fragmenty danych:
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 ...
}
Wystąpienie HttpMessage zawiera MessageId GUID, który umożliwia nam połączenie żądania HTTP z odpowiednią odpowiedzią HTTP, oraz wartość logiczną, która określa, czy obiekt zawiera wystąpienie HttpRequestMessage i HttpResponseMessage. Korzystając z wbudowanych klas HTTP z System.Net.Http, byłem w stanie skorzystać z application/http kodu analizy, który jest zawarty w System.Net.Http.Formatting.
IHttpMessageProcessor
Wystąpienie HttpMessage jest następnie przekazywane do implementacji IHttpMessageProcessor, który jest interfejsem stworzonym w celu oddzielenia odbierania i interpretacji zdarzenia od usługi Azure Event Hubs i jego faktycznego przetwarzania.
Przekazywanie komunikatu HTTP
W tym przykładzie postanowiliśmy wypchnąć żądanie HTTP do usługi Moesif API Analytics. Moesif to oparta na chmurze usługa specjalizująca się w analizie i debugowaniu HTTP. Mają darmowy poziom, więc łatwo jest spróbować. Moesif umożliwia nam wyświetlanie żądań HTTP w czasie rzeczywistym przepływających za pośrednictwem naszej usługi API Management.
Implementacja IHttpMessageProcessor wygląda następująco:
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));
}
}
Funkcja MoesifHttpMessageProcessor korzysta z biblioteki API języka C# dla Moesif, która ułatwia przesyłanie danych zdarzeń HTTP do usługi. Aby wysyłać dane HTTP do interfejsu API modułu zbierającego Moesif, potrzebne jest konto i identyfikator aplikacji. Otrzymasz identyfikator aplikacji Moesif, tworząc konto w witrynie internetowej Moesif , a następnie przejdź do menu w prawym górnym rogu i wybierz pozycję Konfiguracja aplikacji.
Kompletny przykład
Kod źródłowy i testy dla przykładu znajdują się w witrynie GitHub. Aby uruchomić przykład samodzielnie, potrzebujesz usługi zarządzania API, połączonego centrum zdarzeń i konta magazynowego.
Przykład to prosta aplikacja konsolowa, która nasłuchuje zdarzeń pochodzących z Event Hub, konwertuje je na obiekty Moesif EventRequestModel i EventResponseModel, a następnie przekazuje je do interfejsu API modułu zbierającego Moesif.
Na poniższym animowanym obrazie widać żądanie wysyłane do interfejsu API w Portalu Dewelopera, w aplikacji konsolowej, gdzie wyświetlany jest komunikat, który jest odbierany, przetwarzany i przesyłany dalej, a następnie żądanie i odpowiedź pojawiają się w przepływie zdarzeń.
Podsumowanie
Usługa Azure API Management zapewnia idealne miejsce do przechwytywania ruchu HTTP przychodzącego do i z interfejsów API. Usługa Azure Event Hubs to wysoce skalowalne, tanie rozwiązanie do przechwytywania tego ruchu i przekazywania go do pomocniczych systemów przetwarzania na potrzeby rejestrowania, monitorowania i innych zaawansowanych analiz. Nawiązywanie połączenia z systemami monitorowania ruchu innych firm, takimi jak Moesif, jest tak proste, jak kilkadziesiąt wierszy kodu.
Powiązana zawartość
- Dowiedz się więcej o usłudze Azure Event Hubs
- Dowiedz się więcej o integracji usług API Management i Event Hubs