콘텐츠로 이동

English | 한국어

소켓 목차 | 이전: PUB | 다음: XPUB

Socket — SUB

이 장이 정의하는 것 — SUB socket의 구독 동작과 공개 계약.

1. SUB socket 개요

SUB는 topic 필터링으로 구독한 message만 받는 구독 전용 socket 타입이다. message가 함께 운반하는 분류용 byte 열을 topic이라 하며, SUB는 등록한 구독 filter와 topic이 매칭되는 message를 수신한다. SUB는 data 수신 전용이다 — 발행은 PUB·XPUB가 담당하고, 구독 등록·해제 같은 구독 관리는 message를 나르는 data plane이 아니라 socket을 설정·제어하는 control plane 호출이다.

이 문서는 SUB 고유 계약을 정의한다 — 구독 filter의 등록·해제와 매칭 규칙, SUB 전용 옵션(zlink_sub_option_t), topic과 payload record 수신 함수와 구독 목록 조회. 이 함수들은 raw XSUB에도 적용되며, XSUB 고유 동작은 XSUB가 정의한다.

관련 계약의 소유 문서는 다음과 같다.

관련 계약 정의하는 문서
socket 공통 옵션(RCVHWM·RCVBUF 포함)·수명·스레드 안전성·수신 모델 Socket 공통
구독 message를 발행하는 socket과 topic wire 규칙 PUB, XPUB
Auto HWM budget 계산·admission Auto HWM
message lifecycle과 ownership Message

2. 구독과 filter 매칭

구독은 filter 문자열 단위로 관리한다.

  1. 등록zlink_set_subscription이 filter 하나를 구독으로 등록한다. filter의 종료 NUL 앞 byte 열이 byte-prefix가 되어, topic이 그 byte로 시작하는 message가 매칭된다. 빈 문자열은 모든 message를 구독한다. wildcard 구문은 없다.
  2. 해제zlink_unset_subscription이 같은 byte-prefix 해석으로 이전에 등록한 구독을 제거한다.
  3. 조회 — 구독된 topic 수는 읽기 전용 옵션 ZLINK_SUB_OPT_TOPICS_COUNT로 읽고, 개별 filter는 zlink_subscription_at으로 index를 지정해 읽는다.
  4. 수신 — 매칭된 message는 zlink_subscribe로 topic과 payload record 전체를 받는다.

3. 자동 HWM 기본값

SUB의 수신 queue가 유지할 byte 상한(HWM)은 application이 직접 정하지 않으면 context의 Auto HWM 정책이 자동으로 계산한다.

SUB는 context auto HWM 정책에서 recv_ingress 역할로 분류된다. 활성 auto-HWM profile은 Core memory budget 비율과 역할별 byte 경계를 선택하고, Core는 그 budget을 고유 physical directional queue에 분배한다. 기본 profile은 balanced다. 사용자가 RCVHWM을 직접 설정하면 그 application 방향은 자동 분배에서 제외된다. RCVBUF는 OS socket buffer option이며 auto HWM이 변경하지 않는다.

budget 계산과 admission의 정확한 계약은 Auto HWM이, RCVHWM·RCVBUF 옵션 자체는 Socket 공통이 소유한다.

4. Receive flow state

DEALER와 ROUTER는 자신에게 보내는 peer에게 receive-flow 상태를 알린다. SUB은 receive-flow 대상 socket type이 아니다.

  • zlink_socket_set_receive_flow_state()는 SUB socket에 대해 errno == ENOTSUP과 함께 ZLINK_CONFIG_NOT_SUPPORTED를 반환하고 아무것도 바꾸지 않는다.
  • Socket 공통이 정의하는 byte HWM, low water mark와 transport backpressure는 그대로 유지된다.
  • SUB socket의 monitor는 ZLINK_MONITOR_STATUS_DETAIL_FLOW_STATE를 설정하지 않고 ZLINK_EVENT_SEND_FLOW_PAUSED, ZLINK_EVENT_SEND_FLOW_RESUMED, ZLINK_EVENT_FLOW_STATE_STALE를 발생시키지 않는다.

zlink_set_sub_option() / zlink_get_sub_option()과 함께 사용한다.

typedef enum zlink_sub_option_t
{
    ZLINK_SUB_OPT_TOPICS_COUNT = 0x3400  // 구독된 topic 수 (int, 읽기 전용)
} zlink_sub_option_t;

