大模型: 流式会话的实现方式

文章目录

  • 一、什么是流式会话
  • 二、长轮询(Long Polling)
  • 三、WebSocket
    • 1、特定
    • 2、工作原理
    • 3、使用场景
  • 四、Server-Sent Events(SSE)
    • 1、特点
    • 2、工作流程
    • 3、使用场景
    • 4、OkHttpClient-sse
  • 五、gPRC流
  • 六、HTTP/2流

一、什么是流式会话

流式会话(Streamed Conversation)指的是在人机交互的过程中,以流的形式进行信息传输,而不是将信息一次性返回。常见的应用场景:如微信的多人聊天、chatgpt的人机交互等。流式会话有如下技术进行实现:

  • 长轮询
  • WebSocket
  • Server-Sent Events
  • gRPC流
  • HTTP/2流

二、长轮询(Long Polling)

长轮询是一种服务器推送技术。当客户端想服务器端发送请求,会建立连接服务器端发送数据,客户端会对数据进行即时处理,如果没有数据连接挂起但是不会中断。流程为:

  • 客户端发送请求到服务器端
  • 服务器在未返回数据之前不会关闭连接
  • 有数据试服务器端发送数据到客户端
  • 客户端重新发起新的请求

三、WebSocket

WebSocke是一种协议提供了全双工的通信渠道,使得客户端和服务器端可以进行双向通信,不需要进行频繁的轮询等机制进行检查更新。

1、特定

  • 全双工通信: 客户端和服务器端之间可以进行相互通信。
  • 降低通信开销(持久连接):建立连接后,如果客户端和服务端不关闭始终保持打开状态,减少频繁建立连接和关闭连接的开销,提高了性能。
  • 实时通信:数据快速响应。

2、工作原理

  • 握手:客户端和服务端通过握手建立连接
  • 数据传输:建立连接后,如果客户端和服务端不关闭始终保持打开状态,客户端和服务器端可以自由的发送和接收数据。

3、使用场景

  • 即使通信:微信聊天、在线客户系统等
  • 在线游戏:多玩家游戏
  • 协作应用:线下文档实时编辑

四、Server-Sent Events(SSE)

SSE指的是服务器端向客户端发送消息的单向通信(半双工通信)技术,属于HTML5的一部分,它是基于HTTP协议进行工作使用起来比较简单。对于那些不需从客户端到服务器端实时通信的场景来说是一个轻量级的选择。

1、特点

  • 单向通信:SSE仅仅支持服务端到客户端的单向通信。
  • 文本格式:仅仅支持文本格式放消息
  • 自动重连:如果连接断开,浏览器会尝试重新建立连接
  • 基于HTTP协议

2、工作流程

  • 客户端(浏览器、Postman等)发送一个普通的HTTP请求来初始化SSE连接。
  • 服务器建立连接并打开,然后以文本格式向客户端持续发送数据,直到连接关闭。
  • 客户端接收消息并处理。

3、使用场景

  • 人机交互:chatGPT
  • 实时通知:前端页面刷新按钮,直到全部刷新完成,服务器端发送刷新完成消息前端。

4、OkHttpClient-sse

(1)maven依赖

        
            com.squareup.okhttp3
            okhttp-sse
            4.9.3
        

(2)普通的HTTP请求

