认识并使用OkHttp3

认识并使用OkHttp3

  • 一、前言:发送Http请求并处理响应
    • 1、背景
    • 2、传统技术:使用java.net.HttpURLConnection
    • 3、学习OkHttp3(可以实现真正的流式处理)
  • 二、OkHttp3
    • 1、OkHttp3是什么?
    • 2、如何使用OkHttp3呢?
      • 2.1 为OkHttpClient创建单例
        • 2.1.1 默认设置
        • 2.1.2 自定义设置(更常用)
        • 2.1.3 不用主动关闭Http client
      • 2.2 同步方式
      • 2.3 异步方式
        • 2.3.1 这并不是真正的流式处理
      • 2.4 真正的流式处理技术
        • 2.4.1 SSE
        • 2.4.2 WebSocket

一、前言:发送Http请求并处理响应

1、背景

  • 实际开发中,难免有“发送Http请求并处理响应”的技术诉求。
  • 那么,在Java的世界中,如何发送Http请求并处理响应呢?

2、传统技术:使用java.net.HttpURLConnection

  • 代码
public class HttpUtils {/*** 使用java.net.HttpURLConnection发送HTTP请求并处理响应*/public static void useHttpURLConnection(String urlStr) {URL url = null;HttpURLConnection httpURLConnection = null;try {// 1、构建httpURLConnection(我理解就是建立Http连接)url = new URL(urlStr);httpURLConnection = (HttpURLConnection) url.openConnection();httpURLConnection.setRequestProperty("Content-Type", "application/json");httpURLConnection.setRequestMethod("POST");// 单位:毫秒httpURLConnection.setConnectTimeout(5000);httpURLConnection.setReadTimeout(5000);// 当想通过POST,PUT等方式发送请求体时,需要调用setDoOutput方法并将其设置为 true。// 调用此方法后,就能获取 HttpURLConnection 的输出流,并将请求体写入这个流中。httpURLConnection.setDoOutput(true);// 2、发送Http请求String requestBody = "";try (PrintWriter printWriter = new PrintWriter(httpURLConnection.getOutputStream())) {printWriter.write(requestBody);printWriter.flush();}// 3.1 失败if (HttpURLConnection.HTTP_OK != httpURLConnection.getResponseCode()) {try (InputStream errorStream = httpURLConnection.getErrorStream();InputStreamReader errorStreamReader = new InputStreamReader(errorStream, StandardCharsets.UTF_8);BufferedReader errorBufferedReader = new BufferedReader(errorStreamReader)) {String errorMsg = errorBufferedReader.lines().collect(Collectors.joining("\n"));throw new RuntimeException(String.format("The HTTP connection fails and the BufferReader cannot be obtained\nCause by : %s", errorMsg));}}// 3.2 成功,获取bufferedReaderInputStream inputStream = httpURLConnection.getInputStream();InputStreamReader inputStreamReader = new InputStreamReader(Objects.requireNonNull(inputStream));BufferedReader bufferedReader = new BufferedReader(inputStreamReader);// 4、处理响应String line;while ((line = bufferedReader.readLine()) != null) {System.out.println(line);}// 5、资源关闭(用try-with-resources更好)bufferedReader.close();inputStreamReader.close();inputStream.close();httpURLConnection.disconnect();} catch (Exception e) {e.printStackTrace();}}
}

仅做编码思路的展示,故未测试:)

  • 结论:
    • (1)这种写法太麻烦了:)
    • (2)java.net.HttpURLConnection 是 Java 的标准类,主要用于处理同步的 HTTP 请求和响应。它不支持流式传输的 HTTP 功能,如 Server-Sent Events (SSE) 或 WebSocket。HttpURLConnection 主要用于传统的请求-响应模型,其中客户端发送一个请求到服务器,并等待服务器返回一个完整的响应

3、学习OkHttp3(可以实现真正的流式处理)

二、OkHttp3

1、OkHttp3是什么?

  • OkHttp3是一个效率非常高的HTTP客户端,它支持HTTP/2,允许所有同一个主机地址的请求共享同一个socket连接,减少了请求延迟。此外,它还有自动处理网络缓存的功能,以及对GZIP的支持来减少数据的传输量。
    • Http协议是应用层协议,建立在TCP连接的基础上。我们不希望每次发送HTTP请求时,都重新进行TCP的3次握手,那样会导致请求延迟较高。

