Orleans 스트림 구현 세부 정보

이 섹션에서는 Orleans Stream 구현에 대한 개략적인 개요를 제공합니다. 애플리케이션 수준에서 볼 수 없는 개념과 세부 정보를 설명합니다. 스트림만 사용할 계획이라면 이 섹션을 읽을 필요가 없습니다.

용어:

스트림 이벤트를 수집할 수 있고 이벤트를 끌어오거나 이벤트를 사용하는 푸시 기반 메커니즘을 제공하는 지속성 스토리지 기술에 대해 "큐"라는 단어를 참조합니다. 일반적으로 확장성을 제공하기 위해 이러한 기술은 분할된 큐를 제공합니다. 예를 들어 Azure 큐를 사용하면 여러 큐를 만들 수 있고 Event Hubs에는 여러 허브가 있습니다.

영구 스트림

모든 Orleans 영구 스트림 공급자는 공통 구현 PersistentStreamProvider를 공유합니다. 이러한 제네릭 스트림 공급자는 기술별 IQueueAdapterFactory로 구성해야 합니다.

예를 들어 테스트를 위해 큐에서 데이터를 읽는 대신 테스트 데이터를 생성하는 큐 어댑터가 있습니다. 아래 코드에서는 사용자 지정(생성기) 큐 어댑터를 사용하도록 영구 스트림 공급자를 구성하는 방법을 보여 줍니다. 이 작업은 어댑터를 만드는 데 사용되는 팩터리 함수로 영구 스트림 공급자를 구성하여 수행합니다.

hostBuilder.AddPersistentStreams(
    StreamProviderName, GeneratorAdapterFactory.Create);

스트림 생산자가 새 스트림 항목을 생성하고 stream.OnNext()를 호출하는 경우 Orleans 스트리밍 런타임은 해당 스트림 공급자의 IQueueAdapter에서 적절한 메서드를 호출하여 해당 큐에 항목을 직접 추가합니다.

풀링 에이전트

영구 스트림 공급자의 중심에는 풀링 에이전트가 있습니다. 풀링 에이전트는 지속성 큐 세트에서 이벤트를 끌어와서 이를 사용하는 조직의 애플리케이션 코드에 전달합니다. 풀링 에이전트는 분할되고 고가용성 및 탄력적 분산 구성 요소인 분산된 "마이크로 서비스"라고 생각할 수 있습니다. 풀링 에이전트는 애플리케이션 조직을 호스트하는 동일한 사일로 내에서 실행되며 Orleans Streaming Runtime에서 완전히 관리됩니다.

StreamQueueMapperStreamQueueBalancer

풀링 에이전트는 IStreamQueueMapperIStreamQueueBalancer로 매개 변수화됩니다. IStreamQueueMapper는 모든 큐의 목록을 제공하며 스트림을 큐에 매핑하는 작업도 담당합니다. 이렇게 하면 영구 스트림 공급자의 생산자 쪽에서 메시지를 큐에 넣을 큐를 파악할 수 있습니다.

IStreamQueueBalancer는 큐가 Orleans 사일로와 에이전트 간에 균형을 맞추는 방식을 나타냅니다. 목표는 병목 현상을 방지하고 탄력성을 지원하기 위해 분산된 방식으로 에이전트에 큐를 할당하는 것입니다. 새 사일로가 Orleans 클러스터에 추가되면 큐가 이전 사일로와 새 사일로 간에 자동으로 균형을 조정합니다. StreamQueueBalancer를 사용하면 해당 프로세스를 사용자 지정할 수 있습니다. Orleans에는 다양한 분산 시나리오(크고 적은 수의 큐) 및 다양한 환경(Azure, 온-프레미스, 정적)을 지원하기 위해 여러 가지 기본 제공 StreamQueueBalancers가 있습니다.

위의 테스트 생성기 예제를 사용하여 아래 코드는 큐 매퍼 및 큐 분산 장치를 구성하는 방법을 보여줍니다.

hostBuilder
    .AddPersistentStreams(StreamProviderName, GeneratorAdapterFactory.Create,
        providerConfigurator =>
        providerConfigurator.Configure<HashRingStreamQueueMapperOptions>(
            ob => ob.Configure(options => options.TotalQueueCount = 8))
      .UseDynamicClusterConfigDeploymentBalancer());

위의 코드는 8개의 큐가 있는 큐 매퍼를 사용하도록 GeneratorAdapterFactory를 구성하고 DynamicClusterConfigDeploymentBalancer를 사용하여 클러스터 전체에서 큐의 균형을 조정합니다.

풀링 프로토콜

모든 사일로는 풀링 에이전트 세트를 실행하고 모든 에이전트는 하나의 큐에서 풀링됩니다. 풀링 에이전트 자체는 SystemTarget이라는 내부 런타임 구성 요소에 의해 구현됩니다. SystemTargets는 기본적으로 런타임 조직이며, 단일 스레드 동시성의 적용을 받으며, 일반 조직 메시징을 사용할 수 있으며, 조직만큼 가볍습니다. 조직과 달리 SystemTargets는 가상이 아닙니다. 즉, 명시적으로 생성되고(런타임에 의해) 위치가 투명하지 않습니다. SystemTargets로 풀링 에이전트를 구현함으로써 Orleans 스트리밍 런타임은 기본 제공 Orleans 기능을 사용할 수 있으며, 새로운 풀링 에이전트를 만드는 것이 새 조직을 만드는 것만큼 저렴하기 때문에 매우 많은 수의 큐로 확장할 수 있습니다.

