[Web on Reactive Stack] 5. RSocket: 5.3. Annotated Responders

한글로 번역한 Web on Reactive Stack, 5. RSocket: 5.3. Annotated Responders

#spring #reactive #rsocket


5.3. Annotated Responders

RSocket 응답자는 @MessageMapping@ConnectMapping 메서드로 구현할 수 있다. @MessageMapping 메서드는 각 요청을 처리하고 @ConnectMapping 메서드는 커넥션 레벨 이벤트(설정과 메타 데이터 푸시)를 처리한다. 어노테이션 달린 응답자(annotated responder)는 서버 측에서 응답하고 클라이언트 측에서 응답하기 위해 대칭적으로 모두 지원한다.


5.3.1. Server Responders

서버 측에서 어노테이션 달린 응답자(annotated responder)를 사용하려면 RSocketMessageHandler를 스프링 설정에 추가하여 @Controller 빈과 @MessageMapping@ConnectMapping 메서드를 감지하도록 한다.

Java:

@Configuration
static class ServerConfig {

    @Bean
    public RSocketMessageHandler rsocketMessageHandler() {
        RSocketMessageHandler handler = new RSocketMessageHandler();
        handler.routeMatcher(new PathPatternRouteMatcher());
        return handler;
    }
}

Kotlin:

@Configuration
class ServerConfig {

    @Bean
    fun rsocketMessageHandler() = RSocketMessageHandler().apply {
        routeMatcher = PathPatternRouteMatcher()
    }
}

다음으로 자바 RSocket API를 통해 RSocket 서버를 시작하고 다음과 같이 responder에 대한 RSocketMessageHandler를 연결한다:

Java:

ApplicationContext context = ... ;
RSocketMessageHandler handler = context.getBean(RSocketMessageHandler.class);

CloseableChannel server =
    RSocketServer.create(handler.responder())
        .bind(TcpServerTransport.create("localhost", 7000))
        .block();

Kotlin:

import org.springframework.beans.factory.getBean

val context: ApplicationContext = ...
val handler = context.getBean<RSocketMessageHandler>()

val server = RSocketServer.create(handler.responder())
        .bind(TcpServerTransport.create("localhost", 7000))
        .awaitFirst()

RSocketMessageHandler는 메타데이터 compositerouting을 기본적으로 지원한다. 다른 mime 타입으로 바꾸거나 다른 메타 데이터 mime 타입을 등록하려면 MetadataExtractor을 설정할 수 있다.

메타 데이터와 데이터 포맷을 지원하는데 필요한 EncoderDecoder 인스턴스를 설정해야 한다. 코덱 구현을 위해서는 spring-web 모듈이 필요할 수 있다.

기본적으로 SimpleRouteMatcherAntPathMatcher를 통해 라우팅 매칭하는데 사용된다. 효과적인 라우팅 매칭을 위해 spring-web 모듈의 PathPatternRouteMatcher를 사용하는 것이 좋다. RSocket 라우팅은 계층적일 수 있지만 URL path는 아니다. 두 라우팅 매칭은 기본적으로 “.”를 구분자로 사용하며 HTTP URL과 마찬가지로 URL 디코딩은 없다.

RSocketMessageHandlerRSocketStrategies를 통해 설정할 수 있으며, 동일한 프로세스에서 클라이언트와 서버 사이의 설정을 공유해야하는 경우 유용할 수 있다:

Java:

@Configuration
static class ServerConfig {

    @Bean
    public RSocketMessageHandler rsocketMessageHandler() {
        RSocketMessageHandler handler = new RSocketMessageHandler();
        handler.setRSocketStrategies(rsocketStrategies());
        return handler;
    }

    @Bean
    public RSocketStrategies rsocketStrategies() {
        return RSocketStrategies.builder()
            .encoders(encoders -> encoders.add(new Jackson2CborEncoder()))
            .decoders(decoders -> decoders.add(new Jackson2CborDecoder()))
            .routeMatcher(new PathPatternRouteMatcher())
            .build();
    }
}

Kotlin:

@Configuration
class ServerConfig {

    @Bean
    fun rsocketMessageHandler() = RSocketMessageHandler().apply {
        rSocketStrategies = rsocketStrategies()
    }

    @Bean
    fun rsocketStrategies() = RSocketStrategies.builder()
            .encoders { it.add(Jackson2CborEncoder()) }
            .decoders { it.add(Jackson2CborDecoder()) }
            .routeMatcher(PathPatternRouteMatcher())
            .build()
}


