Skip to content

Commit 6918f3c

Browse files
committed
Continuation on #328. Implement local HttpRequestPacket caching since we can't rely on Grizzly ThreadLocal caching.
1 parent ea0a4fc commit 6918f3c

File tree

5 files changed

+121
-43
lines changed

5 files changed

+121
-43
lines changed

Diff for: providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/EventHandler.java

+6-3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.glassfish.grizzly.filterchain.FilterChainContext;
3535
import org.glassfish.grizzly.http.HttpContent;
3636
import org.glassfish.grizzly.http.HttpHeader;
37+
import org.glassfish.grizzly.http.HttpRequestPacket;
3738
import org.glassfish.grizzly.http.HttpResponsePacket;
3839
import org.glassfish.grizzly.http.ProcessingState;
3940
import org.glassfish.grizzly.http.Protocol;
@@ -407,17 +408,19 @@ public boolean onHttpPacketParsed(HttpHeader httpHeader, FilterChainContext ctx)
407408
return result;
408409
}
409410
} finally {
410-
recycleResponsePacket(response);
411+
recycleRequestResponsePackets(response);
411412
}
412413

413414
}
414415

415416

416417
// ----------------------------------------------------- Private Methods
417418

418-
private static void recycleResponsePacket(final HttpResponsePacket response) {
419-
response.getRequest().setExpectContent(false);
419+
private static void recycleRequestResponsePackets(final HttpResponsePacket response) {
420+
HttpRequestPacket request = response.getRequest();
421+
request.setExpectContent(false);
420422
response.recycle();
423+
request.recycle();
421424
}
422425

423426
private static void processKeepAlive(final Connection c,

Diff for: providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/GrizzlyAsyncHttpProvider.java

+48
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
package org.asynchttpclient.providers.grizzly;
1515

1616
import org.asynchttpclient.AsyncHandler;
17+
import org.asynchttpclient.AsyncHttpClient;
1718
import org.asynchttpclient.AsyncHttpClientConfig;
1819
import org.asynchttpclient.AsyncHttpProvider;
1920
import org.asynchttpclient.HttpResponseBodyPart;
@@ -77,9 +78,11 @@
7778
import javax.net.ssl.SSLEngine;
7879
import java.io.File;
7980
import java.io.IOException;
81+
import java.util.ArrayList;
8082
import java.util.LinkedHashSet;
8183
import java.util.List;
8284
import java.util.concurrent.ExecutorService;
85+
import java.util.concurrent.Future;
8386
import java.util.concurrent.TimeUnit;
8487
import java.util.concurrent.TimeoutException;
8588

@@ -619,6 +622,51 @@ public static interface Cleanup {
619622

620623
}
621624

625+
626+
// ========================================================================
627+
628+
public static void main(String[] args) {
629+
AsyncHttpClientConfig config = new AsyncHttpClientConfig.Builder()
630+
.setMaximumConnectionsTotal(-1)
631+
.setMaximumConnectionsPerHost(4500)
632+
.setCompressionEnabled(false)
633+
.setAllowPoolingConnection(true /* keep-alive connection */)
634+
// .setAllowPoolingConnection(false /* no keep-alive connection */)
635+
.setConnectionTimeoutInMs(9000).setRequestTimeoutInMs(9000)
636+
.setIdleConnectionInPoolTimeoutInMs(3000).build();
637+
638+
AsyncHttpClient client =
639+
new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config);
640+
641+
final int warmupRequests = 100000;
642+
final String testUrl = "http://localhost:8080";
643+
List<Future<Response>> futures =
644+
new ArrayList<Future<Response>>(warmupRequests);
645+
for (int i = 0; i < warmupRequests; i++) {
646+
try {
647+
futures.add(client.prepareGet(testUrl).addHeader("Req", Integer.toString(i)).execute());
648+
} catch (IOException e) {
649+
System.err.println("Failed to execute get at iteration #" + i);
650+
}
651+
}
652+
int counter = 0;
653+
for (Future<Response> future : futures) {
654+
counter++;
655+
try {
656+
future.get();
657+
System.out.println(counter + " complete");
658+
} catch (Exception e) {
659+
System.err.println("Failed to execute get at iteration #" + counter);
660+
e.printStackTrace();
661+
client.close();
662+
System.exit(1);
663+
}
664+
}
665+
666+
System.out.println("Test complete...");
667+
client.close();
668+
}
669+
622670
}
623671

624672

Diff for: providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/GrizzlyResponseHeaders.java

