Skip to content

Commit f75f3d1

Browse files
committed
perf: optimize OpenClaw transport and coverage on JDK 17
1 parent f5f82e7 commit f75f3d1

17 files changed

Lines changed: 1623 additions & 84 deletions

‎pom.xml‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@
191191
<configuration>
192192
<skip>false</skip>
193193
<skipTests>false</skipTests>
194-
<argLine>-Xmx1024m -Dfile.encoding=UTF-8</argLine>
194+
<argLine>${argLine} -Xmx1024m -Dfile.encoding=UTF-8</argLine>
195195
<additionalClasspathElements>
196196
<additionalClasspathElement>${basedir}/target/test-classes</additionalClasspathElement>
197197
</additionalClasspathElements>
@@ -304,7 +304,7 @@
304304
<goal>check</goal>
305305
</goals>
306306
<configuration>
307-
<haltOnFailure>false</haltOnFailure>
307+
<haltOnFailure>true</haltOnFailure>
308308
<rules>
309309
<rule>
310310
<element>BUNDLE</element>
@@ -355,7 +355,7 @@
355355
<jdk>[1.8,)</jdk>
356356
</activation>
357357
<properties>
358-
<central-maven-publishing-plugin.version>0.11.0</central-maven-publishing-plugin.version>
358+
<maven-central-publishing-plugin.version>0.11.0</maven-central-publishing-plugin.version>
359359
<additionalparam>-Xdoclint:none -Xlint:unchecked</additionalparam>
360360
<maven-jacoco-plugin.version>0.8.15</maven-jacoco-plugin.version>
361361

@@ -465,7 +465,7 @@
465465
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
466466
<java.version>17</java.version>
467467
<maven.version>3.0</maven.version>
468-
<okhttp3.version>5.4.0</okhttp3.version>
468+
<okhttp3.version>4.12.0</okhttp3.version>
469469
<jackson.version>2.17.2</jackson.version>
470470
<commons-exec.version>1.6.0</commons-exec.version>
471471
<junit.version>5.11.4</junit.version>