2、如何使用OkHttp3呢?

2.1 为OkHttpClient创建单例

  • 当创建单个 OkHttpClient 实例并将其复用于所有 HTTP 调用时,OkHttp 的性能最佳。这是因为每个client都有自己的连接池和线程池。复用连接和线程可以减少延迟并节省内存。相反,为每个请求创建一个client会浪费空闲池上的资源。【官方文档】
2.1.1 默认设置
// HTTP client单例
public final OkHttpClient client = new OkHttpClient();
2.1.2 自定义设置(更常用)
// HTTP client单例
public final OkHttpClient okHttpClient = new OkHttpClient.Builder().addInterceptor(httpLoggingInterceptor).addInterceptor(...).connectTimeout(450, TimeUnit.SECONDS).writeTimeout(450, TimeUnit.SECONDS).readTimeout(450, TimeUnit.SECONDS).cache(...).build();
  • OkHttp的拦截器也要学习下~
2.1.3 不用主动关闭Http client
  • 保持空闲的线程和连接将自动释放

2.2 同步方式

  • 代码:
public class HttpUtils {private static final OkHttpClient okHttpClient = new OkHttpClient.Builder().connectTimeout(450,TimeUnit.SECONDS).writeTimeout(450, TimeUnit.SECONDS).readTimeout(450, TimeUnit.SECONDS).build();public static void useBlockInvokeOfOkHttp() {// 构建请求体(openai api 请求体是一个json格式的数据,对应于一个Java类)ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder().stream(false).messages(Collections.singletonList(Message.builder().role(Constants.Role.USER).content("1+1").build())).model(ChatCompletionRequest.Model.GPT_3_5_TURBO.getCode()).maxTokens(1024).build();// 构建请求Request postRequest;try {postRequest = new Request.Builder().url("https://api.openai.com/v1/chat/completions").header(Header.AUTHORIZATION.getValue(), "Bearer {your apiKey}").post(RequestBody.create(MediaType.parse(ContentType.JSON.getValue()), new ObjectMapper().writeValueAsString(chatCompletionRequest))).build();} catch (JsonProcessingException e) {throw new RuntimeException("new ObjectMapper().writeValueAsString(chatCompletionRequest)) exception", e);}try {Response response = okHttpClient.newCall(postRequest).execute();System.out.println(response.body().string());} catch (Exception e) {throw new RuntimeException("okHttpClient.newCall(postRequest).execute() exception", e);}}
}
  • 执行Response response = okHttpClient.newCall(postRequest).execute();的线程会等待服务器的响应:
{"id": "chatcmpl-8lyvDqO0k7FW9Ff582C8HFDux7oyb","object": "chat.completion","created": 1706446291,"model": "gpt-3.5-turbo-0613","choices": [{"index": 0,"message": {"role": "assistant","content": "1+1 equals 2."},"logprobs": null,"finish_reason": "stop"}],"usage": {"prompt_tokens": 10,"completion_tokens": 7,"total_tokens": 17},"system_fingerprint": null
}

2.3 异步方式

