Java对接Deepseek api - 流式响应

首先需要登录Deepseek开放平台,申请 appkey。

Deepseek api文档

java 版 demo代码如下所示:

@Slf4j
public class DeepSeekApiDemo {
    private static final MediaType MEDIA_APPLICATION_JSON = MediaType.parse("application/json; charset=utf-8");
    private static final String URL = "https://api.deepseek.com/chat/completions";
    private static final String APP_kEY = "你的appkey";
    private static final String MODEL = "deepseek-chat";
    private static final String DONE = "[DONE]";

    public static void main(String[] args) {
        String query = "你是谁";

        long start = System.currentTimeMillis();

        DeepSeekRequest request = DeepSeekRequest.make(query);
        StringBuilder allContent = new StringBuilder();

        OkHttpClient client = initOkHttpClient();
        EventSource.Factory factory = EventSources.createFactory(client);
        Request httpRequest = new Request.Builder()
                .url(URL)
                .addHeader("Authorization", "Bearer " + APP_kEY)
                .post(RequestBody.create(JSON.toJSONString(request), MEDIA_APPLICATION_JSON))
                .build();

        factory.newEventSource(httpRequest, new EventSourceListener() {
            private boolean receiveResponse = false;
            @Override
            public void onClosed(@NotNull EventSource eventSource) {
                super.onClosed(eventSource);
                log.info("sse onClosed.");
                eventSource.cancel();
            }

            @Override
            public void onEvent(@NotNull EventSource eventSource, @Nullable String id, @Nullable String type, @NotNull String data) {
                super.onEvent(eventSource, id, type, data);
                if (! receiveResponse) {
                    receiveResponse = true;
                    log.info("receive first frame, rt: {}ms", System.currentTimeMillis() - start);
                }

                if (DONE.equals(data)) {
                    log.info("sse event response finish, rt: {}ms, allResponse: {}", System.currentTimeMillis() - start, allContent);
                    // 结束流式响应
                    return;
                }
                try {
                    DeepSeekResponse response = JSON.parseObject(data, DeepSeekResponse.class);
                    if (CollectionUtils.isEmpty(response.getChoices())) {
                        return;
                    }
                    DeepSeekChoice choice = response.getChoices().get(0);
                    if (choice.getDelta() == null) {
                        return;
                    }
                    String content = choice.getDelta().getContent();
                    if (StringUtils.isNotEmpty(content)) {
                        log.info("sse event response content: {}", content);
                        allContent.append(content);
                    }
                } catch (Exception e) {
                    log.error("parse response fail. ", e);
                }
            }

            @Override
            public void onFailure(@NotNull EventSource eventSource, @Nullable Throwable t, @Nullable Response response) {
                super.onFailure(eventSource, t, response);
                int code = response != null ? response.code() : 400;
                String msg = response != null ? response.message() : "";
                String body = "";
                if (response != null)  {
                    body = Optional.of(response).map(Response::body).map(responseBody -> {
                        try {
                            return responseBody.string();
                        } catch (IOException e) {
                            log.error("parse response.body error. ", e);
                            return "";
                        }
                    }).orElse("");
                }

                log.info("sse onFailure code:{} msg:{} body:{}. ", code, msg, body, t);
                eventSource.cancel();
            }

            @Override
            public void onOpen(@NotNull EventSource eventSource, @NotNull Response response) {
                super.onOpen(eventSource, response);
                log.info("sse onOpen.");
            }
        });

        log.info("request ok");
    }

    private static OkHttpClient initOkHttpClient() {
        Dispatcher dispatcher = new Dispatcher();
        dispatcher.setMaxRequests(12);
        return new OkHttpClient.Builder()
                .connectTimeout(1000, TimeUnit.MILLISECONDS)
                .writeTimeout(16000, TimeUnit.MILLISECONDS)
                .readTimeout(5000, TimeUnit.MILLISECONDS)
                .dispatcher(dispatcher)
                .build();
    }

    @Setter
    @Getter
    private static class DeepSeekRequest {
        private String model = MODEL;
        /**
         * 历史对话和当前对话都放在message中,最后一条为当前轮对话。由客户端发送的query,role为user、系统回复为 assistant
         */
        private List<DeepSeekContext> messages;
        private boolean stream = true;

        private DeepSeekRequest() {
        }

        /**
         * 构建请求
         * @param query 请求
         * @return DeepSeekRequest
         */
        public static DeepSeekRequest make(String query) {
            DeepSeekRequest request = new DeepSeekRequest();
            DeepSeekContext message = new DeepSeekContext();
            message.setRole("user");
            message.setContent(query);
            request.setMessages(Lists.newArrayList(message));
            return request;
        }
    }

