2013-06-09 36 views
5

Tôi đang cố gắng tạo yêu cầu phát trực tuyến HTTP "full-duplex" bằng cách sử dụng Apache HTTPClient.Apache HTTPClient Streaming HTTP POST Request?

Trong nỗ lực đầu tiên của tôi, tôi đã cố gắng sử dụng mã yêu cầu sau đây:

URL url=new URL(/* code goes here */); 

HttpPost request=new HttpPost(url.toString()); 

request.addHeader("Connection", "close"); 

PipedOutputStream requestOutput=new PipedOutputStream(); 
PipedInputStream requestInput=new PipedInputStream(requestOutput, DEFAULT_PIPE_SIZE); 
ContentType requestContentType=getContentType(); 
InputStreamEntity requestEntity=new InputStreamEntity(requestInput, -1, requestContentType); 
request.setEntity(requestEntity); 

HttpEntity responseEntity=null; 
HttpResponse response=getHttpClient().execute(request); // <-- Hanging here 
try { 
    if(response.getStatusLine().getStatusCode() != 200) 
     throw new IOException("Unexpected status code: "+response.getStatusLine().getStatusCode()); 

    responseEntity = response.getEntity(); 
} 
finally { 
    if(responseEntity == null) 
     request.abort(); 
} 

InputStream responseInput=responseEntity.getContent(); 
ContentType responseContentType; 
if(responseEntity.getContentType() != null) 
    responseContentType = ContentType.parse(responseEntity.getContentType().getValue()); 
else 
    responseContentType = DEFAULT_CONTENT_TYPE; 

Reader responseStream=decode(responseInput, responseContentType); 
Writer requestStream=encode(requestOutput, getContentType()); 

Yêu cầu treo tại cửa sổ dòng nêu trên. Dường như mã đang cố gắng gửi toàn bộ yêu cầu trước khi nhận được phản hồi. Nhìn lại, điều này có ý nghĩa. Tuy nhiên, nó không phải là những gì tôi đã hy vọng. Thay vào đó, tôi đã hy vọng gửi tiêu đề yêu cầu với số Transfer-Encoding: chunked, nhận tiêu đề phản hồi HTTP/1.1 200 OK với tiêu đề Transfer-Encoding: chunked của riêng nó và sau đó tôi sẽ có kết nối HTTP trực tuyến song công để làm việc.

Hạnh phúc, HTTPClient của tôi có một ứng dụng không đồng bộ dựa trên NIO khác với các ví dụ sử dụng tốt (như this one). Câu hỏi của tôi là:

  1. Tôi có giải thích hành vi HTTPClient đồng bộ đúng không? Hoặc là có một cái gì đó tôi có thể làm gì để tiếp tục sử dụng (đơn giản) đồng bộ HTTPClient theo cách tôi mô tả?
  2. Ứng dụng khách NIO có chờ đợi để gửi toàn bộ yêu cầu trước khi tìm kiếm phản hồi không? Hoặc tôi có thể gửi yêu cầu tăng dần và nhận được phản hồi tăng dần cùng một lúc không?

Nếu HTTPClient không hỗ trợ phương thức này, có thư viện máy khách HTTP khác sẽ không? Hoặc tôi có nên lập kế hoạch viết một HTTP client (tối thiểu) để hỗ trợ phương thức này không?

Trả lời

1

Đây là quan điểm của tôi về skim đọc mã:

  1. tôi có thể không hoàn toàn đồng ý với thực tế là một phản ứng phi-200 có nghĩa là thất bại. Tất cả các phản hồi 2XX chủ yếu là hợp lệ. Kiểm tra wiki để biết thêm chi tiết

  2. Đối với bất kỳ yêu cầu TCP nào, tôi khuyên bạn nên nhận toàn bộ phản hồi để xác nhận rằng nó hợp lệ. Tôi nói điều này bởi vì, một phản ứng một phần chủ yếu có thể được coi là phản ứng xấu vì hầu hết các ứng dụng khách không thể sử dụng nó. (Hãy tưởng tượng một trường hợp máy chủ đang phản hồi bằng 2MB dữ liệu và nó đi xuống trong thời gian này)

+0

Đây là những điểm tốt. Tôi chỉ chấp nhận 200 vì tôi đang ở chế độ thử nghiệm; bạn nói đúng là tôi nên chấp nhận 2XX để thành công. Tuy nhiên, đối với điểm thứ hai, toàn bộ mục đích của việc thực hiện là nhận và xử lý phản hồi theo thời gian. – sigpwned

