从零开始搭建AI网站(6):如何使用响应式编程

这篇具有很好参考价值的文章主要介绍了从零开始搭建AI网站(6):如何使用响应式编程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

响应式编程(Reactive Programming)是一种编程范式,旨在处理异步数据流和事件流。它通过使用观察者模式和函数式编程的概念,将数据流和事件流抽象为可观察的序列,然后通过操作这些序列来实现各种功能。

在响应式编程中,数据流和事件流被视为连续的时间序列,可以通过操作符来转换、过滤和组合它们。这种编程范式的主要优势是它可以简化异步编程,并提供一种声明式的方式来处理数据流和事件流。它还可以提高代码的可读性和可维护性,因为它将复杂的异步逻辑封装在操作符中,使得代码更易于理解和修改。

响应式编程可以应用于各种领域,包括前端开发、后端开发、移动开发等。在前端开发中,响应式编程可以用于处理用户界面的事件流和数据流,使得界面能够动态地响应用户的操作。在后端开发中,响应式编程可以用于处理大量的异步请求和数据流,提高系统的吞吐量和响应速度。

常见的响应式编程框架包括RxJava、RxJS、ReactiveX等。这些框架提供了一系列的操作符和工具,用于处理数据流和事件流,并提供了一种简洁而强大的方式来处理异步编程。

当前响应式编程的典型例子莫过于最近炙手可热的ChatGPT的流式输出了。因为ChatGPT请求响应时间较长,如果采用传统的一直等待全部数据就绪,用户恐怕早就跑光了,而响应式方式则不需要等待所有数据就绪,而只需要有部分数据就绪即可输出,从而极大地提升了用户体验。下面以此为例,来说明实现这种效果的原理(开发语言Java)。

先来看看上文中提到的的三个响应式编程框架:RxJava、RxJS和ReactiveX。它们是三个相关的概念,同时也是不同平台上的实现。

  1. RxJava:RxJava是ReactiveX在Java平台上的实现,它提供了一套丰富的API和操作符,用于处理异步和事件驱动的编程。RxJava是基于观察者模式和迭代器模式的,可以用于处理数据流、事件流和异步任务等。
  2. RxJS:RxJS是ReactiveX在JavaScript平台上的实现,它提供了类似于RxJava的API和操作符,用于处理异步和事件驱动的编程。RxJS可以在浏览器端和Node.js环境中使用,可以处理DOM事件、AJAX请求、定时器等。
  3. ReactiveX:ReactiveX是一个跨平台的响应式编程库,它提供了一套统一的API和操作符,用于处理异步和事件驱动的编程。ReactiveX的目标是提供一种通用的编程模型,使得开发者可以在不同的平台和语言中共享代码和思想。

在Springboot中,另有WebFlux模块可供使用,同时它也可以跟上面的模块一起使用。说起Flux,这里也会涉及到另一个概念:Flowable。其实Flowable和Flux都是响应式流的实现,它们有以下关系:

  1. Flowable是RxJava的一部分,而Flux是Reactor的一部分。RxJava是一个用于Java的响应式编程库,而Reactor是一个用于Java的响应式编程框架。
  2. Flowable是RxJava中的一个类,它实现了Reactive-Streams规范,提供了对背压(backpressure)的支持。Flowable可以处理异步和并发的数据流,并且可以控制数据流的速率,以避免生产者和消费者之间的不匹配。
  3. Flux是Reactor中的一个类,它也实现了Reactive-Streams规范,提供了类似的功能。Flux可以处理异步和并发的数据流,并且可以控制数据流的速率。
  4. Flowable和Flux都提供了一系列的操作符,可以对数据流进行转换、过滤、映射等操作。这些操作符可以帮助开发者处理和操作数据流,使代码更加简洁和可读。

跟tRxJava和Reactor密切相关的开发库之一是WebClien。WebClient是一个用于发送HTTP请求的非阻塞的响应式客户端,它是Reactor项目的一部分。

WebClient提供了一种简洁、灵活和可组合的方式来发送HTTP请求,并处理响应。它可以与RxJava和Reactor的异步和响应式编程模型无缝集成,使得在响应式应用程序中处理HTTP请求变得更加方便和高效。

WebClient可以与RxJava的Flowable一起使用,通过toFlowable()方法将响应转换为Flowable流,从而实现对响应的处理和操作。