모든 풀링 에이전트는 IQueueAdapterReceiver.GetQueueMessagesAsync 메서드를 호출하여 큐에서 끌어오는 주기적인 타이머를 실행합니다. 반환된 메시지는 IQueueCache라는 내부 에이전트별 데이터 구조에 배치됩니다. 모든 메시지를 검사하여 대상 스트림을 확인합니다. 에이전트는 Pub-Sub를 사용하여 이 스트림을 구독한 스트림 소비자 목록을 확인합니다. 소비자 목록이 검색되면 에이전트는 로컬(pub-sub 캐시에)로 저장하므로 모든 메시지에 대해 Pub-Sub에 문의할 필요가 없습니다. 또한 에이전트는 pub-sub를 구독하여 해당 스트림을 구독하는 새 소비자에 대한 알림을 받습니다. 에이전트와 pub-sub 간의 이 핸드셰이크는 강력한 스트리밍 구독 의미 체계를 보장합니다. 소비자가 스트림을 구독하면 구독한 후 생성된 모든 이벤트가 표시됩니다. 또한 StreamSequenceToken을 사용하면 이전 것을 구독할 수 있습니다.

큐 캐시

IQueueCache는 큐에서 새 이벤트를 분리하고 소비자에게 전달할 수 있는 내부 에이전트별 데이터 구조입니다. 또한 다른 스트림 및 다른 소비자에게 배달을 분리할 수 있습니다.

하나의 스트림에 3개의 스트림 소비자가 있고 그 중 하나가 느린 상황을 상상해 보세요. 주의를 기울이지 않으면 이 느린 소비자는 에이전트의 진행률에 영향을 주어 해당 스트림의 다른 소비자의 소비를 늦추고 다른 스트림에 대한 이벤트의 큐에서 제거 및 배달을 늦출 수도 있습니다. 이를 방지하고 에이전트에서 최대 병렬 처리를 허용하려면 IQueueCache를 사용합니다.

IQueueCache는 스트림 이벤트를 버퍼링하고 에이전트가 자체 속도로 각 소비자에게 이벤트를 배달하는 방법을 제공합니다. 소비자별 배달은 소비자별 진행률을 추적하는 IQueueCacheCursor라는 내부 구성 요소에 의해 구현됩니다. 이러한 방식으로 각 소비자는 자체 속도로 이벤트를 받습니다. 빠른 소비자는 큐에서 제거되는 즉시 빠르게 이벤트를 수신하고, 느린 소비자는 나중에 이벤트를 받습니다. 메시지가 모든 소비자에게 전달되면 캐시에서 삭제할 수 있습니다.

역 압력

Orleans Streaming Runtime의 역압은 큐에서 에이전트로 스트림 이벤트 가져오기에이전트에서 스트림 소비자로 이벤트 전달의 두 곳에 적용됩니다.

후자는 기본 제공 Orleans 메시지 배달 메커니즘에 의해 제공됩니다. 모든 스트림 이벤트는 표준 Orleans 조직 메시징을 통해 에이전트에서 소비자로 한 번에 하나씩 전달됩니다. 즉, 에이전트는 각 스트림 소비자에게 하나의 이벤트(또는 제한된 크기의 이벤트 일괄 처리)를 보내고 이 호출을 기다립니다. 다음 이벤트는 이전 이벤트에 대한 작업이 확인되거나 중단될 때까지 전달되지 않습니다. 이렇게 하면 소비자별 전달 속도를 한 번에 하나의 메시지로 자연스럽게 제한합니다.

큐에서 에이전트로 스트림 이벤트를 가져올 때 Orleans Streaming은 새로운 특수 Backpressure 메커니즘을 제공합니다. 에이전트가 큐에서 이벤트 큐를 분리하고 소비자에게 전달하기 때문에 단일 느린 소비자가 너무 뒤처져서 IQueueCache가 채워질 수 있습니다. IQueueCache가 무한정 증가하는 것을 방지하기 위해 크기를 제한합니다(크기 제한은 구성할 수 있음). 그러나 에이전트는 배달되지 않은 이벤트를 throw하지 않습니다.

대신 캐시가 채워지기 시작하면 에이전트는 큐에서 이벤트를 큐에서 해제하는 속도를 늦출 수 있습니다. 이렇게 하면 큐에서 소비하는 속도("역압")를 조정하여 느린 배달 기간을 "제거"하고 나중에 빠른 소비 속도로 돌아갈 수 있습니다. "느린 배달" 밸리를 감지하기 위해 IQueueCache는 개별 스트림 소비자에게 이벤트 배달 진행률을 추적하는 캐시 버킷의 내부 데이터 구조를 사용합니다. 따라서 응답성이 뛰어나고 자체 조정 시스템이 생성됩니다.