由于业务需求及历史渊源,我需要在后台使用 ssh+sparkShell提交spark任务,而在spark执行的jar包中,出现了一个神奇的bug。
我需要在jar报的主类main方法中调用http以获取大量的历史数据。代码如下:
public static String doPost(String url, String param, DataParam paramData) {
PrintWriter out = null;
BufferedReader in = null;
StringBuilder result = new StringBuilder();
HttpURLConnection conn=null;
String returnLine = "";
try {
URL httpUrl = new URL(url);
// 打开和URL之间的连接
conn= (HttpURLConnection)httpUrl.openConnection();
// 设置连接超时
conn.setConnectTimeout(3000000);
// 设置读取超时
conn.setReadTimeout(3000000);
// 设置请求方式
conn.setRequestMethod("POST");
// 设置通用的请求属性
conn.setRequestProperty("accept", "*/*");
//conn.setRequestProperty("Connection", "keep-alive");
conn.setRequestProperty("Connection", "close");
conn.setRequestProperty("Content-Type", "application/json");
conn.setRequestProperty("Accept-Encoding", "gzip, deflate, br");
// 发送POST请求,必须设置如下两行
conn.setDoOutput(true);
conn.setDoInput(true);
// 获取URLConnection对象对应的输出流
out = new PrintWriter(conn.getOutputStream());
// 发送请求参数
out.print(param);
// flush输出流的缓冲
out.flush();
InputStream inputStream = conn.getInputStream();
// 定义BufferedReader输入流来读取URL的响应
in = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = in.readLine()) != null) {
result.append(line);
}
} catch (Exception e) {
e.printStackTrace();
//自己封装的日志,不用在意
LogUtils.printlog("HTTP POST error : {"+e.getMessage()+"}", paramData);
} finally {
try {
if (out != null) out.close();
if (in != null) in.close();
conn.disconnect();
} catch (IOException ex) {
ex.printStackTrace();
LogUtils.printlog("HTTP POST error : {"+ex.getMessage()+"}", paramData);
}
}
return returnLine;
这时候历史数据过大,导致了OOM,如下图。明显是在readLine()时溢出了。
Exception in thread "main" java.lang.OutOfMemoryError
at java.lang.StringBuffer.append(StringBuffer.java:367)
at java.io.BufferedReader.readLine(BufferedReader.java:370)
at java.io.BufferedReader.readLine(BufferedReader.java:389)
at com.cloudata.utils.DataPlatformUtil.doPost(DataPlatformUtil.java:73)
后来我也想过是数据太大,于是我根据获取历史数据的时间,按照每天或者每小时进行切割,循环调用接口。于是可怕的一幕出现了。这也是相当灵异。for循环的第一次可以正常返回数据,第二次调用时,这个地方报错了。
报错如下:
HTTP POST error : {org.apache.hadoop.fs.FsUrlConnection cannot be cast to java.net.HttpURLConnection
我惊呆了,在我笔记本上测试时无问题的,但是上服务器就报错,我开始意识到可能是Connection连接或者流的问题。我检查了很久,没发现什么问题,我尝试将属性从
conn.setRequestProperty("Connection", "keep-alive");
改为
conn.setRequestProperty("Connection", "close");
然而并没有用,因为我确定是这里面有问题,但找不到,问题还着急上线,我就把http换成CloseableHttpClient了。速度稍微受点影响,但问题解决了。
public static String doPost(String url,String json,DataParam dataParam){
LogUtils.printlog("url : {"+url+"}" +
"param : {"+json+"}", dataParam);
// 创建httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(300 * 1000)
.setConnectTimeout(300 * 1000).build();
// 创建post方式请求对象
HttpPost post = new HttpPost(url);
post.setConfig(requestConfig);
// 请求的数据包为raw,设置报文头为Content-Type
post.setHeader("Content-Type", "application/json;charset=utf-8");
// 装载参数
StringEntity postingString = new StringEntity(json.toString(), "utf-8");
post.setEntity(postingString);
// 执行请求并拿到结果
HttpResponse response = null;
String content = null;
try {
response = httpClient.execute(post);
// 判断返回状态是否正常
int state = response.getStatusLine().getStatusCode();
if (state != HttpStatus.SC_OK) {
LogUtils.printlog("HTTP POST error : {"+"connection faild 错误代码:"+state+"}", dataParam);
}
// 获取结果实体并返回结果
HttpEntity entity = response.getEntity();
content = EntityUtils.toString(entity);
httpClient.close();
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
LogUtils.printlog("HTTP POST error : {"+e.getMessage()+"}", dataParam);
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
LogUtils.printlog("HTTP POST error : {"+e.getMessage()+"}", dataParam);
}
return content;
}
虽然问题解决了,但是以上先的两个问题并没有真真解决,特别是循环调用第二次出现连接转化异常,我非常纳闷,希望有大神可以提示一下。