    @Setter
    @Getter
    private static class DeepSeekContext {
        private String role;
        private String content;
    }

    @Setter
    @Getter
    private static class DeepSeekResponse {
        private String id;
        private String object;
        private long created;
        private String model;
        @JSONField(name = "system_fingerprint")
        private String systemFingerprint;
        private List<DeepSeekChoice> choices;
    }

    @Setter
    @Getter
    private static class DeepSeekChoice {
        private int index;
        private String logprobs;
        @JSONField(name = "finish_reason")
        private String finishReason;
        private DeepSeekContext delta;
    }
}
### Java 中处理流式响应的方法 在Java中,处理流式响应可以通过多种方式进行优化,以减少等待时间和降低内存消耗,从而提升系统性能[^3]。 #### 使用Servlet 3.1及以上版本支持异步I/O操作 对于传统的Web应用程序来说,可以利用Servlet 3.1引入的支持来实现实时推送数据到客户端的功能。这种方式允许服务器端持续不断地向浏览器发送更新而不需要关闭连接: ```java @WebServlet(urlPatterns = "/stream", asyncSupported = true) public class StreamServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { AsyncContext ctx = req.startAsync(); // 设置响应头 resp.setContentType("text/event-stream"); PrintWriter writer = resp.getWriter(); new Thread(() -> { try { for (int i = 0; i < 5; ++i) { String message = "data: Message number " + i + "\n\n"; writer.write(message); writer.flush(); Thread.sleep(1000); // Simulate delay between messages. } ctx.complete(); } catch (InterruptedException e) { throw new RuntimeException(e); } }).start(); } } ``` 此代码片段展示了如何创建一个简单的SSE(Server-Sent Events)接口,在每次迭代时都会立即将消息写入输出流并刷新缓冲区,使得客户端能够立即接收到新产生的事件。 #### 利用Spring WebFlux框架构建反应式应用 当涉及到更复杂的场景比如微服务架构下的RESTful API开发时,则推荐采用基于Reactor库的Spring WebFlux模块来进行非阻塞式的HTTP请求/响应处理: ```java @RestController @RequestMapping("/api/stream") public class ReactiveController { private final Flux<String> dataStream; public ReactiveController() { this.dataStream = Flux.interval(Duration.ofSeconds(1)) .map(seq -> "Item-" + seq); } @GetMapping(value="/items", produces=MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<String> streamItems(){ return dataStream; } } ``` 上述例子定义了一个`@RestController`类中的方法用于提供无限数量的数据项作为SSE源供订阅者消费;这里的关键在于使用了`Flux<T>`这种Publisher类型的对象代表可能发出多个元素序列,并且指定了媒体类型为"text/event-stream"以便于兼容大多数现代浏览器解析该格式的内容。 #### 结合gRPC实现高效的双向流传输 如果项目需求涉及到了高性能、低延迟的服务间通讯,那么gRPC是一个不错的选择因为它原生就提供了三种不同形式的流模式——单次请求-多次回复(`server streaming`)、多次请求-一次回复(`client streaming`)以及全双工(`bidirectional streaming`)。下面给出的是关于后者的一个简单实例说明怎样编写相应的`.proto`文件和服务端逻辑[^2]: .proto 文件定义: ```protobuf service StudentService { rpc GetStudentsWrapperByAges(stream StudentRequest) returns (stream StudentResponseList) {} } message StudentRequest { ... } message StudentResponseList { repeated Student students = 1; } ``` 服务端实现: ```java class MyStudentServiceImpl extends StudentServiceGrpc.StudentServiceImplBase { @Override public StreamObserver<StudentRequest> getStudentsWrapperByAges(StreamObserver<StudentResponseList> responseObserver){ AtomicReference<List<Student>> studentBuffer = new AtomicReference<>(new ArrayList<>()); return new StreamObserver<>() { @Override public void onNext(StudentRequest request) { List<Student> matchingStudents = findMatchingStudents(request.getAge()); synchronized(studentBuffer){ studentBuffer.get().addAll(matchingStudents); if(!studentBuffer.get().isEmpty()){ responseObserver.onNext( StudentResponseList.newBuilder() .addAllStudents(studentBuffer.get()) .build() ); studentBuffer.set(new ArrayList<>()); } } } @Override public void onError(Throwable t) {} @Override public void onCompleted() { responseObserver.onCompleted(); } }; } } ``` 这段程序实现了对来自客户端的一系列年龄范围查询请求作出即时反馈的能力,每当有新的匹配结果加入临时列表之后就会立刻打包成一条完整的应答信息推送给对方直到所有输入都被处理完毕为止。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值