Spring Boot 실천 고생 기 (13): WebFlux 를 사용 하여 응답 식 '푸 시 API' 구축

10281 단어 springboot
서양의 유명한 종교 사가 밀 차 일리 아 드 는 오늘 우리 가 미래 에 살 지 않 는 다 면 미래 에 우 리 는 과거 에 살 것 이 라 고 말 했다.
모 바 일 인터넷 의 보급 에 따라 C / S 모델 을 바탕 으로 하 는 앱 은 또 하나의 폭발 적 인 성장 을 시작 했다. 과거 에 우 리 는 C / S 모델 에 가서 B / S 를 껴 안 겠 다 고 말 했 지만 실제 적 으로 그 어떠한 개발 모델 도 구체 적 인 수요 응용 장면 에서 의 결과 물 에 불과 하고 절대적 인 좋 고 나 쁨 이 없 었 다.오늘 우 리 는 푸 시 역 사 를 시작 으로 현재 흔히 볼 수 있 는 푸 시 실현 방식 을 소개 하고 반응 식 의 예 를 결합 하여 실전 반응 식 프로 그래 밍 모델 의 응용 을 계속 할 것 이다.
푸 시
푸 시 는 최초 로 이메일 에서 탄생 해 새로운 소식 을 알 리 는 데 사 용 됐 고, 모 바 일 인터넷 시 대 는 모 바 일 클 라 이언 트 프로그램 (앱) 에 더 많이 활용 됐다.
푸 시 서 비 스 는 일반적으로 앞 당 겨 진 정보 제약 을 바탕 으로 형 성 된 것 이다.즉, 사용 하 는 디자인 모델 은 생산자 / 구독 자 모델 (Publish / subscribe) 이다.위 키 는 클 라 이언 트 가 서버 에서 생산 하 는 각종 정 보 를 구독 하 는 채널 을 통 해 언제든지 그 중의 한 채널 에서 새로운 내용 을 얻 을 수 있 고 같은 서버 는 푸 시 를 통 해 해당 하 는 클 라 이언 트 에 게 정 보 를 전달 할 수 있다 고 정의 했다.예 를 들 어 푸 시 서 비 스 는 수도관 같은 데이터 파 이 프 를 만 드 는 것 과 같다.
서버 의 데 이 터 를 얻 으 려 면 보통 두 가지 방법 이 있 습 니 다. 첫 번 째 는 클 라 이언 트 PULL (당 김) 방식 입 니 다. 즉, 일정 시간 마다 서버 에 가서 데이터 가 있 는 지 확인 하 는 것 입 니 다.두 번 째 는 서버 가 데이터 가 있 을 때 클 라 이언 트 에 게 자발적으로 보 내 는 서버 PUSH (푸 시) 방식 이다.분명 한 것 은 PULL 방안 의 장점 은 간단 하지만 실시 성 이 떨어진다 는 것 이다. 반면에 PUSH 방안 은 TCP 의 긴 연결 방식 을 바탕 으로 이 루어 지고 메시지 의 실시 성 이 좋 지만 클 라 이언 트 와 서버 의 긴 연결 심 박 수 를 유지 해 야 한다. 현재 주류 의 푸 시 실현 방식 은 모두 PUSH 를 바탕 으로 하 는 방안 이다.
구체 적 인 푸 시 실현 방식 은 다음 과 같은 세 가지 가 있다.
1. 폴 링 방식 (PULL)
클 라 이언 트 와 서버 가 정기 적 으로 연결 을 구축 하고 메시지 큐 등 방식 으로 새로운 소식 이 있 는 지 조회 해 야 합 니 다. 연결 과 조회 의 빈 도 를 조절 해 야 합 니 다. 빈도 가 너무 느 리 거나 너무 빠 르 면 일부 소식 이 제때에 업데이트 되 지 않 고 너무 빠 르 면 데이터 압력 이 발생 할 수 있 습 니 다. 심각 할 때 백 스테이지 시스템 이 다운 되 거나 고객 센터 의 카드 가 걸 릴 수도 있 습 니 다.
2. 문자 푸 시 방식 (SMS PUSH)
문자 로 푸 시 메 시 지 를 보 내 고 클 라 이언 트 에 문자 차단 모듈 (주로 안 드 로 이 드 플랫폼 을 대상 으로) 을 삽입 하면 문자 메 시 지 를 차단 하고 그 중의 내용 을 추출 하여 App 응용 프로그램 에 전달 할 수 있다. 이 방안 은 운영 자의 짧 은 메 시 지 를 통 해 가장 좋 은 실시 성과 도 착 률 을 확보 할 수 있 지만 이 방안 은 원가 에 대한 요구 가 비교적 높다.개발 자 는 모든 SMS 에 비용 을 지불해 야 합 니 다.
3. 긴 연결 방식 (PUSH)
TCP 장 연결 의 실현 방식 을 바탕 으로 클 라 이언 트 는 주동 적 으로 서버 와 TCP 장 연결 을 한 후에 정기 적 으로 서버 에 심장 박동 패 키 지 를 보 내 연결 을 유지 하고 메시지 가 있 을 때 서버 는 이미 만들어 진 TCP 연결 을 통 해 클 라 이언 트 에 게 알 립 니 다.비록 긴 연결 도 일정한 비용 을 초래 할 수 있 지만 현 재 는 인 프 라 시설 의 업그레이드 와 보완 (예 를 들 어 더 낮은 데이터 요금, 더 큰 대역 폭 자원) 에 따라 오히려 가장 좋 은 방식 이 되 었 다.그러나 클 라 이언 트 의 수량 과 메시지 의 병발 량 이 증가 함 에 따라 메시지 서버 의 성능 과 안정성 에 대한 요구 에 대해 매우 큰 시련 을 제기 했다.
실전
다음은 두 가지 서로 다른 PUSH 기술 로 반응 식 API 를 구축 하 겠 습 니 다.
필드 1: 서버 푸 시 이벤트
서버 푸 시 이벤트 (Server - Sent Events, SSE) 는 서버 측 이 클 라 이언 트 에 데 이 터 를 계속 푸 시 할 수 있 도록 합 니 다.W3C 의 추천 규범 으로서 SSE 는 브 라 우 저 에서 도 광범 위 하 게 지원 되 며 IE 를 제외 한 다른 브 라 우 저 에서 도 지원 된다.IE 에서 도 poly fill 라 이브 러 리 를 사용 하여 지원 할 수 있 습 니 다.
WebFlux 에서 SSE 를 만 드 는 서버 쪽 은 매우 간단 합 니 다.되 돌아 오 는 대상 의 유형 이 Flux 이면 SSE 규범 이 요구 하 는 형식 으로 자동 으로 응답 을 보 냅 니 다.
API 컨트롤 러 SseController 를 만 듭 니 다. 코드 는 다음 과 같 습 니 다.
@Controller
@SpringBootApplication
public class SseController {