6. 함수

SUB/XSUB socket 전용 옵션을 설정한다.

ZLINK_EXPORT zlink_config_result_t zlink_set_sub_option (void *handle_,
                           zlink_sub_option_t option_,
                           const void *optval_,
                           size_t optvallen_);

SUB/XSUB socket 옵션을 설정한다. 모든 socket 타입에 공유되는 공통 옵션은 zlink_set_option()을 사용한다.

반환값: 성공 시 ZLINK_CONFIG_OK, 실패 시 zlink_config_result_t 값. zlink_errno()는 진단용 내부 errno를 그대로 유지한다.

참고: zlink_get_sub_option, zlink_set_option


SUB/XSUB socket 전용 옵션을 조회한다.

ZLINK_EXPORT zlink_config_result_t zlink_get_sub_option (void *handle_,
                           zlink_sub_option_t option_,
                           void *optval_,
                           size_t *optvallen_);

SUB/XSUB socket 옵션의 현재 값을 가져온다.

반환값: 성공 시 ZLINK_CONFIG_OK, 실패 시 zlink_config_result_t 값. zlink_errno()는 진단용 내부 errno를 그대로 유지한다.

참고: zlink_set_sub_option


topic filter를 구독한다.

ZLINK_EXPORT zlink_config_result_t zlink_set_subscription (void *handle_, const char *filter_);

filter_에 매칭되는 message를 구독한다. filter_는 NUL로 끝나는 문자열이며 내부 NUL을 포함할 수 없다. 종료 NUL 앞의 byte를 byte-prefix filter로 사용하므로 message topic이 그 byte로 시작하면 매칭된다. 빈 문자열은 모든 message를 구독한다. wildcard 구문은 없으며 후행 *도 literal byte다.

적용 대상: raw SUB, raw XSUB.

반환값: 성공 시 ZLINK_CONFIG_OK, 실패 시 zlink_config_result_t 값. zlink_errno()는 진단용 내부 errno를 그대로 유지한다.

에러: handle_이 NULL이면 EFAULT. filter_가 NULL이거나 handle 타입이 구독을 지원하지 않으면 EINVAL.

참고: zlink_unset_subscription, zlink_subscribe


topic filter 구독을 해제한다.

ZLINK_EXPORT zlink_config_result_t zlink_unset_subscription (void *handle_, const char *filter_);

이전에 등록된 구독을 제거한다. filter_는 NUL로 끝나고 내부 NUL이 없는 문자열이어야 한다. zlink_set_subscription()과 같은 byte-prefix 해석을 사용하며 종료 NUL 앞의 byte가 이전에 등록한 prefix와 일치해야 한다.

적용 대상: raw SUB, raw XSUB.

반환값: 성공 시 ZLINK_CONFIG_OK, 실패 시 zlink_config_result_t 값. zlink_errno()는 진단용 내부 errno를 그대로 유지한다.

에러: handle_이 NULL이면 EFAULT. filter_가 NULL이거나 handle 타입이 구독 해제를 지원하지 않으면 EINVAL.

참고: zlink_set_subscription


raw SUB 또는 XSUB socket에서 topic과 payload record 전체를 수신한다.

ZLINK_EXPORT zlink_recv_result_t zlink_subscribe (
  void *sub_,
  const zlink_routing_id_t **source_rid_out_,
  char *topic_id_buf_, size_t topic_id_capacity_, size_t *topic_id_len_out_,
  zlink_msg_t *parts_out_, size_t parts_capacity_, size_t *part_count_out_,
  zlink_recv_flags_t flags_);

topic_id_len_out_, parts_out_, part_count_out_은 필수다. 배열 슬롯은 미리 초기화할 필요가 없다. source_rid_out_은 선택 사항이며 raw SUBXSUB에서는 항상 NULL을 받는다. 성공하면 topic의 binary byte를 호출자 buffer에 NUL 없이 복사하고 payload record 전체를 배열에 채운다. Caller는 앞의 *part_count_out_개 슬롯을 zlink_multipart_close로 정확히 한 번 닫아야 한다.

