모니터링¶
가이드 홈 | 이전: Location | 다음: 실행 모델
다른 언어로 보기 — C++ · C#/.NET · Java · Kotlin · Node/TypeScript
이 장을 읽고 나면
지금 무엇이 준비되었는지 읽고, 그 상태가 바뀔 때마다 받고, message 하나가 어디서 끝났는지 남길 수 있다. 이 장의 코드는 예제 저장소의 tutorial README에서 가져온 언어별 관측 표면의 최소 호출이며, README의 「내려받기와 설치」·「빌드」·「실행」 절을 따르면 process의 상태와 기록을 읽을 수 있다.
앞 장들은 등록하고 호출하는 쪽을 다뤘다. 돌기 시작하면 다른 것이 필요해진다 — 연결이 준비되었는지, 어느 상대가 빠졌는지, message가 어디서 실패했는지다. handler를 아무리 읽어도 그 답은 나오지 않는다. Framework는 그것들을 공개 표면으로 제공한다.
runtime의 사건을 handler로 받는 표면은 없다. 운영 endpoint에는 먼저 상태 조회를 사용하고, 상태 변화를 처리해야 할 때만 구독을 추가한다. message 하나의 처리 경로는 진단 기록으로 보고, 대시보드 수치는 운영과 lifecycle의 계기로 수집한다.
1. 운영 상태 읽기로 시작하기¶
이 흐름은 운영 endpoint가 현재 상태를 즉시 응답하면서도, 필요한 곳에서만 변화와 message 처리 경로를 따로 관찰하게 한다.
2. 지금 상태 읽기¶
조회는 호출 시점의 값 한 장을 돌려준다. 운영 endpoint의 응답을 만들거나 한 번만 확인할 때 사용한다.
val initial = runtime.snapshot(ZoneWorldNames.MESH)
registry.applyLiveRoutingIds(readyRoutingIds(initial))
준비 여부와 상태 값을 함께 읽는다. 준비되지 않았다는 것만으로는 무엇을 할지 정할 수 없고, 준비되지 않은 이유가 따로 온다 — 받을 수 있는 상대가 없는 것과 기록 저장소가 멎은 것과 지금 비우는 중인 것은 대응이 다르다.
돌아오는 값에는 상대의 식별자와 지금 상태와 사용할 수 없는 이유만 담긴다. 재연결 시도 횟수나 socket 내부 상태는 공개 계약이 아니다.
3. 변화 구독하기¶
구독은 바뀔 때마다 그 뒤의 완전한 값을 준다. 바뀐 항목만 담은 사건이 아니라 매번 전체다. 이전 값과 비교할 일이 있으면 구독하는 쪽이 보관한다.
val publisher = runtime.observe(ZoneWorldNames.MESH, 32)
observation = publisher
publisher.subscribe(
object : Flow.Subscriber<ZLinkObservedStatus<ZLinkMeshNodeSnapshot>> {
override fun onSubscribe(subscription: Flow.Subscription) {
subscription.request(Long.MAX_VALUE)
}
override fun onNext(observed: ZLinkObservedStatus<ZLinkMeshNodeSnapshot>) {
registry.applyLiveRoutingIds(readyRoutingIds(observed.status()))
}
override fun onError(error: Throwable) {
println(
"node observation error mesh=${ZoneWorldNames.MESH}" +
" detail=${error.message}"
)
}
override fun onComplete() {}
}
)
놓칠 수 있다. 받는 쪽이 느리면 보관 한도를 넘는 중간 값을 건너뛴다. 놓친 개수는 항목마다 함께 오고, 합치기로 건너뛴 것과 한도를 넘겨 다시 받을 수 없는 것으로 나뉜다. 앞의 것은 최신 값을 받았다는 뜻이지만 뒤의 것은 그렇지 않다.
두 값의 선후는 항목에 실린 순번으로 판단한다. 함께 오는 시각은 표시용이다.
조회는 한 번 읽을 때, 구독은 상태 전이를 기록하거나 반응할 때 사용한다. 중간 값을 건너뛰는 것은 구독뿐이다.
구독은 취소할 때까지 열려 있으므로 host의 수명에 묶인 자리에서 돌린다. 언어마다 그 자리가 다르다 — 백그라운드 서비스, 구독 객체의 수명, 취소 signal이다.
4. 진단 수준 정하기¶
message 하나가 어디서 어떻게 끝났는지는 진단이 남긴다. 수준은 다음과 같다.
| 수준 | 남기는 것 |
|---|---|
| 끔 | 남기지 않는다. trace 항목도 문자열도 만들지 않는다 |
| 오류(기본) | dispatch 실패와 backpressure |
| 보통 | 위에 더해 수신·dispatch·완료 같은 주요 전이 |
| 상세 | 위에 더해 byte 크기와 걸린 시간 |
운영에서는 오류 단계로 두고 필요할 때만 올린다. 상세 단계는 message마다 기록을 남기므로 처리량이 많은 구간에서는 그 자체가 부하가 된다. 출력만 버리는 기록 filter는 끔 단계와 같지 않다 — 만드는 비용이 그대로 남는다.
한 요청과 그 답을 잇는 값과, 그 요청이 시작한 뒤따르는 호출까지 잇는 값이 각각 기록에 실린다. 여러 node를 거친 조각을 하나로 묶어 볼 때 사용한다.
기록을 어디에 저장하고 어디로 내보낼지는 application의 기존 설정이 소유한다. Framework는 관측용 callback이나 파일 경로를 공개하지 않고, application이 구성해 둔 표준 provider에 사용한다. provider 호출이 실패해도 원래 메시지 처리의 결과는 바뀌지 않는다.
5. 자주 발생하는 문제¶
- runtime의 사건을 handler로 받고 싶다 — 그런 표면은 없다. 상태 변화는 변화 구독하기로, 개별 message의 결과는 진단 수준 정하기로 본다.
- 구독이 아무것도 주지 않는다 — 그 이름에 변화가 없으면 조용하다. 지금 값이 필요하면 지금 상태 읽기를 먼저 읽고 구독을 이어 받는다.
- health endpoint를 기대한다 — Framework는 HTTP endpoint를 만들지 않는다. 준비 여부를 application의 기존 endpoint에 연결한다 — 운영과 lifecycle이 그 자리다.
- 어느 node에 무엇이 있는지 보고 싶다 — Location의 조회와 운영과 lifecycle의 topology 조회를 사용한다.
- handler가 없는 message를 알고 싶다 — 진단 수준을 오류 이상으로 두면 dispatch 실패로 남는다. 요청은 오류 응답으로 돌아오고 보내기는 조용히 버려지므로, 보내기 쪽은 진단으로만 확인된다 — Channel 동작 원리가 그 차이를 다룬다.
6. 확인¶
Server와 Client를 실행한 뒤 상태 조회에서 준비 상태와 peer 상태를 확인하고, 진단 수준을 높여 보낸 message의 수신·dispatch·완료 기록을 확인한다.
7. 관련 문서¶
- 수치와 운영 호출 — 운영과 lifecycle
- 준비되지 않은 이유의 종류 — Channel 동작 원리
- 처리보다 도착이 빠를 때 — Backpressure
- 언어별 표면 이름 — 주요 타입 사용 색인