    public static void main(String[] args) {
        SpringApplication.run(SseController.class,args);
    }

    @GetMapping("/randomNumbers")
    public Flux> randomNumbers() {
        return Flux.interval(Duration.ofSeconds(1))
                .map(seq -> Tuples.of(seq, ThreadLocalRandom.current().nextInt()))
                .map(data -> ServerSentEvent.builder()
                        .event("random")
                        .id(Long.toString(data.getT1()))
                        .data(data.getT2())
                        .build());
    }

}

코드 에 있 는 SseController 는 SSE 컨트롤 러 를 사용 하 는 예제 입 니 다.이 방법 중 random Numbers () 는 1 초 마다 무 작위 SSE 점 수 를 만 드 는 것 을 나타 낸다.우 리 는 클래스 ServerSentEvent. Builder 를 사용 하여 ServerSentEvent 대상 을 만 들 수 있 습 니 다.여기 서 우 리 는 이벤트 이름 을 random 이 라 고 지정 하고 모든 이벤트 의 식별 자 와 데 이 터 를 지정 합 니 다.이벤트 의 식별 자 는 증가 하 는 정수 이 고 데 이 터 는 발생 하 는 임 의 수 입 니 다.
테스트 1 필드
테스트 클래스 SSEClient 코드:
public class SSEClient {
    public static void main(final String[] args) {
        final WebClient client = WebClient.create();
        client.get()
                .uri("http://localhost:8080/sse/randomNumbers")
                .accept(MediaType.TEXT_EVENT_STREAM)
                .exchange()
                .flatMapMany(response -> response.body(BodyExtractors.toFlux(new ParameterizedTypeReference>() {
                })))
                .filter(sse -> Objects.nonNull(sse.data()))
                .map(ServerSentEvent::data)
                .buffer(11)
                .doOnNext(System.out::println)
                .blockFirst();
    }
}