  • 代码:
public static void useStreamInvokeOfOkHttp() {// 构建请求体ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder().stream(true) // 流式调用.messages(Collections.singletonList(Message.builder().role(Constants.Role.USER).content("1+1").build())).model(ChatCompletionRequest.Model.GPT_3_5_TURBO.getCode()).maxTokens(1024).build();// 构建请求 (和“2.2 同步方式”一致)...// 创建CountDownLatchCountDownLatch latch = new CountDownLatch(1);try {System.out.println(Thread.currentThread().getName() + ": okHttpClient.newCall(postRequest).enqueue");okHttpClient.newCall(postRequest).enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {e.printStackTrace();// 在这里处理请求失败的情况latch.countDown(); // 调用countDown}@Overridepublic void onResponse(Call call, Response response) throws IOException {if (!response.isSuccessful()) {throw new IOException("Unexpected code " + response);}// 在这里处理响应// 注意:这不是主线程String responseBody = response.body().string();System.out.println(Thread.currentThread().getName() + ": " + responseBody);latch.countDown(); // 调用countDown}});} catch (Exception e) {throw new RuntimeException("okHttpClient.newCall(postRequest).execute() exception", e);}// 等待子线程执行结束 (单元测试,主线程执行结束了,程序就结束了)try {latch.await();} catch (InterruptedException e) {throw new RuntimeException(e);}}
  • 结果:
main: okHttpClient.newCall(postRequest).enqueue
OkHttp https://api.openai.com/...: data: {"id":"chatcmpl-8lz1oAgvwuWsxxQQTbxIz2Q65WAyK","object":"chat.completion.chunk","created":1706446700,"model":"gpt-3.5-turbo-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}]}data: {"id":"chatcmpl-8lz1oAgvwuWsxxQQTbxIz2Q65WAyK","object":"chat.completion.chunk","created":1706446700,"model":"gpt-3.5-turbo-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"1"},"logprobs":null,"finish_reason":null}]}...data: [DONE]
2.3.1 这并不是真正的流式处理
  • 如果使用 OkHttp 的同步请求(response = okHttpClient.newCall(request).execute())或者异步请求(client.newCall(request).enqueue(new Callback() {…}))来处理服务器端发送的批量数据,这并不是真正的流式处理(SSE - Server-Sent Events)。
  • 在这种情况下,OkHttp 客户端会等待服务器端发送完所有数据,并关闭响应体,然后一次性返回整个响应体。这意味着不会以实时、流式的方式接收数据。服务器端可能是分批次发送数据,但 OkHttp 客户端只会在所有数据都接收完毕后才处理这些数据。这就导致了两个主要问题
    • 实时性缺失:由于数据是在全部接收完之后才开始处理,将无法实时地接收和处理服务器端发送的每一批数据。
    • 内存问题:如果服务器发送的数据量非常大,那么整个响应体将被加载到内存中,这可能会导致内存溢出或性能问题。

2.4 真正的流式处理技术

  • 为了实现真正的流式处理,需要使用特定于此目的的技术,如 SSE 或 WebSocket。
    • SSE 适用于服务器到客户端的单向通信
    • WebSocket 适用于双向通信。
  • 这些技术允许实时接收数据,每当有新数据到达时就立即处理,而不是等待所有数据都发送完毕。
2.4.1 SSE
  • 依赖
<dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp-sse</artifactId><version>3.14.9</version>
</dependency>
  • 代码:
public class HttpUtils {private static final OkHttpClient okHttpClient = ...private static final EventSource.Factory factory = EventSources.createFactory(okHttpClient);public static void useStreamInvokeOfOkHttpBySse() {// 构建请求体(和“2.3 异步方式”一致)...// 构建请求(和“2.3 异步方式”一致)...EventSourceListener listener = new EventSourceListener() {@Overridepublic void onOpen(EventSource eventSource, Response response) {// 连接开启时的处理}@Overridepublic void onEvent(EventSource eventSource, String id, String type, String data) {// 接收到事件时的处理System.out.println("Event: " + data);}@Overridepublic void onFailure(EventSource eventSource, Throwable t, Response response) {// 连接失败时的处理}};factory.newEventSource(postRequest, listener);}
}public class HttpUtilsTest {public static void main(String[] args) {HttpUtils.useStreamInvokeOfOkHttpBySse();}
}
  • 结果:
Event: {"id":"chatcmpl-8lzJCq71wZuWQmXu9HfvLM2QUN8OE","object":"chat.completion.chunk","created":1706447778,"model":"gpt-3.5-turbo-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}]}
......
Event: {"id":"chatcmpl-8lzJCq71wZuWQmXu9HfvLM2QUN8OE","object":"chat.completion.chunk","created":1706447778,"model":"gpt-3.5-turbo-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]}
Event: [DONE]
  • 特点:
    • EventSourceListener提供了几个方法来处理不同的事件。当服务器发送事件时,onEvent方法会被调用。
2.4.2 WebSocket
  • 代码:
public static void useStreamInvokeOfOkHttpByWebSocket() {// WebSocket 的规范要求必须使用 GET 请求。// WebSocket 握手是基于 HTTP GET 请求的,这是 WebSocket 协议的一个标准要求。Request request = new Request.Builder().url("https://api.openai.com/v1/chat/completions").header(Header.AUTHORIZATION.getValue(), "Bearer {your apiKey}").build();okHttpClient.newWebSocket(request, new WebSocketListener() {@SneakyThrows@Overridepublic void onOpen(WebSocket webSocket, Response response) {// WebSocket 连接打开后,发送数据// 构建请求体ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder().stream(true).messages(Collections.singletonList(Message.builder().role(Constants.Role.USER).content("1+1").build())).model(ChatCompletionRequest.Model.GPT_3_5_TURBO.getCode()).maxTokens(1024).build();webSocket.send(new ObjectMapper().writeValueAsString(chatCompletionRequest));}@Overridepublic void onMessage(WebSocket webSocket, String text) {// 接收到文本消息时调用System.out.println("Received message: " + text);}@Overridepublic void onMessage(WebSocket webSocket, ByteString bytes) {// 接收到二进制消息时调用}@Overridepublic void onClosing(WebSocket webSocket, int code, String reason) {// 当连接即将关闭时调用webSocket.close(1000, null);}@Overridepublic void onFailure(WebSocket webSocket, Throwable t, Response response) {// 当连接失败时调用t.printStackTrace();}});
}
  • 报错:
Expected HTTP 101 response but was '405 Method Not Allowed'

openai api 不支持 WebSocket 连接。这个 API 是基于 HTTP REST 架构设计的,通常是通过发起 HTTP POST 请求来使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/654834.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

单调队列优化DP模型整理

135. 最大子序和&#xff08;活动 - AcWing&#xff09; 找一个长度不超过m的连续子序列&#xff0c;但是并未指定这个子序列的长度&#xff0c;所以长度就有很多种选择&#xff0c;要获取任意一段长度的序列的区间和&#xff0c;那么显然要用到前缀和。然后我们来考虑&#xf…

基于Spring Boot的饮食分享平台设计与实现

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

【Javaweb程序设计】【C00161】基于SSM电子产品交易管理系统(论文+PPT)

基于SSM电子产品交易管理系统&#xff08;论文PPT&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于ssm的电子产品交易系统 本系统分为前台用户和后台管理员2个功能模块. 前台用户模块&#xff1a;当游客打开系统的网址后&#xff0c;首先看到的就…

738. 单调递增的数字 - 力扣(LeetCode)

题目描述 当且仅当每个相邻位数上的数字 x 和 y 满足 x < y 时&#xff0c;我们称这个整数是单调递增的。 给定一个整数 n &#xff0c;返回 小于或等于 n 的最大数字&#xff0c;且数字呈 单调递增 。 题目示例 输入: n 332 输出: 299 解题思路 题目要求小于等于N的最…

换个思路快速上手UML和plantUML——时序图

上一章我们介绍了类图&#xff0c;我们很清楚&#xff0c;类图是从更加宏观的角度去梳理系统结构的&#xff0c;从类图中我们可以获取到类与类之间&#xff1a;继承&#xff0c;实现等关系信息&#xff0c;是宏观逻辑。下面我们继续换一个思路&#xff1a;作为一名软件工程结构…

给刚上小学的侄女准备新年礼物,有什么让小朋友喜欢的玩具推荐?

给刚上小学的侄女准备新年礼物&#xff0c;我觉得也是有很多选择的。因为现在的市场上款式太多了&#xff0c;选择自己心意的适合小侄女的都是可以的。但是如果非要选益智的或是智能高科技的&#xff0c;对孩子来说既能玩耍又能在玩的同时学习到知识&#xff0c;能够开拓孩子眼…

无限可能!安全狗入选“潜力十强企业”

近日&#xff0c;等级保护测评公布了“2023年网络安全优秀评选”活动评选结果。 作为国内云原生安全领导厂商&#xff0c;安全狗凭借突出的综合实力&#xff0c;荣获“潜力十强企业”称号。 厦门服云信息科技有限公司&#xff08;品牌名&#xff1a;安全狗&#xff09;创办于20…

uniapp微信小程序-前端设计模式学习(中)

三、工厂模式 通俗解释&#xff08;理解记忆&#xff09; 假设我们有一个汽车工厂。我们可以让工厂根据用户的选择生产不同型号的汽车&#xff0c;而用户无需知道具体的汽车制造过程。 工厂模式的优势在于&#xff0c;它隐藏了对象的创建细节&#xff0c;让客户端代码更简洁…

《HTML 简易速速上手小册》第10章:HTML 的维护与优化(2024 最新版)

文章目录 10.1 网页性能优化10.1.1 基础知识10.1.2 案例 1&#xff1a;优化网页图像10.1.3 案例 2&#xff1a;使用延迟加载优化性能10.1.4 案例 3&#xff1a;优化 CSS 和 JavaScript 的加载 10.2 SEO 最佳实践10.2.1 基础知识10.2.2 案例 1&#xff1a;创建一个 SEO 友好的博…

数据库管理-第139期 做大还是做小-Oracle名称哪些事(20240125)

数据库管理139期 2024-01-25 第139期 做大还是做小-Oracle名称哪些事&#xff08;20240125&#xff09;1 问题2 排查3 扩展总结 第139期 做大还是做小-Oracle名称哪些事&#xff08;20240125&#xff09; 作者&#xff1a;胖头鱼的鱼缸&#xff08;尹海文&#xff09; Oracle A…

麒麟系统—— openKylin 安装 mongodb

麒麟系统—— openKylin 安装 mongodb 一、准备工作1. 确保麒麟系统 openKylin 已经安装完毕。 二、下载解压 MongoDB二、增加环境变量三、配置MongoDB创建数据目录创建日志文件运行 四、加入到服务中 MongoDB是一款高性能、开源的NoSQL数据库&#xff0c;因其灵活的数据结构、…

python第五节:集合set(3)

集合遍历 for循环遍历集合中元素 例子1&#xff1a; set1 {a,b,cde,张三,123} for i in set1: print(i) 结果&#xff1a; a cde b 张三 123 enumerate遍历索引和元素 例子2&#xff1a; set1 {a,b,cde,张三,123} for index,value in enumerate(set1): print(index…

MyBatis 的注解实现方法

MyBatis 的注解实现方法 MyBatis 的注解实现方法引入依赖添加配置创建表创建实体类创建mapper接口InsertDeleteSelectResults和ResultMap通过配置文件解决 UpdateOptions MyBatis 的注解实现方法 引入依赖 在springBoot项目中下载了EditStarters插件的,可以直接在配置文件处右…

效率高的B树系列

文章目录 前言B树概念性质插入数据分析代码实现性能分析 B树概念特性插入数据分析应用 B*树概念B*树的分裂 总结B树系列的区别B树系列对比哈希和平衡搜索树 前言 前面我们所学习到的数据结构&#xff0c;只能用来存储少量的数据&#xff0c;因为内存大小是非常有限的&#xff…

obs-studio 源码学习 obs.h

obs.h 引用头文件介绍 c99defs.h&#xff1a;这个头文件提供了一些 C99 标准的定义和声明&#xff0c;包括一些常用的宏定义和类型定义&#xff0c;用于提高代码的可移植性和兼容性。 bmem.h&#xff1a;这个头文件提供了对内存分配和管理的功能&#xff0c;包括一些内存分配…

一个查询IP地理信息和CDN提供商的离线终端工具Nali官方使用指南

Nali 一个查询IP地理信息和CDN提供商的离线终端工具. 功能 支持多种数据库 纯真 IPv4 离线数据库ZX IPv6 离线数据库Geoip2 城市数据库 (可选)IPIP 数据库 (可选)ip2region 数据库 (可选)DB-IP 数据库 (可选)IP2Location DB3 LITE 数据库 (可选)CDN 服务提供商查询支持管道处…

.ui文件相关

目录 ui类生成过程&#xff1a; 提问&#xff1a; 等以后自己熟练了用代码写这些样式内容&#xff0c;尽量用代码写&#xff0c;原因很简单&#xff1a; 用代码写的可以直接修改代码&#xff0c;但是在设计界面修改的东西&#xff0c;电脑没有QC这玩意&#xff0c;还真不好改…

Gitlab7.14 中文版安装教程

Gitlab7.14 中文版安装教程 注&#xff1a; 本教程由羞涩梦整理同步发布&#xff0c;本人技术分享站点&#xff1a;blog.hukanfa.com转发本文请备注原文链接&#xff0c;本文内容整理日期&#xff1a;2024-01-28csdn 博客名称&#xff1a;五维空间-影子&#xff0c;欢迎关注 …

JWT(JSON Web Token)详解以及在go-zero中配置的方法

目的 对用户进行身份认证和信息交换 RFC 7519 传统方式 通过session保存对话信息&#xff0c;服务端返回一个session id&#xff0c;用户保存这个id在cookie内&#xff0c;然后每次请求都传给服务端 局限性 对于服务器集群难以向每个服务器共享同一session jwt的方式是…