다음을 통해 공유


Orleans에서 변경할 수 없는 형식의 직렬화

Orleans(은)는 변경할 수 없는 형식이 포함된 메시지 직렬화와 관련된 일부 오버헤드를 방지하는 데 사용할 수 있는 기능이 있습니다. 이 섹션에서는 관련 위치에 대한 컨텍스트부터 시작하여 기능 및 해당 애플리케이션에 대해 설명합니다.

Orleans의 serialization

노이즈 메서드가 호출되면 Orleans 런타임은 메서드 인수의 전체 복사본을 만들고 복사본에서 요청을 형성합니다. 이렇게 하면 호출된 조직으로 데이터가 전달되기 전에 인수 개체를 수정하는 호출 코드로부터 보호됩니다.

호출된 조직이 다른 사일로에 있는 경우 복사본은 결국 바이트 스트림으로 직렬화되고 네트워크를 통해 대상 사일로로 전송되고, 여기서 다시 개체로 역직렬화됩니다. 호출된 조직이 동일한 사일로에 있으면 복사본이 호출된 메서드에 직접 전달됩니다.

반환 값은 먼저 복사한 다음, 직렬화 및 역직렬화와 같은 방식으로 처리됩니다.

복사, 직렬화 및 역직렬화의 3가지 프로세스 모두 개체 ID를 준수합니다. 즉, 동일한 개체가 있는 목록을 두 번 전달하면 받는 쪽에서는 동일한 값을 가진 두 개체가 아니라 동일한 개체가 있는 목록을 두 번 받게 됩니다.

복사 최적화

대부분의 경우 심층 복사는 필요하지 않습니다. 예를 들어 가능한 시나리오는 클라이언트에서 바이트 배열을 수신하고 바이트 배열을 포함한 해당 요청을 처리하기 위해 조직으로 전달하는 웹 프런트 엔드입니다. 프런트 엔드 프로세스는 배열이 조직에 전달되면 배열을 사용하여 아무 작업도 수행하지 않습니다. 특히 배열을 다시 사용하여 향후 요청을 수신하지 않습니다. 조직 내에서 바이트 배열은 입력 데이터를 가져오기 위해 구문 분석되지만 수정되지는 않습니다. 조직은 웹 클라이언트에 다시 전달하기 위해 만든 다른 바이트 배열을 반환합니다. 반환되는 즉시 배열을 삭제합니다. 웹 프런트 엔드는 수정 없이 결과 바이트 배열을 클라이언트에 다시 전달합니다.

이러한 시나리오에서는 요청 또는 응답 바이트 배열을 복사할 필요가 없습니다. 아쉽게도 Orleans 런타임은 나중에 웹 프런트 엔드 또는 그레인을 통해 배열이 수정되는지 여부를 알 수 없으므로 이를 자체적으로 파악할 수 없습니다. 가능한 모든 환경에서 값이 더 이상 수정되지 않음을 나타내는 일종의 .NET 메커니즘이 있습니다. 이 기능이 없으므로 Orleans 특정 메커니즘을 추가했습니다.Immutable<T> 래퍼 클래스 및 ImmutableAttribute입니다.

[Immutable] 특성을 사용하여 형식, 매개 변수, 속성 또는 필드를 변경할 수 없는 것으로 만듭니다.

사용자 정의 형식의 경우 형식에 ImmutableAttribute를 추가할 수 있습니다. 이렇게 하면 Orleans serializer가 이 형식의 인스턴스를 복사하지 않도록 합니다. 다음 코드 조각은 [Immutable]을 사용하여 변경할 수 없는 형식을 나타내는 방법을 보여 줍니다. 이 형식은 전송 중에 복사되지 않습니다.

[Immutable]
public class MyImmutableType
{
    public int MyValue { get; }

    public MyImmutableType(int value)
    {
        MyValue = value;
    }
}

때로는 개체를 제어할 수 없는 경우가 있습니다. 예를 들면 조직 간에 전송하는 List<int>입니다. 개체의 일부는 변경할 수 없고 다른 부분은 변경할 수 있는 경우도 있을 수 있습니다. 이러한 경우 Orleans(은)는 추가 옵션을 지원합니다.

  1. 메서드 서명은 매개 변수별로 ImmutableAttribute를 포함할 수 있습니다.

    public interface ISummerGrain : IGrain
    {
      // `values` will not be copied.
      ValueTask<int> Sum([Immutable] List<int> values);
    }
    
  2. 포함 형식의 인스턴스가 복사될 때 복사본이 만들어지지 않도록 개별 속성 및 필드를 ImmutableAttribute로 표시할 수 있습니다.

    [GenerateSerializer]
    public sealed class MyType
    {
        [Id(0), Immutable]
        public List<int> ReferenceData { get; set; }
    
        [Id(1)]
        public List<int> RunningTotals { get; set; }
    }
    

Immutable<T> 사용

Immutable<T> 래퍼 클래스는 값을 변경할 수 없는 것으로 간주될 수 있음을 나타내는 데 사용됩니다. 즉, 기본 값은 수정되지 않으므로 안전한 공유를 위해 복사가 필요하지 않습니다. Immutable<T>을 사용한다는 것은 값의 공급자나 값을 받는 사람이 나중에 수정하지 않음을 의미합니다. 이는 일방적인 약정이 아니라 상호 이중적인 약정입니다.

조직 인터페이스에서 Immutable<T>을 사용하려면 T를 전달하는 대신 Immutable<T>을 전달합니다. 예를 들어 위에서 설명한 시나리오에서 조직 메서드는 다음과 같습니다.

Task<byte[]> ProcessRequest(byte[] request);

그러면 다음과 같이 됩니다.

Task<Immutable<byte[]>> ProcessRequest(Immutable<byte[]> request);

Immutable<T>을 만들려면 생성자를 사용하기만 하면 됩니다.

Immutable<byte[]> immutable = new(buffer);

변경할 수 없는 내부 값을 얻으려면 .Value 속성을 사용합니다.

byte[] buffer = immutable.Value;

Orleans의 불변성

Orleans 목적의 경우 불변성은 다소 엄격한 명령문입니다. 데이터 항목의 내용은 항목의 의미 체계를 변경할 수 있는 방식으로 수정되지 않거나 동시에 항목에 액세스하는 다른 스레드를 방해할 수 있습니다. 이를 보장하는 가장 안전한 방법은 단순히 항목을 전혀 수정하지 않는 것입니다. 논리적 불변성보다는 비트 불변성입니다.

경우에 따라 이를 논리적 불변성으로 완화해도 안전하지만, 변경 코드가 스레드로부터 안전하게 보호되도록 주의해야 합니다. 다중 스레딩 처리는 복잡하고 Orleans 컨텍스트에서 일반적이지 않으므로 이 접근 방식에 대해 강력하게 권장하고 비트 불변성을 고수하는 것이 좋습니다.