+0

Đây không phải là lý do khiến chương trình bị treo. Đó là do luồng đầu vào đường ống. –

0

Một chủ đề riêng biệt phải được văn bản cho OutputStream cho mã của bạn để làm việc.

  • Đoạn mã trên cung cấp HttpClient với một PipedInputStream.
  • PipedInputStream tạo sẵn các byte khi chúng được ghi vào OutputStream tương ứng.
  • Đoạn mã trên không ghi vào luồng OutputStream (mà phải được thực hiện bởi một chủ đề riêng biệt.
  • Do đó mã được treo chính xác nơi nhận xét của bạn là.
  • Dưới mui xe, khách hàng Apache nói "InputStream. read() "trong trường hợp các luồng đường ống yêu cầu outputStream.write (byte) được gọi trước đó (bởi một luồng riêng biệt).
  • Vì bạn không bơm byte vào OutputStream được kết hợp từ một luồng riêng biệt, InputStream chỉ ngồi và chờ luồng OutputStream được ghi bởi "một số chuỗi khác".

Từ JavaDocs:

Một đường ống input stream nên được kết nối với một dòng đầu ra bằng đường ống; luồng đầu vào đường ống sau đó cung cấp bất kỳ byte dữ liệu nào được ghi vào luồng đầu ra đường ống.

Thông thường, dữ liệu được đọc từ đối tượng PipedInputStream bởi một sợi và dữ liệu được ghi vào PipedOutputStream tương ứng theo một số chủ đề khác.

Cố gắng sử dụng cả hai đối tượng từ một sợi đơn không được đề xuất là vì nó có thể bế tắc chuỗi.

Luồng đầu vào đường ống chứa bộ đệm, tách hoạt động đọc khỏi các hoạt động viết, trong giới hạn. Một đường ống được gọi là "bị hỏng" nếu một luồng đã cung cấp byte dữ liệu cho luồng đầu ra được kết nối đường ống không còn hoạt động nữa.

Note: Dường như với tôi, kể từ khi con suối bằng đường ống và đồng thời không được đề cập trong báo cáo vấn đề của bạn, rằng nó không cần thiết. Hãy thử gói một ByteArrayInputStream() với đối tượng Entity thay vì đầu tiên để kiểm tra sự lành mạnh ... điều đó sẽ giúp bạn thu hẹp vấn đề.

Cập nhật

Ngẫu nhiên, tôi đã viết một đảo ngược của Apache HTTP API Khách hàng [PipedApacheClientOutputStream] cung cấp một giao diện OutputStream cho HTTP POST sử dụng Apache Commons HTTP khách hàng 4.3.4. Điều này có thể được gần gũi với những gì bạn đang tìm kiếm ...

Calling-mã trông như thế này:

// Calling-code manages thread-pool 
ExecutorService es = Executors.newCachedThreadPool(
    new ThreadFactoryBuilder() 
    .setNameFormat("apache-client-executor-thread-%d") 
    .build()); 


// Build configuration 
PipedApacheClientOutputStreamConfig config = new  
    PipedApacheClientOutputStreamConfig(); 
config.setUrl("http://localhost:3000"); 
config.setPipeBufferSizeBytes(1024); 
config.setThreadPool(es); 
config.setHttpClient(HttpClientBuilder.create().build()); 

// Instantiate OutputStream 
PipedApacheClientOutputStream os = new  
PipedApacheClientOutputStream(config); 

// Write to OutputStream 
os.write(...); 

try { 
    os.close(); 
} catch (IOException e) { 
    logger.error(e.getLocalizedMessage(), e); 
} 

// Do stuff with HTTP response 
... 

// Close the HTTP response 
os.getResponse().close(); 

// Finally, shut down thread pool 
// This must occur after retrieving response (after is) if interested 
// in POST result 
es.shutdown(); 

Note- Trong thực tế cùng khách hàng, dịch vụ chấp hành viên, và config có thể sẽ được tái sử dụng trong suốt vòng đời của ứng dụng, vì vậy chuẩn bị và mã đóng bên ngoài trong ví dụ trên sẽ có khả năng sống trong bootstrap/init và mã finalization thay vì trực tiếp inline với bản phát hành OutputStream.