‎src/main/java/io/github/easy4j/openclaw/OpenClawClient.java‎

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
* </ul>
4141
*
4242
* <h3>构造器选择</h3>
43-
* <p>提供 8 个重载覆盖三类场景:</p>
43+
* <p>提供多种构造器覆盖三类场景:</p>
4444
* <ul>
4545
* <li>仅 HTTP / 仅 CLI:传入单个子配置,禁用另一子系统</li>
4646
* <li>HTTP + CLI:传入两个子配置,子系统都按各自 {@code enabled} 决定</li>
@@ -71,6 +71,7 @@ public class OpenClawClient implements AutoCloseable {
7171
private final OpenClawToolInvokeClient toolsInvokeClient;
7272
private final OpenClawCli cli;
7373
private final OpenClawGatewayWsClient wsClient;
74+
private final OkHttpClient ownedHttpClient;
7475

7576
// ============================================================
7677
// 构造器(单配置 / 双配置 / 组合配置 × 自动或强制)
@@ -80,35 +81,44 @@ public class OpenClawClient implements AutoCloseable {
8081
* 仅 HTTP 子系统(CLI 禁用)。自动创建默认 {@link ObjectMapper} 与 {@link OkHttpClient}。
8182
*/
8283
public OpenClawClient(OpenClawHttpClientConfig httpConfig) {
83-
this(httpConfig, new OpenClawCliConfig(), new ObjectMapper(), new OkHttpClient());
84+
this(httpConfig, new OpenClawCliConfig(), new ObjectMapper(),
85+
OpenClawOkHttpClientFactory.create(httpConfig), true);
86+
}
87+
88+
/**
89+
* 仅 HTTP 子系统,使用调用方管理的共享 {@link OkHttpClient}。
90+
* <p>适用于直接注入 Spring 容器中由 okhttp3-extension/starter 配置的客户端。</p>
91+
*/
92+
public OpenClawClient(OpenClawHttpClientConfig httpConfig, OkHttpClient httpClient) {
93+
this(httpConfig, new ObjectMapper(), httpClient);
8494
}
8595

8696
/**
8797
* 仅 HTTP 子系统(CLI 禁用),强制注入共享 {@link ObjectMapper} 与 {@link OkHttpClient}。
8898
*/
8999
public OpenClawClient(OpenClawHttpClientConfig httpConfig, ObjectMapper objectMapper, OkHttpClient httpClient) {
90-
this(httpConfig, new OpenClawCliConfig(), objectMapper, httpClient);
100+
this(httpConfig, new OpenClawCliConfig(), objectMapper, httpClient, false);
91101
}
92102

93103
/**
94104
* 仅 CLI 子系统(HTTP 禁用)。自动创建默认 {@link ObjectMapper} 与 {@link OkHttpClient}。
95105
*/
96106
public OpenClawClient(OpenClawCliConfig cliConfig) {
97-
this(new OpenClawHttpClientConfig(), cliConfig, new ObjectMapper(), new OkHttpClient());
107+
this(new OpenClawHttpClientConfig(), cliConfig, new ObjectMapper(), new OkHttpClient(), true);
98108
}
99109

100110
/**
101111
* 仅 CLI 子系统(HTTP 禁用),强制注入共享 {@link ObjectMapper} 与 {@link OkHttpClient}。
102112
*/
103113
public OpenClawClient(OpenClawCliConfig cliConfig, ObjectMapper objectMapper, OkHttpClient httpClient) {
104-
this(new OpenClawHttpClientConfig(), cliConfig, objectMapper, httpClient);
114+
this(new OpenClawHttpClientConfig(), cliConfig, objectMapper, httpClient, false);
105115
}
106116

107117
/**
108118
* HTTP + CLI 子系统。自动创建默认 {@link ObjectMapper} 与 {@link OkHttpClient}。
109119
*/
110120
public OpenClawClient(OpenClawHttpClientConfig httpConfig, OpenClawCliConfig cliConfig) {
111-
this(httpConfig, cliConfig, new ObjectMapper(), new OkHttpClient());
121+
this(httpConfig, cliConfig, new ObjectMapper(), OpenClawOkHttpClientFactory.create(httpConfig), true);
112122
}
113123

114124
/**
@@ -121,10 +131,16 @@ public OpenClawClient(OpenClawHttpClientConfig httpConfig, OpenClawCliConfig cli
121131
*/
122132
public OpenClawClient(OpenClawHttpClientConfig httpConfig, OpenClawCliConfig cliConfig,
123133
ObjectMapper objectMapper, OkHttpClient httpClient) {
134+
this(httpConfig, cliConfig, objectMapper, httpClient, false);
135+
}
136+
137+
private OpenClawClient(OpenClawHttpClientConfig httpConfig, OpenClawCliConfig cliConfig,
138+
ObjectMapper objectMapper, OkHttpClient httpClient, boolean ownsHttpClient) {
124139
Objects.requireNonNull(httpConfig, "httpConfig");
125140
Objects.requireNonNull(cliConfig, "cliConfig");
126141
Objects.requireNonNull(objectMapper, "objectMapper");
127142
Objects.requireNonNull(httpClient, "httpClient");
143+
this.ownedHttpClient = ownsHttpClient ? httpClient : null;
128144

129145
boolean httpEnabled = httpConfig.isEnabled();
130146
boolean cliEnabled = cliConfig.isEnabled();
@@ -165,7 +181,16 @@ public OpenClawClient(OpenClawClientConfig config) {
165181
this(Objects.requireNonNull(config, "config").getHttp(),
166182
config.getCli(),
167183
new ObjectMapper(),
168-
new OkHttpClient());
184+
OpenClawOkHttpClientFactory.create(config.getHttp()),
185+
true);
186+
}
187+
188+
/**
189+
* 使用组合配置和调用方管理的共享 {@link OkHttpClient}。
190+
* <p>SDK 关闭时不会关闭、清空或重建该客户端的连接池和调度器。</p>
191+
*/
192+
public OpenClawClient(OpenClawClientConfig config, OkHttpClient httpClient) {
193+
this(config, new ObjectMapper(), httpClient);
169194
}
170195

171196
/**
@@ -175,7 +200,8 @@ public OpenClawClient(OpenClawClientConfig config, ObjectMapper objectMapper, Ok
175200
this(Objects.requireNonNull(config, "config").getHttp(),
176201
config.getCli(),
177202
objectMapper,
178-
httpClient);
203+
httpClient,
204+
false);
179205
}
180206

181207
/**
@@ -198,6 +224,7 @@ public OpenClawClient(OpenClawHttpClientConfig httpConfig,
198224
this.toolsInvokeClient = toolsInvokeClient;
199225
this.cli = cli;
200226
this.wsClient = wsClient;
227+
this.ownedHttpClient = null;
201228
}
202229

203230
/**
@@ -386,6 +413,16 @@ public OpenClawChatClient chat() {
386413
return chatClient;
387414
}
388415

416+
/**
417+
* 获取 HTTP 子系统实际使用的 {@link OkHttpClient}。
418+
* <p>通过注入构造器传入时返回同一个实例,其生命周期仍由调用方管理。</p>
419+
*
420+
* @return HTTP 子系统使用的 OkHttpClient;HTTP 子系统禁用时返回 {@code null}
421+
*/
422+
public OkHttpClient getOkHttpClient() {
423+
return Objects.nonNull(chatClient) ? chatClient.getHttpClient() : null;
424+
}
425+
389426
/**
390427
* 获取 Embeddings 客户端。
391428
*/
@@ -618,6 +655,7 @@ public void close() {
618655
closeQuietly(responsesClient);
619656
closeQuietly(toolsInvokeClient);
620657
closeQuietly(wsClient);
658+
OpenClawOkHttpClientFactory.shutdown(ownedHttpClient);
621659
}
622660

623661
/**

‎src/main/java/io/github/easy4j/openclaw/OpenClawHttpClientConfig.java‎

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,32 @@ public class OpenClawHttpClientConfig {
6666
private boolean verifySsl = true;
6767

6868
/** 连接超时(毫秒) */
69-
private int connectTimeoutMillis = 15_000;
69+
private int connectTimeoutMillis = 2_000;
7070

7171
/** 读取超时(毫秒) */
7272
private int readTimeoutMillis = 120_000;
7373

74+
/** 写入超时(毫秒) */
75+
private int writeTimeoutMillis = 10_000;
76+
77+
/** 整个调用超时(毫秒);0 表示不额外限制,由读取超时控制 */
78+
private int callTimeoutMillis;
79+
80+
/** 连接池最大空闲连接数 */
81+
private int maxIdleConnections = 32;
82+
83+
/** 空闲连接保活时间(毫秒) */
84+
private long keepAliveDurationMillis = 300_000L;
85+
86+
/** 异步请求最大并发数 */
87+
private int maxRequests = 128;
88+
89+
/** 单主机异步请求最大并发数 */
90+
private int maxRequestsPerHost = 64;
91+
92+
/** 遇到失效连接等传输故障时是否允许 OkHttp 自动恢复 */
93+
private boolean retryOnConnectionFailure = true;
94+
7495
/**
7596
* Gateway HTTP Webhooks 基础路径,对应 {@code hooks.path},默认 {@code /hooks}。
7697
*/
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package io.github.easy4j.openclaw;
2+
3+
import okhttp3.ConnectionPool;
4+
import okhttp3.Dispatcher;
5+
import okhttp3.OkHttpClient;
6+
7+
import java.util.Objects;
8+
import java.util.concurrent.TimeUnit;
9+
10+
/**
11+
* OpenClaw 默认 OkHttpClient 工厂。
12+
*
13+
* <p>Spring 等容器已经提供 {@link OkHttpClient} 时应优先使用注入构造器;本工厂只负责
14+
* SDK 独立运行场景,并保证 Chat、Tools、Responses 等客户端共享同一连接池。</p>
15+
*/
16+
public final class OpenClawOkHttpClientFactory {
17+
18+
private OpenClawOkHttpClientFactory() {
19+
}
20+
21+
/**
22+
* 按 HTTP 配置创建适合高并发长连接复用的客户端。
23+
*
24+
* @param config HTTP 配置
25+
* @return SDK 自主管理的 OkHttpClient
26+
*/
27+
public static OkHttpClient create(OpenClawHttpClientConfig config) {
28+
Objects.requireNonNull(config, "config");
29+
Dispatcher dispatcher = new Dispatcher();
30+
dispatcher.setMaxRequests(Math.max(1, config.getMaxRequests()));
31+
dispatcher.setMaxRequestsPerHost(Math.max(1, config.getMaxRequestsPerHost()));
32+
ConnectionPool connectionPool = new ConnectionPool(
33+
Math.max(1, config.getMaxIdleConnections()),
34+
Math.max(1L, config.getKeepAliveDurationMillis()),
35+
TimeUnit.MILLISECONDS);
36+
OkHttpClient.Builder builder = new OkHttpClient.Builder()
37+
.dispatcher(dispatcher)
38+
.connectionPool(connectionPool)
39+
.connectTimeout(Math.max(1, config.getConnectTimeoutMillis()), TimeUnit.MILLISECONDS)
40+
.readTimeout(Math.max(0, config.getReadTimeoutMillis()), TimeUnit.MILLISECONDS)
41+
.writeTimeout(Math.max(1, config.getWriteTimeoutMillis()), TimeUnit.MILLISECONDS)
42+
.callTimeout(Math.max(0, config.getCallTimeoutMillis()), TimeUnit.MILLISECONDS)
43+
.retryOnConnectionFailure(config.isRetryOnConnectionFailure());
44+
if (!config.isVerifySsl()) {
45+
builder.hostnameVerifier((hostname, session) -> true);
46+
}
47+
return builder.build();
48+
}
49+
50+
/**
51+
* 释放 SDK 自建客户端资源。外部注入的客户端不得调用此方法。
52+
*
53+
* @param client SDK 自建客户端
54+
*/
55+
public static void shutdown(OkHttpClient client) {
56+
if (Objects.isNull(client)) {
57+
return;
58+
}
59+
client.dispatcher().cancelAll();
60+
client.connectionPool().evictAll();
61+
client.dispatcher().executorService().shutdown();
62+
}
63+
}

0 commit comments

Comments
 (0)