topic_id_capacity_가 topic 길이보다 작으면(길이 0 topic은 capacity 0으로 성공한다) 함수는 *topic_id_len_out_에 필요한 topic 길이를 기록하고 ZLINK_RECV_BUFFER_TOO_SMALLENOBUFS를 반환한다. 이 경우 Core는 그 message의 topic과 payload를 내부에 보관하고, topic_id_len_out_을 제외한 output과 parts_out_은 변경하지 않는다. 슬롯 소유권도 이전하지 않으므로 호출자는 충분한 buffer로 다시 호출해 보관된 같은 message를 받는다. 용량이 0보다 큰데 topic_id_buf_가 NULL이면 queue를 검사하거나 소비하기 전에 ZLINK_RECV_INVALID_HANDLEEFAULT를 반환하고 모든 output과 parts_out_을 변경하지 않는다.

parts_capacity_가 payload part 수보다 작으면 record를 소비하지 않고 필요한 수를 *part_count_out_에 쓴 뒤 ZLINK_RECV_BUFFER_TOO_SMALL+ENOBUFS를 반환한다. 다른 output과 배열 슬롯은 변하지 않으며, 충분한 배열로 재시도하면 같은 record를 받는다. 적용 타입은 raw SUB, raw XSUB다.


지정된 index의 구독 filter를 조회한다.

ZLINK_EXPORT zlink_config_result_t zlink_subscription_at (void *handle_,
                           size_t index_,
                           char *filter_out_,
                           size_t *filter_len_inout_,
                           int *is_pattern_out_);

index_는 조회 시점의 구독 snapshot을 filter byte 열의 사전식 오름차순으로 정렬한 결과에 대한 0-기반 index이며 등록 순서가 아니다. 호출이 성공하면 filter_out_에는 해당 filter byte만 기록하고 종료 NUL은 추가하지 않는다. 따라서 이 output은 C 문자열이 아니며, 진입 시 *filter_len_inout_는 buffer 크기이고 반환 시 filter byte 길이다. is_pattern_out_은 NULL을 허용하는 선택 output이다. NULL이 아니면 filter가 pattern 구독인지 기록하며, 모든 raw 구독은 byte-prefix filter이므로 0을 기록한다.

buffer가 작으면 필요한 길이를 *filter_len_inout_에 기록하고 ZLINK_CONFIG_BUFFER_TOO_SMALL, errno == ENOBUFS를 반환한다. 이 결과에서는 filter_out_에 부분 데이터를 기록하지 않고 *is_pattern_out_도 변경하지 않는다. 구독 목록(subscription inventory)을 소비하거나 변경하지 않으므로 호출자는 같은 index_를 충분한 buffer로 다시 조회할 수 있다.

적용 타입: raw SUB, raw XSUB.

반환값: 성공 시 ZLINK_CONFIG_OK, 실패 시 zlink_config_result_t 값. zlink_errno()는 진단용 내부 errno를 그대로 유지한다.

에러: index가 범위를 벗어나면 ENOENT. buffer가 작으면 ENOBUFS. handle 타입이 구독 조회를 지원하지 않으면 ENOTSUP.

참고: zlink_set_subscription, zlink_get_sub_option

7. 구현 및 contract test 검증 요구

공개 표면(구독 함수, SUB 옵션 set·get, zlink_subscribe 결과, 반환값·errno)만으로 다음을 확인한다. 각 항목은 unit test 하나로 이어진다.

구독 등록·해제 - zlink_set_subscription으로 등록한 filter는 byte-prefix로 매칭된다 — topic이 filter의 종료 NUL 앞 byte로 시작하는 message가 zlink_subscribe로 수신된다. - 빈 문자열 filter는 모든 message를 구독한다. - 후행 *를 포함한 filter는 *를 literal byte로 매칭한다 — wildcard로 확장되지 않는다. - zlink_unset_subscription은 종료 NUL 앞 byte가 이전에 등록한 prefix와 일치하는 구독을 제거한다. - zlink_set_subscription·zlink_unset_subscription에서 handle_이 NULL이면 EFAULT, filter_가 NULL이거나 handle 타입이 구독(해제)을 지원하지 않으면 EINVAL이다.

