使用Spring Boot轻松实现流式AI输出的步骤

 更新时间:2025年02月26日 10:37:10   作者:夜阑卧听风吹雨,铁马冰河入梦来  
本文介绍了如何使用Spring Boot和WebFlux实现流式AI输出,通过非阻塞I/O、反应式编程和函数式路由等技术,优化了AI应用的响应速度,提升了用户体验,感兴趣的朋友一起看看吧

1、背景

随着AI的快速发展,越来越多的AI应用诞生了,但是AI也有响应慢的问题,一般不能够即时响应,为了优化用户体验,现在大部分AI应用都是实现了打字机的效果,那么这种效果是如何实现的呢?今天我们先看一下后端的实现逻辑。

代码流程是后端发出请求,请求智能体或AI模型暴露的流式接口,然后返回一个流式接口。

为什么不直接前端请求AI接口,因为有的AI接口在前端直接请求,可能会出现跨域问题。因为AI接口返回的响应中没有包含跨域的Access-Control-Allow-Origin。

2、实现步骤

1、引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

2、使用webClient发起对AI接口请求

代码中的URL,请求头、请求体或者请求方法都可以按照对应的AI接口文档进行替换。

WebClient webClient = WebClient.create();
        Flux<String> resultFlux = webClient.post()
                .uri(URL)  //请求url
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .header(HttpHeaders.AUTHORIZATION, "Bearer "+ API_KEY) // 添加认证头部
                .bodyValue(requestBody)//请求体
                .retrieve()
                .bodyToFlux(String.class);  //返回流式结果

3、启动类需要添加@EnableAsync注解

4、如果工程中有过滤器,需要进行配置

我的工程启动后报错

Async support must be enabled on a servlet and for all filters involved in async request processing. This is done in Java code using the Servlet API or by adding "<async-supported>true</async-supported>" to servlet and filter declarations in web.xml.

大概意思是异步支持必须在servlet和所有的过滤器中被标注成是enabled

Servlet和Filter声明:如果您使用的是基于XML的配置,可以在web.xml文件中的servlet和filter声明中添加<async-supported>true</async-supported>元素来启用异步支持

<servlet>
    <servlet-name>myServlet</servlet-name>
    <servlet-class>com.example.MyAsyncServlet</servlet-class>
    <async-supported>true</async-supported>
</servlet>
<filter>
    <filter-name>myFilter</filter-name>
    <filter-class>com.example.MyAsyncFilter</filter-class>
    <async-supported>true</async-supported>
</filter>

如果您使用的是基于注解的配置或Java配置类,可以通过实现javax.servlet.Servlet接口并覆盖isAsyncSupported()方法返回true,或者通过@WebServlet和@WebFilter注解的asyncSupported属性来设置。我使用的是这种方式

@WebServlet(urlPatterns = "/async", asyncSupported = true)
public class MyAsyncServlet extends HttpServlet {
    // ...
}
@WebFilter(urlPatterns = "/*", asyncSupported = true)
public class MyAsyncFilter implements Filter {
    // ...
}

5、postman发送请求后结果