WebClient webClient = WebClient.create();
Flowable<String> response = webClient.get()
        .uri("https://example.com")
        .retrieve()
        .bodyToFlux(String.class)
        .toFlowable();

同样,WebClient也可以与Reactor的Flux一起使用,通过bodyToFlux()方法将响应转换为Flux流,从而实现对响应的处理和操作。

WebClient webClient = WebClient.create();
Flux<String> response = webClient.get()
        .uri("https://example.com")
        .retrieve()
        .bodyToFlux(String.class);

下面我们将关注点放在Reactor框架中,在Reactor中,不得不提另一个跟Flux相对的概念:Mono。Flux和Mono是Reactor框架中的两个关键类,它们都是用于处理响应式流的。

  1. Flux是一个表示0到N个元素的响应式流。它可以发出多个元素,并以异步的方式产生这些元素。Flux可以用于处理多个值的数据流,例如从数据库查询结果、文件读取等。
  2. Mono是一个表示0或1个元素的响应式流。它要么发出一个元素,要么不发出任何元素。Mono可以用于处理单个值的数据流,例如从缓存中获取数据、获取单个实体等。
  3. Flux和Mono之间有以下关系:
  • Flux可以被转换成Mono。
Flux<Integer> flux = Flux.just(1, 2, 3);
Mono<Integer> mono = flux.single();
    • Mono可以被转换成Flux。
Mono<Integer> mono = Mono.just(1);
Flux<Integer> flux = mono.flux();

Flux和Mono可以通过一系列的操作符进行转换、过滤、映射等操作,使得对响应式流的处理变得更加灵活和方便。它们是Reactor框架中的核心类,用于构建响应式应用程序。

webClient可以实现复杂的处理逻辑,比如异常处理:

webClient.get()
         .uri(url)
         .retrieve()
         .onStatus(HttpStatus::is4xxClientError, response -> Mono.error(new CustomException("客户端错误")))
         .onStatus(HttpStatus::is5xxServerError, response -> Mono.error(new CustomException("服务器错误")))
         .bodyToMono(String.class)
         .onErrorResume(throwable -> {
             if (throwable instanceof WebClientResponseException) {
                 WebClientResponseException ex = (WebClientResponseException) throwable;
                 // 处理响应异常
             } else {
                 // 处理其他异常
             }
         });

在使用 Spring Boot 的 WebClient 时,bodyToMono 和 bodyToFlux 方法都可以用于将响应体转换为 Mono 或 Flux 对象。

bodyToMono 方法用于将响应体转换为 Mono 对象,适用于响应体只有一个元素的情况,例如返回一个 JSON 对象或者一个字符串。

bodyToFlux 方法用于将响应体转换为 Flux 对象,适用于响应体有多个元素的情况,例如返回一个 JSON 数组或者一个流式数据。

因此,当我们需要处理的响应体只有一个元素时,应该使用 bodyToMono 方法;当我们需要处理的响应体有多个元素时,应该使用 bodyToFlux 方法。

在 Reactor 中,Flux 流结束的实现原理是通过发送一个 onComplete 信号来通知订阅者流已经结束。当 Flux 流中的所有元素都被消费完毕时,会自动发送一个 onComplete 信号。

例如,当我们使用 Flux.range(1, 10) 创建一个包含 1 到 10 的整数序列的 Flux 流时,当订阅者订阅该流并消费完所有元素后,会自动发送一个 onComplete 信号来通知订阅者流已经结束。

在使用 Spring Boot 的 WebClient 时,当我们使用 bodyToFlux 方法将响应体转换为 Flux 对象时,如果响应体是一个流式数据,那么当流式数据传输完毕后,会自动发送一个 onComplete 信号来通知订阅者流已经结束。

webClient.get()
         .uri(url)
         .retrieve()
         .bodyToFlux(String.class)
         .doFinally(signalType -> {
             if (signalType == SignalType.ON_COMPLETE) {
                 System.out.println("流已结束");
             }
         })
         .subscribe();

有了这些基础知识的准备,我们再来看看ChatGPT的响应结果样例。OpenAI的聊天接口是:

http://api.openai.com/v1/chat/completitions。

该接口接受这样的一个请求数据结构:ChatCompletionRequest。其中有个属性stream 可以设定是否采用流输出。默认false。

这个例子是非stream输出,输出格式为:ChatCompletionResponse

