我正在使用Apache HTTPClient 4连接到具有默认级别访问权限的twitter的流式api。它在开始时运行良好,但在检索数据几分钟后,它就会出现这个错误:
2012-03-28 16:17:00,040 DEBUG org.apache.http.impl.conn.SingleClientConnManager: Get connection for route HttpRoute[{tls}->http://myproxy:80->https://stream.twitter.com:443]
2012-03-28 16:17:00,040 WARN com.cloudera.flume.core.connector.DirectDriver: Exception in source: TestTwitterSource
java.lang.IllegalStateException: Invalid use of SingleClientConnManager: connection still allocated.
at org.apache.http.impl.conn.SingleClientConnManager.getConnection(SingleClientConnManager.java:216)
Make sure to release the connection before allocating another one.
at org.apache.http.impl.conn.SingleClientConnManager$1.getConnection(SingleClientConnManager.java:190)
我明白为什么我要面对这个问题。我试图在水槽群中使用这个HttpClient作为水槽源。代码如下所示:
public Event next() throws IOException, InterruptedException {
try {
HttpHost target = new HttpHost("stream.twitter.com", 443, "https");
new BasicHttpContext();
HttpPost httpPost = new HttpPost("/1/statuses/filter.json");
StringEntity postEntity = new StringEntity("track=birthday",
"UTF-8");
postEntity.setContentType("application/x-www-form-urlencoded");
httpPost.setEntity(postEntity);
HttpResponse response = httpClient.execute(target, httpPost,
new BasicHttpContext());
BufferedReader reader = new BufferedReader(new InputStreamReader(
response.getEntity().getContent()));
String line = null;
StringBuffer buffer = new StringBuffer();
while ((line = reader.readLine()) != null) {
buffer.append(line);
if(buffer.length()>30000) break;
}
return new EventImpl(buffer.toString().getBytes());
} catch (IOException ie) {
throw ie;
}
}
我正在尝试将响应流中的30,000个字符缓冲到StringBuffer,然后将其作为收到的数据返回。我显然没有关闭连接 - 但我想我还是不想关闭它。 Twitter的开发指南讨论了这个here它的内容如下:
某些HTTP客户端库仅返回响应主体 连接已被服务器关闭。这些客户端不起作用 用于访问Streaming API。您必须使用将要的HTTP客户端 逐步返回响应数据。最强大的HTTP客户端库 将提供此功能。 Apache HttpClient将处理 例如,这个用例。
它清楚地告诉您HttpClient将以递增方式返回响应数据。我已经完成了示例和教程,但是我没有找到任何接近这样做的东西。如果你们使用了httpclient(如果不是apache)并逐步阅读twitter的流式api,请告诉我你是如何实现这一壮举的。那些没有的人,请随时为答案做出贡献。 TIA。
更新
我尝试过这样做:1)我将流句柄移动到水槽源的开放方法。 2)使用简单的inpustream并将数据读入bytebuffer。所以这就是方法体现在的样子:
byte[] buffer = new byte[30000];
while (true) {
int count = instream.read(buffer);
if (count == -1)
continue;
else
break;
}
return new EventImpl(buffer);
这在一定程度上起作用 - 我得到推文,它们很好地被写入目的地。问题在于instream.read(缓冲区)返回值。即使流上没有数据,并且缓冲区具有默认的\ u0000字节和30,000个字节,因此该值将被写入目标。所以目标文件看起来像这样..“tweets..tweets..tweeets .. \ u0000 \ u0000 \ u0000 \ u0000 \ u0000 \ u0000 \ u0000 ... tweets..tweets ...”。我知道count不会返回-1因为这是一个永无止境的流,所以如何判断缓冲区是否有来自read命令的新内容?
答案 0 :(得分:0)
问题是您的代码泄漏了连接。无论您关闭内容流还是中止请求,请确保无论如何。
InputStream instream = response.getEntity().getContent();
try {
BufferedReader reader = new BufferedReader(
new InputStreamReader(instream));
String line = null;
StringBuffer buffer = new StringBuffer();
while ((line = reader.readLine()) != null) {
buffer.append(line);
if (buffer.length()>30000) {
httpPost.abort();
// connection will not be re-used
break;
}
}
return new EventImpl(buffer.toString().getBytes());
} finally {
// if request is not aborted the connection can be re-used
try {
instream.close();
} catch (IOException ex) {
// log or ignore
}
}
答案 1 :(得分:0)
事实证明这是一个水槽问题。 Flume经过优化,可以传输大小为32kb的事件。任何超过32kb的东西,Flume都会出局。 (解决方法是将事件大小调整为大于32KB)。所以,我已经将代码更改为至少缓冲20,000个字符。它有点工作,但它不是万无一失的。如果缓冲区长度超过32kb,这仍然会失败,但是,在一小时的测试中它到目前为止还没有失败 - 我认为这与Twitter不会在其公共流上发送大量数据这一事实有关。
while ((line = reader.readLine()) != null) {
buffer.append(line);
if(buffer.length()>20000) break;
}