You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Jetty HttpClient流式解析JSON与UrlEncoded响应?

Optimal Streaming Parsing for Jetty HttpClient (UrlEncoded/JSON Responses)

Great question! Ditching BufferingResponseListener for streaming parsing is a smart move—especially for high-throughput systems or large payloads—since it eliminates loading entire responses into memory at once. Let’s walk through how to implement this with Jetty’s built-in tools, tailored to your ADM messaging class.

Why Streaming is Better Than Buffering

BufferingResponseListener stores the full response body in a byte array before parsing, which can lead to unnecessary memory overhead. Streaming parsing processes the response incrementally as data arrives, keeping memory usage low and improving scalability.


1. Streaming UrlEncoded Responses (Token Request)

Jetty’s UrlEncoded class has native support for streaming parsing via decodeTo(InputStream, Charset, MultiMap). We can use Response.Listener.Adapter to access the response input stream directly without buffering.

Modified Token Listener

Replace your mTokenListener with this implementation:

private final Response.Listener mTokenListener = new Response.Listener.Adapter() {
    @Override
    public void onSuccess(Response response) {
        try (InputStream inputStream = response.getInputStream()) {
            MultiMap<String> params = new MultiMap<>();
            // Stream the response directly into the MultiMap
            UrlEncoded.decodeTo(inputStream, StandardCharsets.UTF_8, params);
            
            long now = System.currentTimeMillis() / 1000;
            mExpiresIn = now + Long.parseLong(params.getString("expires_in"));
            mAccessToken = params.getString("access_token");
        } catch (IOException e) {
            // Handle parsing/stream errors appropriately (log, alert, etc.)
            e.printStackTrace();
        }
    }

    @Override
    public void onFailure(Response response, Throwable failure) {
        // Handle request failures (network issues, HTTP errors)
        failure.printStackTrace();
    }
};

Key benefits here:

  • No intermediate byte buffer for the full response
  • UrlEncoded.decodeTo parses key-value pairs as they’re read from the stream
  • try-with-resources ensures the input stream is closed properly

2. Streaming JSON Responses (Message Send Results)

Jetty’s JSON class offers parse(InputStream), which reads from a stream instead of a pre-buffered string. Note: While this uses an input stream, Jetty’s underlying JSON parser still loads the entire document into memory to build the map. For true streaming JSON parsing (for very large payloads), we’ll also cover a third-party option with Jackson.

Option A: Jetty’s Built-in JSON Parsing

Replace your mMessageListener with this:

private final Response.Listener mMessageListener = new Response.Listener.Adapter() {
    @Override
    public void onSuccess(Response response) {
        try (InputStream inputStream = response.getInputStream()) {
            // Parse directly from the response stream
            Map<String, Object> resp = (Map<String, Object>) JSON.parse(inputStream);
            
            String oldRegistrationId = (String) response.getRequest().getAttributes().get("registrationID");
            String newRegistrationId = (String) resp.get("registrationID");
            if (newRegistrationId != null && !newRegistrationId.equals(oldRegistrationId)) {
                // Update the changed ADM registration id in the database
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onFailure(Response response, Throwable failure) {
        if (response.getStatus() % 100 == 4) {
            try (InputStream inputStream = response.getInputStream()) {
                Map<String, Object> resp = (Map<String, Object>) JSON.parse(inputStream);
                String reason = (String) resp.get("reason");
                
                if ("AccessTokenExpired".equals(reason)) {
                    postToken();
                } else if ("Unregistered".equals(reason)) {
                    // Delete the invalid ADM registration id from the database
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        failure.printStackTrace();
    }
};

Option B: True Streaming JSON with Jackson (For Large Payloads)

If you’re dealing with large JSON responses and want minimal memory usage, use Jackson’s streaming API. First add Jackson’s core dependency to your project, then modify the parsing logic:

// Add these imports:
// com.fasterxml.jackson.core.JsonParser;
// com.fasterxml.jackson.core.JsonToken;
// com.fasterxml.jackson.databind.ObjectMapper;

private final Response.Listener mMessageListener = new Response.Listener.Adapter() {
    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public void onSuccess(Response response) {
        try (InputStream inputStream = response.getInputStream()) {
            JsonParser parser = objectMapper.getFactory().createParser(inputStream);
            
            String oldRegistrationId = (String) response.getRequest().getAttributes().get("registrationID");
            String newRegistrationId = null;

            // Iterate through JSON tokens incrementally
            while (parser.nextToken() != JsonToken.END_OBJECT) {
                String fieldName = parser.getCurrentName();
                if ("registrationID".equals(fieldName)) {
                    parser.nextToken(); // Move to the field value
                    newRegistrationId = parser.getText();
                    break; // No need to parse the rest of the document
                }
                parser.skipChildren(); // Skip nested objects/arrays we don't care about
            }

            if (newRegistrationId != null && !newRegistrationId.equals(oldRegistrationId)) {
                // Update the database
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // Implement onFailure similarly with streaming parsing for error responses
};

This approach only parses the specific fields you need, avoiding loading the entire JSON document into memory.


Final Notes

  • Always use try-with-resources to close input streams and prevent resource leaks.
  • For your ADM use case, the Jetty-native streaming methods will likely be sufficient unless you’re handling extremely large responses.
  • When switching listeners, ensure you update the send() calls in postToken() and postMessage()—they’ll work seamlessly with the new Response.Listener implementations.

内容的提问来源于stack exchange,提问作者Alexander Farber

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:42:50