$ curl https://api.openai.com/v1/chat/completions -H 'Content-Type: application/json' -H "Authorization: Bearer sk-zDxkX0Na0e63B18c9c6bT3BlBkFJf3De387b398749c5bD1d" -d '{"model": "gpt-3.5-turbo","stream":"false","messages": [{"role": "user", "content": "Hello!"}]}'
{"id":"chatcmpl-7tywVQ4vSPzs8yuZy5FqvL0CX07W0","object":"chat.completion","created":1693576659,"model":"gpt-3.5-turbo-0613","choices":[{"index":0,"message":{"role":"assistant","content":"Hello

这个例子是stream输出,输出结构体为:字符串格式的ChatCompletionResponse:

curl https://api.openai.com/v1/chat/completions -H 'Content-Type: application/json' -H "Authorization: Bearer sk-zDxkX0Na0e63B18c9c6bT3BlBkFJf3De387b398749c5bD1d" -d '{"model": "gpt-3.5-turbo","stream":"true","messages": [{"role": "user", "content": "Hello!"}]}'

从零开始搭建AI网站(6):如何使用响应式编程,响应式编程,Reactor

比较stream和非stream的输出区别,有一下几点:

1.非stream 输出只有一条记录;stream 有若干条,取决于响应内容大小;

2. 非stream 输出包含消耗的tokens数量,stream 没有;

3. 非stream 输出结果是json格式的ChatCompletionResponse结构,stream 输出j格式类似:data:str(ChatCompletionResponse),同时以data:[NONE]结尾;

结合上面的知识,我们就能实现上述功能:

public Publisher<String> generateChatCompletion(ChatCompletionRequest chatCompletionRequest) {
        WebClient.ResponseSpec responseSpec = webClient.post()
                .uri(this.apiUrl + "/chat/completions").header("Authorization", "Bearer " + this.apiKey)//                .accept(MediaType.TEXT_EVENT_STREAM)                
                .bodyValue(chatCompletionRequest)
                .retrieve();
        if (chatCompletionRequest.getStream())
            return                    
            responseSpec.bodyToFlux(ChatCompletionResponse.class)
                            .onErrorResume(error -> {
                                // 异常处理逻辑 
                                logger.error("bodyToFlux error: {}", error);
                                return Flux.empty();
                            })
                            .filter(response -> {
                                ChatMessage message = response.getChoices().get(0).getMessage();
                                if (message != null) {
                                    String content = message.getContent();
                                    return StringUtils.isNotEmpty(StringUtils.trim(content));
                                }
                                return false;
                            })
                            .mapNotNull(response -> {
                                try {
                                    return objectMapper.writeValueAsString(response);
                                } catch (JsonProcessingException e) {
                                    logger.error(e);
                                    return null;
                                }
                            }).concatWithValues("[DONE]");
        else            
        return     
        responseSpec.bodyToMono(ChatCompletionResponse.class)
                            .onErrorResume(error -> {
                                // 异常处理逻辑   
                                logger.error("bodyToMono error: {}", error);
                                return Mono.empty();
                            }).mapNotNull(response -> {
                                try {
                                    return objectMapper.writeValueAsString(response);
                                } catch (JsonProcessingException e) {
                                    logger.error(e);
                                    return null;
                                }
                            });
    }

Publisher是一个通用的概念,它代表一个发布者,可以发布数据或事件。在Spring WebFlux中,Flux和Mono都是Publisher的实现类。

试用地址:https://chatgpt-discount.zeabur.app文章来源地址https://www.toymoban.com/news/detail-693509.html

到了这里,关于从零开始搭建AI网站(6):如何使用响应式编程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 一篇从零开始、步骤完整的网站搭建教程(全篇7000字、102张截图说明,力求每一个人都能看懂,附源码)

    从今年八月开始到现在自己也是从0开始做了有两个网站: 这中间也经常有不了解的地方需要去查。其实网上的资料也不少 但可能相对比较零散,需要反复的查来查去,费时又累心 那这次有时间就想着说写一篇从零开始、步骤完整的网站搭建教程 希望能帮助大家节省时间,不

    2023年04月09日
    浏览(48)
  • 【AI实战】开源可商用的中英文大语言模型baichuan-7B,从零开始搭建

    baichuan-7B 是由百川智能开发的一个开源可商用的大规模预训练语言模型。基于 Transformer 结构,在大约1.2万亿 tokens 上训练的70亿参数模型,支持中英双语,上下文窗口长度为4096。在标准的中文和英文权威 benchmark(C-EVAL/MMLU)上均取得同尺寸最好的效果。 GitHub: https://github.c

    2024年02月09日
    浏览(56)
  • 如何从零开始搭建自动化测试框架?详细步骤给到你

    目录 前言 1、确定需求 2、选择测试工具 3、选择编程语言 4、设计测试框架结构 5、编写测试用例 6、编写测试脚本 7、执行自动化测试 8、分析测试结果 9、设计测试用例 10、组织测试数据 11、选择开发工具 12、安排测试计划 13、提高测试覆盖率 总结 搭建自动化测试框架是一

    2023年04月09日
    浏览(68)
  • Windows 11 上从零开始基于 wsl-ubuntu 搭建 AI 学习环境及部署多种私有 ChatGPT

    今天(2023/6/2)刚刚换了 1 块 4TB 的硬盘,在 Windows 11 上从零开始,基于 Windows Subsystem for Linux 搭建一套 AI 学习环境。 其中包括,安装 Python、安装 Miniconda3、安装 CUDA Toolkit、安装 git lfs、配置 Hugging Face 的缓存路径、本地部署 ChatGLM-6B、本地部署 VisualGLM-6B等等内容。 安装 Ubuntu-

    2024年02月07日
    浏览(58)
  • 【AI实战】从零开始搭建中文 LLaMA-33B 语言模型 Chinese-LLaMA-Alpaca-33B

    2023年2月25日消息,Meta 推出了一种针对研究社区的基于人工智能 (AI) 的新型大型语言模型,与微软、谷歌等一众受到 ChatGPT 刺激的公司一同加入人工智能竞赛。 Meta 的 LLaMA 是“大型语言模型 Meta AI” (Large Language Model Meta AI)的缩写,它可以在非商业许可下提供给政府、社区

    2024年02月13日
    浏览(50)
  • 解锁讯飞星火:从零开始,教你如何轻松驾驭这款全能AI工具

    星火助手中心   |  原创作者/编辑:凯哥Java                      |  分类:人工智能学习系列教程 0 1 前言 在之前的文章中,介绍了AI的好处,但是我们知道,因为种种原因,在国内想要访问AI需要通过上网手段才可以访问到,而且还有封号风险。今天凯哥给大家介绍一款

    2024年02月21日
    浏览(70)
  • 【Flutter】macOS从零开始使用FVM搭建Flutter开发环境

    本文为个人记录macOS系统使用fvm从零开始搭建flutter开发环境到项目运行的过程,非教程性质,仅供参考,如有疑问或建议,欢迎大家在评论区留言 附上开发设备配置 以vscode为编码工具 下载地址:Download Visual Studio Code - Mac, Linux, Windows 下载后解压安装即可 Xcode 在 iOS 真机或者

    2024年02月09日
    浏览(52)
  • 如何使用Python从零开始构建游戏

    作者:禅与计算机程序设计艺术 游戏开发是一个庞大的领域,其涉及到许多领域如数学、计算机图形学、音视频等等,本文只讨论游戏编程,不涉及到其他相关领域知识。 游戏编程可以分成以下几步: 游戏逻辑 渲染 物理引擎 AI 用户交互 游戏中可能会用到不同的技术或框架

    2024年02月08日
    浏览(43)
  • 从零开始学习如何使用 Postman 请求头

    当你在使用 Postman 发送请求时,请求头(Headers)是你可以包含在 HTTP 请求中的重要部分之一。请求头包含了关于请求的元数据信息,这些信息对于服务器来处理请求是非常重要的。下面是一份详细的图文介绍,说明了如何在 Postman 中使用请求头。 首先,打开 Postman 应用程序

    2024年04月09日
    浏览(39)
  • 如何高效的学习接口自动化测试?从零开始学习接口自动化测试:选择合适的学习资源和编程语言

    目录 引言: 一、学习前的准备 二、选择合适的学习资源 三、实践中学习 四、总结 在日常的软件开发过程中,接口自动化测试是一个非常重要的环节。接口自动化测试可以帮助我们快速准确地检测出软件中的缺陷,提高软件的质量和稳定性。但是,要学习接口自动化测试并

    2024年02月06日
    浏览(69)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包