메시징
글 17개
VitalRouter 는 자식 스코프에서도 라우터를 새로 만들지 않는다
씬 스코프에서 RegisterVitalRouter 로 붙인 로그 인터셉터가 씬을 다섯 번 불러오면 다섯 번 돕니다. 부모의 라우터를 공유하는데 스코프를 닫을 때 인터셉터는 떼지 않아서입니다. Isolated 를 켜면 쌓이지 않습니다.
VitalRouter 의 Sequential 은 다른 클래스와도 줄을 선다
대사 Presenter 에만 Sequential 을 붙였는데 인벤토리 Presenter 와 함께 한 줄로 섭니다. VitalRouter 의 순서 제어는 발행 사이를 줄 세우는 인터셉터이고, 줄의 범위는 그 인스턴스를 나눠 갖는 범위이기 때문입니다.
VitalRouter 의 연속 발행은 백그라운드 스레드에서 멈출 수 있다
기다리는 구독자가 둘 이상인 커맨드를 백그라운드 스레드에서 연달아 발행하면 PublishAsync 가 영영 끝나지 않을 수 있습니다. VitalRouter 2.8.0 의 두 줄 순서가 원인이고, 유니티 메인 스레드에서는 걸리지 않았습니다.
VitalRouter 의 할당 0 은 기다리지 않는 발행에서만이다
VitalRouter 는 zero-allocation 을 내세우고, 동기 발행은 정말 0 B 입니다. 그런데 인터셉터를 하나 달면 24 B, 구독자가 실제로 기다리면 유니티에서 발행 한 번에 1 KB 가까이 붙습니다. 어디까지 막고 어디서부터 못 막는지 봅니다.
VitalRouter 인터셉터의 next 는 커서 하나다
재시도 인터셉터가 next 를 두 번 부르면 두 번째는 뒤 인터셉터를 건너뜁니다. VitalRouter 의 next 는 발행 문맥 하나가 가진 델리게이트와 커서 int 하나라서입니다. 지웠다 넣은 인터셉터가 앞에 끼어드는 것도 같은 구조에서 나옵니다.
VitalRouter 는 구독자 하나가 던지면 뒤를 부르지 않는다
Hit 을 이펙트, 체력 바, 사운드가 구독할 때 이펙트가 던지면 나머지는 불리지 않습니다. VitalRouter 의 발행 루프에 try/catch 가 없어서입니다. 실패는 하나만 남고, 늦은 실패는 다음 발행에 옮겨 붙기도 합니다.
VitalRouter 의 구독과 해제는 목록을 처음부터 찾는다
씬을 나갈 때 구독자 4,000명을 하나씩 Dispose 하면 IL2CPP 에서 3 ms 가 넘습니다. VitalRouter 는 빈 칸도 해제할 구독자도 배열 처음부터 찾기 때문입니다. 함께 사라지는 구독은 라우터째 버리면 탐색이 없습니다.
VitalRouter 의 발행은 관심 없는 구독자까지 전부 훑는다
Damage 구독자 3명과 Heal 구독자 997명이 한 라우터에 있으면 Damage 발행은 1,000명을 다 들릅니다. 라우터에 타입별 목록이 없어서입니다. 거르기는 싸지만 0 이 아니고, 부르는 순서는 약속이 아닙니다.
VitalRouter 는 라우터 하나에 모든 커맨드를 흘린다
C# event 는 커맨드마다 누가 누구를 아는 선이 생기고, 발행한 쪽이 처리 끝을 기다릴 수 없습니다. VitalRouter 는 라우터 하나에 모든 커맨드를 흘리고, 발행이 늘 ValueTask 를 돌려주게 해서 둘을 풉니다.
MessagePipe 의 구독 진단은 숫자도 위치도 틀릴 수 있다
MessagePipe 는 해제 누락을 찾으라고 구독 수와 구독한 곳을 기록합니다. 그런데 비동기 브로커에서는 구독 수가 음수로 내려가고, 구독한 곳은 네임스페이스 이름이나 IL2CPP 설정에 따라 엉뚱하게 찍히거나 사라집니다.
MessagePipe 의 필터는 구독할 때 한 번 접힌다
MessagePipe 필터는 발행 때 목록을 도는 것이 아니라 구독 때 핸들러를 델리게이트 체인으로 한 번 감싸 둡니다. 그래서 같은 Order 면 합친 순서의 반대로 돌고, 비용은 구독에 몰리며, 람다 구독에는 속성 필터가 붙지 않습니다.
MessagePipe 의 병렬 발행은 앞이 던지면 뒤를 시작하지 않는다
MessagePipe 의 PublishAsync 는 기본이 병렬이지만, 핸들러를 차례로 시작하다 곧바로 실패한 핸들러를 만나면 뒤는 시작하지 않습니다. async 여도 첫 await 전에 던지면 같고, 늦게 실패하면 나머지를 기다리지 않고 끝납니다.
MessagePipe 의 버퍼드와 키 브로커는 일반 브로커에 규칙 하나를 얹는다
MessagePipe 의 버퍼드 브로커와 키 브로커는 일반 구독 목록을 그대로 쓰고 규칙 하나만 바꿉니다. 마지막 값은 버퍼드 발행자만 적고, 키 목록은 비는 순간 지웁니다. 그래서 일반 발행은 재생되지 않고, 키 구독은 매번 할당합니다.
MessagePipe 는 자식 스코프에서 꺼내도 나뉘지 않는다
MessagePipe 브로커의 기본 수명은 싱글턴이라, 자식 스코프에서 꺼낸 발행자와 구독자도 루트의 구독 목록 하나를 씁니다. 스코프 수명으로 등록해야 나뉘고, 그 스코프를 닫으면 구독이 한꺼번에 걷힙니다.
MessagePipe 의 동시 해제는 남의 구독을 지운다
같은 MessagePipe 구독을 두 스레드가 동시에 Dispose 하면 해제 검사가 락 밖이라 둘 다 통과합니다. 목록은 칸 번호만 보고 지우므로 KeyNotFoundException 이 나거나, 그사이 칸을 받은 다른 구독이 조용히 사라집니다.
MessagePipe 는 구독자를 칸 번호가 고정된 배열에 둔다
MessagePipe 의 발행이 락 없이 할당 0 으로 도는 것도, 호출 순서가 구독 순서와 다른 것도, 999명이 빠져도 1,024칸을 훑는 것도 모두 칸 번호를 바꾸지 않는 구독 목록에서 나옵니다.
MessagePipe 는 이벤트를 DI 컨테이너의 서비스로 만든다
C# event 는 발행자를 직접 알아야 하고 해제를 잊으면 구독자가 붙들립니다. MessagePipe 는 브로커를 컨테이너에 등록해 양쪽이 메시지 타입만 알게 하고, 해제를 IDisposable 로 돌려줘 구독자 수와 무관하게 만듭니다.