Skip to content

Commit 2f11372

Browse files
committed
feat: add configurable OpenCode HTTP client pool
1 parent c8db565 commit 2f11372

5 files changed

Lines changed: 146 additions & 9 deletions

File tree

src/main/java/io/github/easy4j/opencode/OpenCodeClient.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,13 @@ private void copyHttpConfig(OpenCodeHttpClientConfig src) {
212212
this.config.getHttp().setPassword(src.getPassword());
213213
this.config.getHttp().setConnectTimeoutMillis(src.getConnectTimeoutMillis());
214214
this.config.getHttp().setReadTimeoutMillis(src.getReadTimeoutMillis());
215+
this.config.getHttp().setWriteTimeoutMillis(src.getWriteTimeoutMillis());
216+
this.config.getHttp().setCallTimeoutMillis(src.getCallTimeoutMillis());
217+
this.config.getHttp().setMaxIdleConnections(src.getMaxIdleConnections());
218+
this.config.getHttp().setKeepAliveDurationMillis(src.getKeepAliveDurationMillis());
219+
this.config.getHttp().setMaxRequests(src.getMaxRequests());
220+
this.config.getHttp().setMaxRequestsPerHost(src.getMaxRequestsPerHost());
221+
this.config.getHttp().setRetryOnConnectionFailure(src.isRetryOnConnectionFailure());
215222
this.config.getHttp().setVerifySsl(src.isVerifySsl());
216223
this.config.getHttp().setDefaultModel(src.getDefaultModel());
217224
this.config.getHttp().setDefaultAgent(src.getDefaultAgent());

src/main/java/io/github/easy4j/opencode/OpenCodeHttpClientConfig.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,41 @@ public class OpenCodeHttpClientConfig {
5555
*/
5656
private int readTimeoutMillis = 300_000;
5757

58+
/**
59+
* 写入超时(毫秒)。
60+
*/
61+
private int writeTimeoutMillis = 120_000;
62+
63+
/**
64+
* 整个调用超时(毫秒);0 表示不额外限制。
65+
*/
66+
private int callTimeoutMillis;
67+
68+
/**
69+
* 连接池最大空闲连接数。
70+
*/
71+
private int maxIdleConnections = 32;
72+
73+
/**
74+
* 空闲连接保活时间(毫秒)。
75+
*/
76+
private long keepAliveDurationMillis = 300_000L;
77+
78+
/**
79+
* 异步请求最大并发数。
80+
*/
81+
private int maxRequests = 128;
82+
83+
/**
84+
* 单主机异步请求最大并发数。
85+
*/
86+
private int maxRequestsPerHost = 128;
87+
88+
/**
89+
* 遇到失效连接等传输故障时是否允许 OkHttp 自动恢复。
90+
*/
91+
private boolean retryOnConnectionFailure = true;
92+
5893
/**
5994
* 是否校验 HTTPS 证书;为 false 时关闭校验(仅建议开发环境)。
6095
*/
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package io.github.easy4j.opencode;
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+
* OpenCode 独立运行时使用的 OkHttpClient 工厂。
12+
*
13+
* <p>Spring 容器已经提供外部 {@link OkHttpClient} 时应使用注入构造器,本工厂不会参与。</p>
14+
*/
15+
public final class OpenCodeOkHttpClientFactory {
16+
17+
private OpenCodeOkHttpClientFactory() {
18+
}
19+
20+
/**
21+
* 根据 OpenCode HTTP 配置创建客户端。
22+
*
23+
* @param config HTTP 配置
24+
* @return SDK 自主管理的 OkHttpClient
25+
*/
26+
public static OkHttpClient create(OpenCodeHttpClientConfig config) {
27+
Objects.requireNonNull(config, "config");
28+
Dispatcher dispatcher = new Dispatcher();
29+
dispatcher.setMaxRequests(Math.max(1, config.getMaxRequests()));
30+
dispatcher.setMaxRequestsPerHost(Math.max(1, config.getMaxRequestsPerHost()));
31+
ConnectionPool connectionPool = new ConnectionPool(
32+
Math.max(1, config.getMaxIdleConnections()),
33+
Math.max(1L, config.getKeepAliveDurationMillis()),
34+
TimeUnit.MILLISECONDS);
35+
OkHttpClient.Builder builder = new OkHttpClient.Builder()
36+
.dispatcher(dispatcher)
37+
.connectionPool(connectionPool)
38+
.connectTimeout(Math.max(1, config.getConnectTimeoutMillis()), TimeUnit.MILLISECONDS)
39+
.readTimeout(Math.max(0, config.getReadTimeoutMillis()), TimeUnit.MILLISECONDS)
40+
.writeTimeout(Math.max(1, config.getWriteTimeoutMillis()), TimeUnit.MILLISECONDS)
41+
.callTimeout(Math.max(0, config.getCallTimeoutMillis()), TimeUnit.MILLISECONDS)
42+
.retryOnConnectionFailure(config.isRetryOnConnectionFailure());
43+
if (!config.isVerifySsl()) {
44+
builder.hostnameVerifier((hostname, session) -> true);
45+
}
46+
return builder.build();
47+
}
48+
49+
/**
50+
* 释放 SDK 自建客户端资源。
51+
*
52+
* @param client SDK 自建客户端
53+
*/
54+
public static void shutdown(OkHttpClient client) {
55+
if (Objects.isNull(client)) {
56+
return;
57+
}
58+
client.dispatcher().cancelAll();
59+
client.connectionPool().evictAll();
60+
client.dispatcher().executorService().shutdown();
61+
}
62+
}

src/main/java/io/github/easy4j/opencode/api/OpenCodeHttpClient.java

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.fasterxml.jackson.databind.DeserializationFeature;
55
import com.fasterxml.jackson.databind.ObjectMapper;
66
import io.github.easy4j.opencode.OpenCodeHttpClientConfig;
7+
import io.github.easy4j.opencode.OpenCodeOkHttpClientFactory;
78
import io.github.easy4j.opencode.api.model.*;
89
import io.github.easy4j.opencode.exception.OpenCodeHttpException;
910
import lombok.extern.slf4j.Slf4j;
@@ -18,7 +19,6 @@
1819
import java.util.Map;
1920
import java.util.Objects;
2021
import java.util.Optional;
21-
import java.util.concurrent.TimeUnit;
2222

2323
/**
2424
* OpenCode Server HTTP 客户端,封装 REST API。
@@ -44,14 +44,7 @@ public OpenCodeHttpClient(OpenCodeHttpClientConfig config, ObjectMapper objectMa
4444
}
4545

4646
private static OkHttpClient buildOkHttpClient(OpenCodeHttpClientConfig config) {
47-
// 兜底创建
48-
OkHttpClient.Builder builder = new OkHttpClient.Builder()
49-
.connectTimeout(config.getConnectTimeoutMillis(), TimeUnit.MILLISECONDS)
50-
.readTimeout(config.getReadTimeoutMillis(), TimeUnit.MILLISECONDS);
51-
if (!config.isVerifySsl()) {
52-
builder.hostnameVerifier((hostname, session) -> true);
53-
}
54-
return builder.build();
47+
return OpenCodeOkHttpClientFactory.create(config);
5548
}
5649

5750
// ============================================================
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package io.github.easy4j.opencode;
2+
3+
import okhttp3.OkHttpClient;
4+
import org.junit.jupiter.api.Test;
5+
6+
import static org.junit.jupiter.api.Assertions.assertEquals;
7+
import static org.junit.jupiter.api.Assertions.assertFalse;
8+
9+
/**
10+
* {@link OpenCodeOkHttpClientFactory} 配置测试。
11+
*/
12+
class OpenCodeOkHttpClientFactoryTest {
13+
14+
@Test
15+
void shouldApplyIndependentHttpConfiguration() {
16+
OpenCodeHttpClientConfig config = new OpenCodeHttpClientConfig();
17+
config.setConnectTimeoutMillis(11001);
18+
config.setReadTimeoutMillis(22002);
19+
config.setWriteTimeoutMillis(33003);
20+
config.setCallTimeoutMillis(44004);
21+
config.setMaxIdleConnections(13);
22+
config.setKeepAliveDurationMillis(55005L);
23+
config.setMaxRequests(66);
24+
config.setMaxRequestsPerHost(33);
25+
config.setRetryOnConnectionFailure(false);
26+
27+
OkHttpClient client = OpenCodeOkHttpClientFactory.create(config);
28+
try {
29+
assertEquals(11001, client.connectTimeoutMillis());
30+
assertEquals(22002, client.readTimeoutMillis());
31+
assertEquals(33003, client.writeTimeoutMillis());
32+
assertEquals(44004, client.callTimeoutMillis());
33+
assertEquals(66, client.dispatcher().getMaxRequests());
34+
assertEquals(33, client.dispatcher().getMaxRequestsPerHost());
35+
assertFalse(client.retryOnConnectionFailure());
36+
} finally {
37+
OpenCodeOkHttpClientFactory.shutdown(client);
38+
}
39+
}
40+
}

0 commit comments

Comments
 (0)