서비스 개발

[JAVA] text/event-stream 받는 방법

무우님 2024. 4. 27. 12:48

최근 AI API를 쓰다보면, 텍스트를 스트림으로 받아서 출력하는 경우가 많아졌습니다. 이에 따른 간단한 처리 방법을 작성 합니다.

 

text/event-stream은 Server-Sent Events (SSE)를 사용하는 프로토콜로, 서버에서 클라이언트로 이벤트를 실시간으로 보내는 데 사용됩니다. Java에서 SSE를 처리하려면 HTTP 클라이언트를 사용하여 연결을 만들고 이벤트를 읽을 수 있는 방법이 필요 합니다.

Java에서 text/event-stream을 받는 방법에 대한 간단한 가이드 입니다.

필요한 라이브러리
Java HTTP 클라이언트 (Java 11 이상)

아래는 Java 11 HTTP 클라이언트를 사용하여 text/event-stream을 처리하는 코드 예제 입니다.

 

java.net.http.HttpClient

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.Scanner;

public class SSEClient {

    public static void main(String[] args) {
        // HTTP 클라이언트 생성
        HttpClient client = HttpClient.newBuilder()
                                      .connectTimeout(Duration.ofSeconds(10))
                                      .build();

        // HTTP 요청 생성
        HttpRequest request = HttpRequest.newBuilder()
                                         .uri(URI.create("http://example.com/sse")) // 이 URL은 실제 SSE 이벤트가 제공되는 URL로 변경하세요.
                                         .build();

        // HTTP 응답을 비동기로 받기
        client.sendAsync(request, HttpResponse.BodyHandlers.ofLines())
              .thenAccept(response -> {
                  // 라인 단위로 이벤트 처리
                  response.body().forEach(line -> {
                      System.out.println("Received line: " + line);

                      // 특정 이벤트 시작을 찾음
                      if (line.startsWith("data:")) {
                          // 이벤트 데이터 추출
                          String eventData = line.substring(5).trim();
                          System.out.println("Received event data: " + eventData);
                      }
                  });
              })
              .exceptionally(e -> {
                  System.err.println("Error: " + e.getMessage());
                  return null;
              });

        // 메인 스레드가 종료되지 않도록 대기
        Scanner scanner = new Scanner(System.in);
        System.out.println("Press enter to exit...");
        scanner.nextLine();
    }
}

 

OkHttp

동일한 작동이지만 저는 OkHttp 라이브러리를 더 선호합니다.

 

OkHttp는 Java와 Kotlin에서 HTTP 요청을 보내거나 응답을 받는 데 널리 사용되는 HTTP 클라이언트입니다. OkHttp를 사용하여 text/event-stream을 수신하려면 지속적인 연결을 유지하며 서버에서 보내는 이벤트를 처리할 수 있어야 합니다.

OkHttp로는 아래와 같습니다.

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.concurrent.TimeUnit;

public class SSEClient {

    public static void main(String[] args) {
        // OkHttp 클라이언트 생성
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(10, TimeUnit.SECONDS)
                .readTimeout(0, TimeUnit.SECONDS) // 무한 타임아웃 설정
                .retryOnConnectionFailure(true) // 재연결 시도
                .build();

        // HTTP 요청 생성
        Request request = new Request.Builder()
                .url("http://example.com/sse") // SSE 이벤트가 제공되는 URL로 변경하세요.
                .header("Accept", "text/event-stream")
                .build();

        // HTTP 응답을 수동으로 스트리밍으로 수신
        try (Response response = client.newCall(request).execute()) {
            if (response.isSuccessful()) {
                BufferedReader reader = new BufferedReader(new InputStreamReader(response.body().byteStream()));

                String line;
                while ((line = reader.readLine()) != null) {
                    System.out.println("Received line: " + line);

                    // "data:"로 시작하는 이벤트 데이터 추출
                    if (line.startsWith("data:")) {
                        String eventData = line.substring(5).trim();
                        System.out.println("Received event data: " + eventData);
                    }
                }
            } else {
                System.err.println("Failed to connect: " + response.code());
            }
        } catch (Exception e) {
            System.err.println("Error: " + e.getMessage());
        }
    }
}