Skip to content

Commit aeb0cc1

Browse files
committed
feat: optimize long link test
1 parent 16c7c28 commit aeb0cc1

7 files changed

Lines changed: 652 additions & 63 deletions

File tree

trpc-proto/trpc-proto-http/src/main/java/com/tencent/trpc/proto/http/client/Http2ConsumerInvoker.java

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
import static com.tencent.trpc.core.common.Constants.DEFAULT_CLIENT_REQUEST_TIMEOUT_MS;
1616
import static com.tencent.trpc.proto.http.common.HttpConstants.CONNECTION_REQUEST_TIMEOUT;
1717

18-
import autovalue.shaded.com.google.common.common.base.Objects;
1918
import com.tencent.trpc.core.common.config.BackendConfig;
2019
import com.tencent.trpc.core.common.config.ConsumerConfig;
2120
import com.tencent.trpc.core.common.config.ProtocolConfig;
@@ -29,6 +28,7 @@
2928
import java.nio.charset.StandardCharsets;
3029
import java.util.HashMap;
3130
import java.util.Map;
31+
import java.util.Objects;
3232
import java.util.concurrent.Future;
3333
import java.util.concurrent.TimeUnit;
3434
import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
@@ -44,6 +44,11 @@
4444

4545
/**
4646
* HTTP/2 protocol client invoker, supporting both h2 and http2c.
47+
*
48+
* <p>Each {@link #send(Request)} entry signals the underlying {@link Http2cRpcClient} that
49+
* it is being used (drives the idle-eviction heuristic) and reports success / failure to
50+
* drive the consecutive-failure counter that flips the client to unavailable on sustained
51+
* backend outages.</p>
4752
*/
4853
public class Http2ConsumerInvoker<T> extends AbstractConsumerInvoker<T> {
4954

@@ -60,19 +65,38 @@ public Http2ConsumerInvoker(Http2cRpcClient client, ConsumerConfig<T> config,
6065
*
6166
* @param request client request
6267
* @return Response
63-
* @throws Exception if send request failed
68+
* @throws Exception declared to honour the abstract contract; the implementation never
69+
* lets exceptions escape — every failure path is wrapped into a Response with
70+
* {@code exception != null}.
6471
*/
6572
@Override
6673
public Response send(Request request) throws Exception {
74+
Http2cRpcClient http2cRpcClient = (Http2cRpcClient) client;
75+
// Mark "used" before any work so even a failed request keeps the idle-eviction timer
76+
// accurate (a failing client is still actively used and must not be reaped as orphan).
77+
http2cRpcClient.markUsed();
78+
6779
int requestTimeout = config.getBackendConfig().getRequestTimeout();
68-
SimpleHttpRequest simpleHttpRequest = buildRequest(request, requestTimeout);
80+
SimpleHttpRequest simpleHttpRequest;
81+
try {
82+
simpleHttpRequest = buildRequest(request, requestTimeout);
83+
} catch (Exception ex) {
84+
http2cRpcClient.markFailure();
85+
return RpcUtils.newResponse(request, null, ex);
86+
}
6987

7088
try {
7189
SimpleHttpResponse simpleHttpResponse = execute(request, requestTimeout,
72-
simpleHttpRequest);
73-
74-
return handleResponse(request, simpleHttpResponse);
90+
simpleHttpRequest, http2cRpcClient);
91+
Response response = handleResponse(request, simpleHttpResponse);
92+
if (response.getException() == null) {
93+
http2cRpcClient.markSuccess();
94+
} else {
95+
http2cRpcClient.markFailure();
96+
}
97+
return response;
7598
} catch (Exception e) {
99+
http2cRpcClient.markFailure();
76100
return RpcUtils.newResponse(request, null, e);
77101
}
78102

@@ -132,15 +156,12 @@ private Response handleResponse(Request request, SimpleHttpResponse simpleHttpRe
132156
* @param request TRPC request
133157
* @param requestTimeout request timeout
134158
* @param simpleHttpRequest HTTP request
159+
* @param http2cRpcClient already-resolved owning client (avoids a redundant cast)
135160
* @return HTTP response
136161
* @throws Exception if do HTTP request failed
137162
*/
138163
private SimpleHttpResponse execute(Request request, int requestTimeout,
139-
SimpleHttpRequest simpleHttpRequest) throws Exception {
140-
Http2cRpcClient http2cRpcClient = (Http2cRpcClient) client;
141-
// Refresh idle counter so the cluster manager's reconnect-check timer keeps treating
142-
// this client as healthy while it's actively serving requests.
143-
http2cRpcClient.markUsed();
164+
SimpleHttpRequest simpleHttpRequest, Http2cRpcClient http2cRpcClient) throws Exception {
144165
CloseableHttpAsyncClient httpAsyncClient = http2cRpcClient.getHttpAsyncClient();
145166
Future<SimpleHttpResponse> httpResponseFuture = httpAsyncClient.execute(simpleHttpRequest,
146167
new FutureCallback<SimpleHttpResponse>() {
@@ -210,7 +231,7 @@ private SimpleHttpRequest buildRequest(Request request, int requestTimeout) thro
210231
}
211232
// Set custom business headers, consistent with the TRPC protocol, only process String and byte[]
212233
request.getAttachments().forEach((k, v) -> {
213-
if (Objects.equal(k, HttpHeaders.TRANSFER_ENCODING) || Objects.equal(k, HttpHeaders.CONTENT_LENGTH)) {
234+
if (Objects.equals(k, HttpHeaders.TRANSFER_ENCODING) || Objects.equals(k, HttpHeaders.CONTENT_LENGTH)) {
214235
return;
215236
}
216237
if (v instanceof String) {

trpc-proto/trpc-proto-http/src/main/java/com/tencent/trpc/proto/http/client/Http2RpcClient.java

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
import static com.tencent.trpc.transport.http.common.Constants.KEYSTORE_PATH;
1616

1717
import com.tencent.trpc.core.common.config.ProtocolConfig;
18+
import com.tencent.trpc.core.exception.ErrorCode;
19+
import com.tencent.trpc.core.exception.TRpcException;
1820
import com.tencent.trpc.core.logger.Logger;
1921
import com.tencent.trpc.core.logger.LoggerFactory;
2022
import java.io.File;
@@ -24,28 +26,41 @@
2426
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
2527
import org.apache.hc.core5.http2.HttpVersionPolicy;
2628
import org.apache.hc.core5.http2.ssl.ConscryptClientTlsStrategy;
29+
import org.apache.hc.core5.pool.PoolReusePolicy;
30+
import org.apache.hc.core5.reactor.IOReactorConfig;
2731
import org.apache.hc.core5.ssl.SSLContexts;
32+
import org.apache.hc.core5.util.TimeValue;
2833

2934
/**
30-
* HTTP 2 protocol client.
35+
* HTTP/2 (TLS) protocol client. Inherits long-connection state ({@code lastUsedNanos},
36+
* {@code consecutiveFailures}, {@link #markUsed}, {@link #markSuccess}, {@link #markFailure}
37+
* and the overridden {@link #isAvailable()}) from {@link Http2cRpcClient}; the differences
38+
* are the TLS handshake and the explicit {@link HttpVersionPolicy} negotiation.
39+
*
40+
* <p>The connection manager is sized and tuned identically to {@link Http2cRpcClient}: pool
41+
* limits derived from {@code maxConns}, idle / expired eviction, SO_KEEPALIVE and a hard
42+
* connection TTL.</p>
3143
*/
3244
public class Http2RpcClient extends Http2cRpcClient {
3345

34-
private static final Logger logger = LoggerFactory.getLogger(HttpRpcClient.class);
46+
private static final Logger logger = LoggerFactory.getLogger(Http2RpcClient.class);
47+
48+
private static final int VALIDATE_AFTER_INACTIVITY_MS = 2000;
49+
private static final long EVICT_IDLE_CONNECTIONS_SECONDS = 60L;
50+
private static final int CONNECTION_TTL_MINUTES = 10;
3551

3652
/**
3753
* The protocol type used for interaction with the server, such as HTTP1, H2, or protocol negotiation.
3854
* In trpc, the interaction is forced to use H2 or HTTP1 protocol based on the configuration.
3955
*/
4056
protected HttpVersionPolicy clientVersionPolicy;
41-
4257
public Http2RpcClient(ProtocolConfig config) {
4358
super(config);
4459
this.clientVersionPolicy = HttpVersionPolicy.FORCE_HTTP_2;
4560
}
4661

4762
@Override
48-
protected void doOpen() {
63+
protected void doOpen() throws TRpcException {
4964
try {
5065
String keyStorePath = String
5166
.valueOf(getProtocolConfig().getExtMap().get(KEYSTORE_PATH));
@@ -61,20 +76,46 @@ protected void doOpen() {
6176
.build();
6277

6378
// 2. Configure connection pool.
79+
int maxConns = protocolConfig.getMaxConns();
6480
final PoolingAsyncClientConnectionManager cm = PoolingAsyncClientConnectionManagerBuilder
6581
.create().useSystemProperties()
6682
.setTlsStrategy(new ConscryptClientTlsStrategy(sslContext))
83+
.setMaxConnTotal(maxConns)
84+
.setMaxConnPerRoute(maxConns)
85+
.setConnPoolPolicy(PoolReusePolicy.LIFO)
86+
.setValidateAfterInactivity(TimeValue.ofMilliseconds(VALIDATE_AFTER_INACTIVITY_MS))
87+
.setConnectionTimeToLive(TimeValue.ofMinutes(CONNECTION_TTL_MINUTES))
6788
.build();
6889

6990
// 3. Configure the client to force HTTPS protocol to use HTTP1 communication and H2 protocol
7091
// to use H2 communication.
7192
httpAsyncClient = HttpAsyncClients.custom()
72-
.setVersionPolicy(this.clientVersionPolicy).setConnectionManager(cm)
93+
.setVersionPolicy(this.clientVersionPolicy)
94+
.setConnectionManager(cm)
95+
.setIOReactorConfig(IOReactorConfig.custom()
96+
.setSoKeepAlive(true)
97+
.build())
98+
.evictExpiredConnections()
99+
.evictIdleConnections(TimeValue.ofSeconds(EVICT_IDLE_CONNECTIONS_SECONDS))
73100
.build();
74101
// 4. Start the client.
75102
httpAsyncClient.start();
76103
} catch (Exception e) {
77-
logger.error("httpAsyncClient error: ", e);
104+
logger.error("open https/h2 client ({}) failed",
105+
getProtocolConfig().toSimpleString(), e);
106+
throw TRpcException.newFrameException(ErrorCode.TRPC_CLIENT_CONNECT_ERR,
107+
"open https/h2 client (" + getProtocolConfig().toSimpleString() + ") failed",
108+
e);
78109
}
79110
}
111+
112+
/**
113+
* Defensive close ensuring the inherited handle is released. We let the parent's
114+
* {@code doClose} do the actual cleanup; this method exists in case future TLS-only
115+
* resources need explicit release.
116+
*/
117+
@Override
118+
protected void doClose() {
119+
super.doClose();
120+
}
80121
}

0 commit comments

Comments
 (0)