+6-16
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@ class GrizzlyResponseHeaders extends HttpResponseHeaders {
3434

3535
private final FluentCaseInsensitiveStringsMap headers =
3636
new FluentCaseInsensitiveStringsMap();
37-
private final HttpResponsePacket response;
38-
private volatile boolean initialized;
3937

4038
// ------------------------------------------------------------ Constructors
4139

@@ -45,7 +43,12 @@ public GrizzlyResponseHeaders(final HttpResponsePacket response,
4543
final AsyncHttpProvider provider) {
4644

4745
super(uri, provider);
48-
this.response = response;
46+
final MimeHeaders headersLocal = response.getHeaders();
47+
for (String name : headersLocal.names()) {
48+
for (String header : headersLocal.values(name)) {
49+
headers.add(name, header);
50+
}
51+
}
4952

5053
}
5154

@@ -58,19 +61,6 @@ public GrizzlyResponseHeaders(final HttpResponsePacket response,
5861
*/
5962
@Override
6063
public FluentCaseInsensitiveStringsMap getHeaders() {
61-
if (!initialized) {
62-
synchronized (headers) {
63-
if (!initialized) {
64-
initialized = true;
65-
final MimeHeaders headersLocal = response.getHeaders();
66-
for (String name : headersLocal.names()) {
67-
for (String header : headersLocal.values(name)) {
68-
headers.add(name, header);
69-
}
70-
}
71-
}
72-
}
73-
}
7464
return headers;
7565
}
7666

Diff for: providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/GrizzlyResponseStatus.java

+20-8
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,13 @@
2929
*/
3030
public class GrizzlyResponseStatus extends HttpResponseStatus {
3131

32-
private final HttpResponsePacket response;
32+
private static final String PROTOCOL_NAME = "HTTP";
33+
private final int statusCode;
34+
private final String statusText;
35+
private final int majorVersion;
36+
private final int minorVersion;
37+
private final String protocolText;
38+
3339

3440

3541
// ------------------------------------------------------------ Constructors
@@ -40,7 +46,11 @@ public GrizzlyResponseStatus(final HttpResponsePacket response,
4046
final AsyncHttpProvider provider) {
4147

4248
super(uri, provider);
43-
this.response = response;
49+
statusCode = response.getStatus();
50+
statusText = response.getReasonPhrase();
51+
majorVersion = response.getProtocol().getMajorVersion();
52+
minorVersion = response.getProtocol().getMinorVersion();
53+
protocolText = response.getProtocolString();
4454

4555
}
4656

@@ -54,7 +64,7 @@ public GrizzlyResponseStatus(final HttpResponsePacket response,
5464
@Override
5565
public int getStatusCode() {
5666

57-
return response.getStatus();
67+
return statusCode;
5868

5969
}
6070

@@ -65,7 +75,7 @@ public int getStatusCode() {
6575
@Override
6676
public String getStatusText() {
6777

68-
return response.getReasonPhrase();
78+
return statusText;
6979

7080
}
7181

@@ -76,7 +86,7 @@ public String getStatusText() {
7686
@Override
7787
public String getProtocolName() {
7888

79-
return "http";
89+
return PROTOCOL_NAME;
8090

8191
}
8292

@@ -87,7 +97,7 @@ public String getProtocolName() {
8797
@Override
8898
public int getProtocolMajorVersion() {
8999

90-
return response.getProtocol().getMajorVersion();
100+
return majorVersion;
91101

92102
}
93103

@@ -98,7 +108,7 @@ public int getProtocolMajorVersion() {
98108
@Override
99109
public int getProtocolMinorVersion() {
100110

101-
return response.getProtocol().getMinorVersion();
111+
return minorVersion;
102112

103113
}
104114

@@ -108,7 +118,9 @@ public int getProtocolMinorVersion() {
108118
*/
109119
@Override
110120
public String getProtocolText() {
111-
return response.getProtocolString();
121+
122+
return protocolText;
123+
112124
}
113125

114126
}

Diff for: providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/filters/AsyncHttpClientFilter.java

+41-16
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import org.glassfish.grizzly.filterchain.NextAction;
4444
import org.glassfish.grizzly.http.HttpRequestPacket;
4545
import org.glassfish.grizzly.http.Method;
46+
import org.glassfish.grizzly.http.ProcessingState;
4647
import org.glassfish.grizzly.http.Protocol;
4748
import org.glassfish.grizzly.http.util.CookieSerializerUtils;
4849
import org.glassfish.grizzly.http.util.Header;
@@ -60,13 +61,16 @@
6061
import java.util.Collection;
6162
import java.util.List;
6263
import java.util.Map;
64+
import java.util.concurrent.ConcurrentLinkedQueue;
6365

6466
import static org.asynchttpclient.providers.grizzly.filters.SwitchingSSLFilter.getHandshakeError;
6567
import static org.asynchttpclient.util.AsyncHttpProviderUtils.getAuthority;
6668
import static org.asynchttpclient.util.MiscUtil.isNonEmpty;
6769

6870
public final class AsyncHttpClientFilter extends BaseFilter {
6971

72+
private ConcurrentLinkedQueue<HttpRequestPacketImpl> requestCache
73+
= new ConcurrentLinkedQueue<HttpRequestPacketImpl>();
7074

7175
private final AsyncHttpClientConfig config;
7276
private final GrizzlyAsyncHttpProvider grizzlyAsyncHttpProvider;
@@ -188,29 +192,31 @@ private boolean sendAsGrizzlyRequest(final Request request,
188192
convertToUpgradeRequest(httpCtx);
189193
}
190194

191-
final HttpRequestPacket.Builder builder = HttpRequestPacket.builder();
192-
builder.method(request.getMethod());
193-
builder.protocol(Protocol.HTTP_1_1);
195+
HttpRequestPacket requestPacket = requestCache.poll();
196+
if (requestPacket == null) {
197+
requestPacket = new HttpRequestPacketImpl();
198+
}
199+
requestPacket.setMethod(request.getMethod());
200+
requestPacket.setProtocol(Protocol.HTTP_1_1);
194201

195202
// Special handling for CONNECT.
196203
if (Method.CONNECT.matchesMethod(request.getMethod())) {
197204
final int port = uri.getPort();
198-
builder.uri(uri.getHost() + ':' + (port == -1 ? 443 : port));
205+
requestPacket.setRequestURI(uri.getHost() + ':' + (port == -1 ? 443 : port));
199206
} else {
200-
builder.uri(uri.getPath());
207+
requestPacket.setRequestURI(uri.getPath());
201208
}
202209

203210
if (GrizzlyAsyncHttpProvider.requestHasEntityBody(request)) {
204211
final long contentLength = request.getContentLength();
205212
if (contentLength > 0) {
206-
builder.contentLength(contentLength);
207-
builder.chunked(false);
213+
requestPacket.setContentLengthLong(contentLength);
214+
requestPacket.setChunked(false);
208215
} else {
209-
builder.chunked(true);
216+
requestPacket.setChunked(true);
210217
}
211218
}
212219

213-
HttpRequestPacket requestPacket;
214220
if (httpCtx.isWSRequest() && !httpCtx.isEstablishingTunnel()) {
215221
try {
216222
final URI wsURI = new URI(httpCtx.getWsRequestURI());
@@ -222,13 +228,11 @@ private boolean sendAsGrizzlyRequest(final Request request,
222228
} catch (URISyntaxException e) {
223229
throw new IllegalArgumentException("Invalid WS URI: " + httpCtx.getWsRequestURI());
224230
}
225-
} else {
226-
requestPacket = builder.build();
227231
}
228232

229233
requestPacket.setSecure(secure);
230234
addQueryString(request, requestPacket);
231-
addHostHeader(request, uri, builder);
235+
addHostHeader(request, uri, requestPacket);
232236
addGeneralHeaders(request, requestPacket);
233237
addCookies(request, requestPacket);
234238

@@ -325,15 +329,15 @@ private static FilterChainContext obtainProtocolChainContext(
325329

326330
private static void addHostHeader(final Request request,
327331
final URI uri,
328-
final HttpRequestPacket.Builder builder) {
332+
final HttpRequestPacket requestPacket) {
329333
String host = request.getVirtualHost();
330334
if (host != null) {
331-
builder.header(Header.Host, host);
335+
requestPacket.addHeader(Header.Host, host);
332336
} else {
333337
if (uri.getPort() == -1) {
334-
builder.header(Header.Host, uri.getHost());
338+
requestPacket.addHeader(Header.Host, uri.getHost());
335339
} else {
336-
builder.header(Header.Host, uri.getHost() + ':' + uri.getPort());
340+
requestPacket.addHeader(Header.Host, uri.getHost() + ':' + uri.getPort());
337341
}
338342
}
339343
}
@@ -479,4 +483,25 @@ public void getBytes(byte[] bytes) {
479483
}
480484

481485
} // END GrizzlyTransferAdapter
486+
487+
488+
class HttpRequestPacketImpl extends HttpRequestPacket {
489+
490+
private ProcessingState processingState = new ProcessingState();
491+
492+
// -------------------------------------- Methods from HttpRequestPacketImpl
493+
494+
495+
@Override
496+
public ProcessingState getProcessingState() {
497+
return processingState;
498+
}
499+
500+
@Override
501+
public void recycle() {
502+
super.recycle();
503+
processingState.recycle();
504+
requestCache.add(this);
505+
}
506+
}
482507
}

0 commit comments

Comments
 (0)