구독 목록 조회 - 읽기 전용 ZLINK_SUB_OPT_TOPICS_COUNTzlink_get_sub_option으로 읽으면 구독된 topic 수가 int로 반환된다. - zlink_subscription_at의 0-기반 index_는 등록 순서가 아니라 filter byte 열로 정렬한 snapshot 순서를 따른다. 이 snapshot은 호출마다 새로 만들며 ZLINK_SUB_OPT_TOPICS_COUNT 조회와 원자적으로 묶이지 않는다. 두 호출 사이에 subscription이 바뀌면 같은 index_가 다른 filter를 가리키거나 ENOENT가 될 수 있으므로, 목록을 읽는 동안에는 caller가 subscription 변경을 직렬화한다. - 성공 시 filter_out_에는 filter byte만 기록되고 종료 NUL은 기록되지 않는다. *filter_len_inout_은 그 byte 길이며 filter_out_은 C 문자열이 아니다. - is_pattern_out_은 NULL을 허용하는 선택 output이다. 제공하면 모든 raw 구독이 byte-prefix filter이므로 0이 기록된다. - buffer가 작으면 필요한 길이를 *filter_len_inout_에 기록하고 ZLINK_CONFIG_BUFFER_TOO_SMALLENOBUFS를 반환한다. filter_out_에 부분 데이터를 쓰지 않고 *is_pattern_out_도 바꾸지 않으며, 같은 index_를 충분한 buffer로 다시 조회할 수 있다. - 범위를 벗어난 index는 ENOENT, 구독 조회를 지원하지 않는 handle 타입은 ENOTSUP이다.

topic과 payload record 수신 - 성공한 zlink_subscribe는 topic의 binary byte를 NUL 없이 caller buffer에 복사하고 payload record 전체를 배열에 채운다 — caller가 앞의 *part_count_out_개 슬롯을 zlink_multipart_close로 정확히 한 번 닫는다. - raw SUB·XSUB에서 source_rid_out_은 항상 NULL을 받는다. - topic_id_capacity_가 topic 길이보다 작으면(길이 0 topic은 capacity 0으로 성공) *topic_id_len_out_에 필요한 길이를 기록하고 ZLINK_RECV_BUFFER_TOO_SMALLENOBUFS를 반환한다. Core가 그 message의 topic과 payload를 내부에 보관하므로 충분한 buffer로 다시 호출하면 같은 message를 수신하고, topic_id_len_out_을 제외한 output과 parts_out_은 변하지 않는다. - parts_capacity_가 payload part 수보다 작으면 *part_count_out_에 필요한 수를 기록하고 ZLINK_RECV_BUFFER_TOO_SMALL+ENOBUFS를 반환한다. Record와 다른 output은 그대로이며 충분한 배열로 재시도하면 같은 record를 받는다. - topic frame 뒤에 payload part가 없는(topic frame에 MORE가 없는) record를 받으면 ZLINK_RECV_INTERNAL_ERROREPROTO를 반환한다. - 용량이 0보다 큰데 topic_id_buf_가 NULL이면 queue를 검사·소비하기 전에 ZLINK_RECV_INVALID_HANDLEEFAULT를 반환하고 모든 output과 parts_out_이 변하지 않는다. - multipart message의 모든 payload part는 배열 순서대로 한 번에 반환되며 부분 record 상태는 남지 않는다.

자동 HWM 기본값 - RCVHWM을 직접 설정한 application 방향은 자동 분배에서 제외된다. - RCVBUF는 auto HWM이 변경하지 않는다.

Receive flow state 부재 - SUB socket에 zlink_socket_set_receive_flow_state()를 호출하면 ZLINK_CONFIG_NOT_SUPPORTEDENOTSUP이며 아무것도 바뀌지 않는다. - SUB socket의 monitor는 ZLINK_MONITOR_STATUS_DETAIL_FLOW_STATE를 설정하지 않고 ZLINK_EVENT_SEND_FLOW_PAUSED·ZLINK_EVENT_SEND_FLOW_RESUMED·ZLINK_EVENT_FLOW_STATE_STALE를 발생시키지 않는다.

공통 반환 규약 - zlink_config_result_t를 반환하는 위 함수들은 성공 시 ZLINK_CONFIG_OK, 실패 시 zlink_config_result_t 값을 반환하며 zlink_errno()는 진단용 내부 errno를 그대로 유지한다.

Auto HWM budget 계산·admission의 검증은 Auto HWM §5가 소유한다.

소켓 목차 | 이전: PUB | 다음: XPUB