public class Test {
  public static void main(String[] args) throws ClassNotFoundException {
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(1000, TimeUnit.SECONDS)
                .writeTimeout(1000, TimeUnit.SECONDS)
                .readTimeout(1000, TimeUnit.SECONDS)
                .build();

        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json;charset=utf-8"), 请求体字符串格式);
        Request request = new Request.Builder()
                .url(url)
                .addHeader("key", "value")
                .post(requestBody)
                .build();

        try {
            Response execute = client.newCall(request).execute();
                if (execute.code() != 200) {
                throw new RuntimeException(execute.message());
            }
            ResponseBody body = execute.body();
            System.out.println(execute.code());
            System.out.println(JSONObject.toJSONString(body));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

(3)HTTP - Response

public void TestOkHttpSse() throws InterruptedException {
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(1000, TimeUnit.SECONDS)
                .writeTimeout(1000, TimeUnit.SECONDS)
                .readTimeout(1000, TimeUnit.SECONDS)
                .build();

        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json;charset=utf-8"), 请求体字符串格式);
        Request request = new Request.Builder()
                .url(url)
                .addHeader("key", "value")
                .post(requestBody)
                .build();
        CountDownLatch countDownLatch = new CountDownLatch(1);

        EventSource.Factory factory = EventSources.createFactory(client);
        StringBuffer output = new StringBuffer();
        // 自定义监听器
        EventSourceListener eventSourceListener = new EventSourceListener() {
            @Override
            public void onOpen(EventSource eventSource, Response response) {
                System.out.println(JSONObject.toJSONString(response));
            }

            @Override
            public void onEvent(EventSource eventSource, String id, String type, String data) {
                //   接受消息 data
                 httpServletResponse.getWriter().write(data + "\n");
                 httpServletResponse.getWriter().flush();
                System.out.println(JSONObject.toJSONString(data));
            }

            @Override
            public void onClosed(EventSource eventSource) {
                System.out.println("sse close: {}");
                countDownLatch.countDown();
            }

            @Override
            public void onFailure(EventSource eventSource, Throwable t, Response response) {
                countDownLatch.countDown();
            }
        };

        // 创建事件
        EventSource eventSource = factory.newEventSource(request, eventSourceListener);
        countDownLatch.await();
    }

(4)HTTP-SSE

public class OkHttpSSEListener extends EventSourceListener {

    private static final Logger LOGGER = LoggerFactory.getLogger(OkHttpSSEListener.class);
    @Getter
    private final CountDownLatch countDownLatch = new CountDownLatch(1);
    private final SseEmitter emitter;

    @Getter
    private final List<String> contextStreams = new ArrayList<>();

    @Getter
    private String errorMessage;

    public OkHttpSSEListener(SseEmitter emitter) {
        requireNonNull(emitter, "httpServletResponse is null");
        this.emitter = emitter;
    }


    @Override
    public void onClosed(@NotNull EventSource eventSource) {
        LOGGER.info("-- OpenAi see close ... --");
        emitter.complete();
        countDownLatch.countDown();
    }

    @Override
    public void onEvent(@NotNull EventSource eventSource, @Nullable String id, @Nullable String type, @NotNull String data) {
        try {
            LOGGER.info("chat stream data: {}", data);
            if ("[DONE]".equals(data)) {
                LOGGER.info("-- OpenAI return data success --");
            } else {
                contextStreams.add(data);
            }

            emitter.send(data, MediaType.APPLICATION_JSON);
        } catch (IOException e) {
            countDownLatch.countDown();
            LOGGER.error(e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public void onFailure(@NotNull EventSource eventSource, @Nullable Throwable t, @Nullable Response response) {
        try {
            if (Objects.isNull(response)) {
                throw new RuntimeException(String.format("-- OpenAi sse connection exception: %s --", t.getMessage()), t);
            } else {
                ResponseBody body = response.body();
                if (Objects.isNull(body)) {
                    throw new RuntimeException(String.format("-- OpenAi sse connection exception: %s --", response));
                } else {
                    throw new RuntimeException(String.format("-- OpenAi sse connection exception: %s --", body.string()));
                }
            }
        } catch(Exception e) {
            LOGGER.error(e.getMessage(), e);
            errorMessage = e.getMessage();
        }
        countDownLatch.countDown();
        eventSource.cancel();

    }

    @Override
    public void onOpen(@NotNull EventSource eventSource, @NotNull Response response) {
        LOGGER.info("-- OpenAI establishes sse connection... --");
    }

五、gPRC流

支持流式传输的RPC远程调用框架,gRPC流既可以是单向的也可以是双向的。

六、HTTP/2流

引入了多路复用功能,允许连接发送多个请求和响应,不会相互阻塞。

你可能感兴趣的:(【大模型】,java-ee)