5.3.2. Client Responders

클라이언트 측의 어노테이션 응답자는 RSocketRequester.Builder에서 설정해야 한다. 자세한 내용은 Client Responders을 참조하라.


5.3.3. @MessageMapping

서버 또는 클라이언트 responder 설정이 완료되면 @MessageMapping 메서드를 다음과 같이 사용할 수 있다:

Java:

@Controller
public class RadarsController {

    @MessageMapping("locate.radars.within")
    public Flux<AirportLocation> radars(MapRequest request) {
        // ...
    }
}

Kotlin:

@Controller
class RadarsController {

    @MessageMapping("locate.radars.within")
    fun radars(request: MapRequest): Flow<AirportLocation> {
        // ...
    }
}

위의 @MessageMapping 메서드는 “locate.radars.within” 라우팅을 가진 Request-Stream 상호작용에 응답한다. 이 메서드는 다음 메서드 인자를 사용하는 유연한 메서드 시그니처를 지원한다:

메서드 인자 설명
@Payload 요청의 페이로드. Mono 또는 Flux와 같이 비동기 타입의 구체적인 값이 될 수 있다.

참고: 어노테이션 사용은 선택사항이다. 단순 타입이 아니면서 지원되는 인자가 아닌 경우에는 페이로드로 간주된다.
RSocketRequester 원격 종료 요청을 보낸 요청자(requester)
@DestinationVariable 매핑 패턴의 변수에 기반한 라우팅으로부터 추출된 값. 예로, @MessageMapping("find.radar.{id}")
@Header 등록된 메타 데이터를 추출한 값. MetadataExtractor 참고
@Headers Map<String, Object> 등록된 모든 메타 데이터를 추출한 값. MetadataExtractor 참고

반환값은 응답 페이로드로 직렬화될 하나 이상의 객체로 예상된다. 반환값은 Mono 또는 Flux와 같은 비동기 타입이거나, 구체적인 값 또는 void 또는 Mono<Void>와 같은 값이 없는(no-value) 비동기 타입일 수 있다.

@MessageMapping 메서드가 지원하는 RSocket 상호작용 타입은 입력(예: @Payload 인자) 및 출력의 카디널리티로부터 결정된다. 여기서 카디널리티는 다음을 의미한다:

카디널리티(Cardinality) 설명
1 명시적인 값, 혹은 Mono<T>와 같은 단일값(single-value) 비동기 타입
Many Flux<T>와 같은 다중값(multi-value) 비동기 타입
0 입력에서는 메서드에 @Payload 인자가 없음을 의미한다.

출력의 경우 void 또는 Mono<Void>와 같은 값이 없는(no-value) 비동기 타입

다음은 모든 입력과 출력 카디널리티 조합과 그에 따른 상호작용 타입 유형이다:

입력 카디널리티
(Input Cardinality)
출력 카디널리티
(Output Cardinality)
상호동작 유형
(Interaction Types)
0, 1 0 Fire-and-Forget, Request-Response
0, 1 1 Request-Response
0, 1 Many Request-Stream
Many 0, 1, Many Request-Channel


5.3.4. @ConnectMapping

@ConnectMapping은 RSocket 커넥션 시작시 SETUP 프레임을 핸들링한다. 그리고 METADATA_PUSH 프레임 (예: io.rsocket.RSocketmetadataPush(payload))으로 이어지는 메타 데이터 푸시 알림을 핸들링한다.

@ConnectMapping 메서드는 @MessageMapping과 동일한 인자를 지원하지만 SETUPMETADATA_PUSH 프레임의 메타 데이터와 데이터를 기반으로 한다. @ConnectMapping에 패턴을 지정하면 특정 라우팅 정보가 있는 커넥션만 처리한다. 아무 패턴도 선언되지 않은 경우라면 모든 커넥션이 매칭된다.

@ConnectMapping 메서드는 데이터를 반환할 수 없으며 void 또는 Mono<Void>를 반환 타입으로 선언해야 한다. 만일 신규 커넥션에 대해 오류를 반환하면 커넥션은 거절된다. RSocketRequester에 커넥션을 요청하기 위해 처리를 보류해서는 안 된다. 자세한 내용은 Server Requester를 참조하라.


목차 가이드





댓글을 남기시려면 Github 로그인을 해주세요 :D