controller类接口

 @RequestMapping(method = RequestMethod.POST, value = "/getAIResult",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    Flux<String> getAIResult(@RequestBody String content){
        // 构建请求体
        HashMap<String, Object> requestBody = new HashMap<>();
        requestBody.put("model", "6bbdf08d55244bd9be24052ded2a58ef");
        requestBody.put("context",0);
        requestBody.put("stream", true);
        List<HashMap<String, String>> messages = new ArrayList<>();
        HashMap<String, String> message = new HashMap<>();
        message.put("role", "user");
        message.put("content", content);
        messages.add(message);
        requestBody.put("messages", messages);
        WebClient webClient = WebClient.create();
        Flux<String> resultFlux = webClient.post()
                .uri(URL)
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .header(HttpHeaders.AUTHORIZATION, "Bearer "+ API_KEY) // 添加认证头部
                .bodyValue(requestBody)
                .retrieve()
                .bodyToFlux(String.class);
        // 输出响应结果
        // // 订阅响应以触发实际的 HTTP 请求
        // resultFlux.subscribe(
        //         response -> System.out.println("Response received: " + response),
        //         error -> System.err.println("Error occurred: " + error.getMessage())
        // );
        return resultFlux;
    }

3、使用技术介绍

WebFlux

WebFlux模块是Spring 5引入的一部分,旨在提供一种新的方式来构建响应式的Web应用程序。它允许你以异步和非阻塞的方式处理HTTP请求,这在处理高并发场景时可以显著提高性能。

WebFlux的特点

  • 非阻塞I/O:与传统的Servlet API不同,WebFlux使用的是非阻塞I/O模型,这意味着它可以更有效地利用线程资源。
  • 反应式编程:WebFlux内置了对反应式编程的支持,主要通过Reactor库实现,使得编写和处理异步代码更加容易。
  • 函数式路由:除了注解驱动的控制器,WebFlux还提供了函数式路由API,让你能够以声明性的方式定义路由规则。

以上来自AI内容生成,看完一头雾水,下方给出介绍

非阻塞I/O

含义:非阻塞I/O(Non-blocking I/O)是一种编程模型,它允许应用程序在等待某些操作完成时不会被阻塞。与传统的Servlet API(如Spring MVC)相比,WebFlux采用了基于事件和回调的非阻塞I/O模型。

  • 传统Servlet API (阻塞I/O):在传统的Servlet环境中,每个HTTP请求都会分配一个线程来处理。如果这个处理过程包含了一个长时间运行的操作(例如数据库查询或网络调用),那么该线程会被阻塞直到操作完成。这意味着线程不能用来处理其他请求,从而降低了服务器的效率。
  • WebFlux (非阻塞I/O):WebFlux使用了Netty这样的异步网络框架,它们可以在不阻塞线程的情况下执行I/O操作。当一个请求涉及到耗时的任务时,它不会阻塞当前的线程;相反,任务完成后会触发相应的回调函数继续处理。这种方式使得单个线程可以处理多个并发请求,极大地提高了资源利用率和服务的吞吐量。

反应式编程

含义:反应式编程(Reactive Programming)是一种面向数据流和变化传播的编程范式。它强调的是通过声明式的代码来描述数据流的变化,并能够对这些变化做出响应。WebFlux内置了对反应式编程的支持,主要通过Project Reactor库实现,这是Spring 5引入的一个核心特性。

  • Reactor库:Reactor提供了两个核心类型——MonoFlux,分别表示0到1个元素的异步序列和0到N个元素的异步序列。开发者可以使用这些类型来构建复杂的异步逻辑,而不需要显式地管理线程或同步问题。
  • 好处
    • 简化异步编程:通过组合操作符(如map, flatMap, filter等),你可以轻松地创建复杂的异步工作流,同时保持代码的简洁性和可读性。
    • 错误处理:Reactor还提供了一套强大的错误处理机制,比如onErrorResumeretry等,使得处理异常情况更加直观。
    • 背压支持:对于生产者-消费者模式中的流量控制,Reactor实现了背压(Backpressure),确保系统不会因为过载而崩溃。

函数式路由

含义:函数式路由是WebFlux提供的另一种定义HTTP端点的方式,除了传统的注解驱动控制器之外。它允许你以一种声明性的、函数式的方式来配置路由规则,这在某些情况下可能比注解更灵活、更具表达力。

  • RouterFunction和HandlerFunction:在函数式路由中,RouterFunction用于定义路由匹配逻辑,而HandlerFunction则负责处理实际的请求。两者结合在一起,可以非常清晰地表达出“如果路径匹配,则执行某个处理器”的意图。

到此这篇关于用Spring Boot轻松实现流式AI输出的文章就介绍到这了,更多相关Spring Boot流式AI输出内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • struts2+spring+ibatis框架整合实现增删改查

    struts2+spring+ibatis框架整合实现增删改查

    这篇文章主要为大家详细介绍了struts2+spring+ibatis框架整合实现增删改查操作,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-07-07
  • 带你快速上手Servlet

    带你快速上手Servlet

    这篇文章主要介绍了带你快速上手Servlet,文中有非常详细的代码示例,对正在学习java的小伙伴们有很好的帮助,需要的朋友可以参考下
    2021-05-05
  • mybatis-plus实现自定义SQL、多表查询与多表分页查询语句实例

    mybatis-plus实现自定义SQL、多表查询与多表分页查询语句实例

    mybatisplus是个很好用的插件,相信小伙伴们都知道,下面这篇文章主要给大家介绍了关于mybatis-plus实现自定义SQL、多表查询与多表分页查询语句的相关资料,需要的朋友可以参考下
    2022-09-09
  • java使用反射访问成员变量的值示例

    java使用反射访问成员变量的值示例

    这篇文章主要介绍了java使用反射访问成员变量的值,结合实例形式分析了java基于反射机制操作类成员变量相关实现技巧,需要的朋友可以参考下
    2019-07-07
  • springboot 错误处理小结

    springboot 错误处理小结

    在 java web开发过程中,难免会有一些系统异常或人为产生一些异常。在 RESTful springboot 项目中如何优雅的处理?下面脚本之家小编给大家带来了springboot 错误处理小结,感兴趣的朋友一起看看吧
    2018-03-03
  • Spring-boot的debug调试代码实例

    Spring-boot的debug调试代码实例

    这篇文章主要介绍了Spring-boot的debug调试代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • Java中内部类使用方法实战案例分析

    Java中内部类使用方法实战案例分析

    这篇文章主要介绍了Java中内部类使用方法,结合具体案例形式分析了Java内部类原理、调用方法及相关使用注意事项,需要的朋友可以参考下
    2019-09-09
  • 解决ThreadLocal获取不到值大坑

    解决ThreadLocal获取不到值大坑

    这篇文章主要介绍了解决ThreadLocal获取不到值大坑
    2023-05-05
  • Java try-with-resource语法使用解析

    Java try-with-resource语法使用解析

    这篇文章主要介绍了Java try-with-resource语法使用解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-03-03
  • 深入了解Java语言中的并发性选项有何不同

    深入了解Java语言中的并发性选项有何不同

    这篇文章主要介绍了深入了解Java语言中的并发性选项有何不同,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,,需要的朋友可以参考下
    2019-06-06

最新评论