테스트 코드 에서 저 희 는 WebClient 를 사용 하여 SSE 를 방문 합 니 다. 전송 요청 부분 은 REST API 에 접근 하 는 것 과 같 고 다른 점 은 HTTP 응답 에 대한 처리 에 있 습 니 다.SSE 서비스의 응답 은 메시지 흐름 이기 때문에 저 희 는 flatMapMany 모 노 를 Flux 대상 으로 바 꾸 고 방법 BodyExtractors.toFlux 을 통 해 완성 해 야 합 니 다. 그 중의 매개 변 수 는 new Parameterized TypeReference 입 니 다.
장면 2: WebSocket
웹 소켓 은 클 라 이언 트 와 서버 측의 양 방향 통신 을 지원 합 니 다.클 라 이언 트 와 서버 간 의 상호작용 방식 이 비교적 복잡 할 때 웹 소켓 을 사용 할 수 있다.웹 소켓 은 주류 브 라 우 저 에서 모두 지원 을 받 았 다.
웹 플 럭스 도 웹 소켓 서버 를 만 드 는 데 지원 했다.서버 쪽 에서 우 리 는 웹 소켓 통신 을 처리 하기 위해 인터페이스 org.springframework.web.reactive.socket.WebSocketHandler 를 실현 해 야 한다.인터페이스 웹 소켓 Handler 의 방법 handle 의 인 자 는 인터페이스 웹 소켓 Session 의 대상 으로 클 라 이언 트 정 보 를 얻 고 메 시 지 를 받 으 며 메 시 지 를 보 낼 수 있 습 니 다.
API 컨트롤 러 WebsocketController 를 만 듭 니 다. 코드 는 다음 과 같 습 니 다.
@SpringBootApplication
public class WebsocketController {

    public static void main(String[] args) {
        SpringApplication.run(WebsocketController.class,args);
    }

    @Bean
    public HandlerMapping webSocketMapping(final EchoHandler echoHandler) {
        final Map map = new HashMap<>(1);
        map.put("/echo", echoHandler);

        final SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
        mapping.setOrder(Ordered.HIGHEST_PRECEDENCE);
        mapping.setUrlMap(map);
        return mapping;
    }

    @Bean
    public WebSocketHandlerAdapter handlerAdapter() {
        return new WebSocketHandlerAdapter();
    }

}

코드 에 있 는 EchoHandler 는 받 는 메시지 마다 "ECHO - >" 를 추가 하여 보 냅 니 다."접두사 응답 메시지 입 니 다. WebSocketsession 의 receive 방법의 반환 값 은 Flux 대상 으로 받 은 메시지 흐름 을 표시 합 니 다. send 방법의 매개 변 수 는 Publisher 대상 으로 보 낼 메시지 흐름 을 표시 합 니 다. handle 방법 에 서 는 map 를 사용 하여 receive 측 법 에 포 함 된 Flux 에 포 함 된 메 시 지 를 계속 처리 한 다음 send 방법 으로 직접 처리 합 니 다."발송
테스트 2 필드
테스트 클래스 WSClient 코드:
public class WSClient {

    public static void main(final String[] args) {
        final WebSocketClient client = new ReactorNettyWebSocketClient();
        client.execute(URI.create("ws://localhost:8080/echo"), session ->
                session.send(Flux.just(session.textMessage("Hello")))
                       .thenMany(session.receive().take(1).map(WebSocketMessage::getPayloadAsText))
                        .doOnNext(System.out::println)
                        .then())
                .block(Duration.ofMillis(5000));
    }

}

웹 소켓 에 접근 하려 면 웹 클 라 이언 트 를 사용 할 수 없고 전문 적 인 웹 소켓 클 라 이언 트 를 사용 해 야 합 니 다. Spring Boot 의 웹 Flux 템 플 릿 에 서 는 기본적으로 Reactor Netty 라 이브 러 리 를 사용 합 니 다. Reactor Netty 라 이브 러 리 는 웹 소켓 클 라 이언 트 의 구현 을 제공 합 니 다. 웹 소켓 클 라 이언 트 의 execute 방법 은 웹 소켓 서버 와 연결 되 고 주어진 웹 소켓 Handler 대상 을 실행 합 니 다.웹 소켓 Handler 구현 에 있어 서 먼저 웹 소켓 Session 의 send 방법 을 통 해 서버 에 문자열 Hello 를 보 낸 다음 receive 방법 으로 서버 의 응답 과 출력 을 기다 리 고 있 습 니 다. 방법 take (1) 는 클 라 이언 트 가 서버 에서 보 낸 첫 번 째 메시지 만 가 져 오 는 것 을 나타 내 는 역할 을 합 니 다.
작은 매듭
푸 시 기술 의 상용 실현 방식 에 대한 소 개 를 통 해 우 리 는 푸 시 기술 의 내재 적 특징 을 알 게 되 었 고 두 가지 간단 한 예 를 통 해 반응 식 프로 그래 밍 의 응용 장면 을 더욱 배 웠 다. 우 리 는 어떤 기술 도 특정한 장면 에서 발생 하고 사용 해 야 한 다 는 것 을 더욱 잘 알 수 있다. 절대적 인 좋 고 나 쁨 은 없다. 단지 우리 가 그것 을 어떻게 잘 다 루 는 지 에 달 려 있다.
예제 코드: boot - flux
참고 자원
1. Spring Boot 공식 문서 2, WebFlux 참고 지침 3, Reactor Netty
나의 다른 통과 문 - 지속 적 인 실천, 우 리 는 함께 간다.

좋은 웹페이지 즐겨찾기