- 浏览: 38345 次
- 性别:
- 来自: 北京
文章分类
最新评论
package com.jd.jdcommons.bi;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLDecoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import net.sf.json.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jd.framework.util.string.StringUtil;
public class BiTaskUtil {
private static final Logger logger = LoggerFactory.getLogger(BiTaskUtil.class);
private static final Map<String, String> configCache = new HashMap<String, String>();
private static final CloseableHttpClient httpClient;
private static final String CHARSET = "UTF-8";
private static ObjectMapper objectMapper = null;
static {
RequestConfig config = RequestConfig.custom().setConnectTimeout(60000).setSocketTimeout(15000).build();
httpClient = HttpClientBuilder.create().setDefaultRequestConfig(config).build();
String filePath = BiTaskUtil.class.getResource("/").getPath() + "biconfig/bi-config.properties";
Properties pps = new Properties();
try {
pps.load(new FileInputStream(filePath));
Enumeration<?> enum1 = pps.propertyNames();//得到配置文件的名字
while(enum1.hasMoreElements()) {
String strKey = (String) enum1.nextElement();
if (!StringUtil.isEmpty(strKey)) {
String strValue = pps.getProperty(strKey);
strValue = (strValue == null ? "" : strValue.trim());
configCache.put(strKey.trim(), strValue);
}
}
objectMapper = new ObjectMapper();
// 设置输入时忽略在JSON字符串中存在但Java对象实际没有的属性
objectMapper.disable(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES);
objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
} catch (IOException e) {
logger.error("bi接口API调用工具类在初始化时,解析配置文件[" + filePath + "]异常:", e);
throw new RuntimeException("bi接口API调用工具类在初始化时,解析配置文件[" + filePath + "]异常:", e);
}
}
/**
* 调用BI保存任务的接口 http:aa/api/task/save?time="1234765464"&data="{}"
* @return SaveTaskRetVo
* @throws Exception
*/
public static SaveTaskRetVo saveTask(String erp, String sqlNum, String sqlContent, String jobName, String targetFileName) throws Exception {
logger.warn("调用bi保存任务接口执行开始!");
SaveTaskRetVo str = null;
List<NameValuePair> paramsList = new ArrayList<NameValuePair>();
Date now = new Date();
paramsList.add(new BasicNameValuePair("time", String.valueOf(now.getTime())));
String url = configCache.get("saveUrl");
JSONObject jsonParam = new JSONObject();
jsonParam.put("jssResourceId", configCache.get("jssResourceId"));
if (!StringUtil.isEmpty(configCache.get("groupName")))
jsonParam.put("groupName", configCache.get("groupName"));
paramsList.add(new BasicNameValuePair("data", jsonParam.toString()));
// 执行保存API接口请求
String result = post(url, paramsList);
if (!StringUtil.isEmpty(result)) {
JSONObject jsonObject = JSONObject.fromObject(result);
str = new SaveTaskRetVo();
if(0 == jsonObject.getInt("code")){
JSONObject responseBody = (JSONObject) jsonObject.get("obj");
str = objectMapper.readValue(responseBody.toString(), SaveTaskRetVo.class);
} else {
logger.warn("调用bi保存任务接口失败 ! url:" + url + ",SQL_NUM=" + sqlNum);
}
str.setCode(jsonObject.getInt("code"));
str.setSuccess(jsonObject.getBoolean("success"));
str.setMessage(jsonObject.getString("message"));
} else {
logger.warn("调用bi保存任务接口返回数据为空 ! url:" + url + ",SQL_NUM=" + sqlNum);
str = new SaveTaskRetVo();
str.setCode(-1);
str.setSuccess(false);
str.setMessage("调用bi保存任务接口,返回值为空!");
}
logger.warn("调用bi保存任务接口执行结束!");
return str;
}
/**
* 调用BI查询任务的接口 http:aa/api/task/query?time="1234765464"&data="{}"
* @return
* @throws Exception
*/
public static QueryTaskRetVo queryTask( int id) throws Exception {
logger.warn("调用bi查询任务接口执行开始!");
QueryTaskRetVo str = null;
List<NameValuePair> paramsList = new ArrayList<NameValuePair>();
Date now = new Date();
paramsList.add(new BasicNameValuePair("time", String.valueOf(now.getTime())));
String url = configCache.get("queryUrl");
JSONObject jsonParam = new JSONObject();
jsonParam.put("id", id);
paramsList.add(new BasicNameValuePair("data", jsonParam.toString()));
// 执行查询API接口请求
String result = post(url, paramsList);
if (!StringUtil.isEmpty(result)) {
JSONObject jsonObject = JSONObject.fromObject(result);
str = new QueryTaskRetVo();
if(0 == jsonObject.getInt("code")){
JSONObject responseBody = (JSONObject) jsonObject.get("obj");
str = objectMapper.readValue(responseBody.toString(), QueryTaskRetVo.class);
} else {
logger.warn("调用bi查询任务接口失败 ! url:" + url + ",任务ID:" + id);
}
str.setCode(jsonObject.getInt("code"));
str.setSuccess(jsonObject.getBoolean("success"));
str.setMessage(jsonObject.getString("message"));
} else {
logger.warn("调用bi查询任务接口返回数据为空 ! url:" + url + ",任务ID:" + id);
str = new QueryTaskRetVo();
str.setCode(-1);
str.setSuccess(false);
str.setMessage("调用bi查询任务接口,返回值为空!");
}
logger.warn("调用bi查询任务接口执行结束!");
return str;
}
/**
* 执行post请求
* @param url 请求url
* @param paramsList 参数列表
* @return
*/
public static String post(String url, List<NameValuePair> paramsList) {
String result = null;
try {
HttpPost httpPost = new HttpPost(url);
//添加参数 , 设置编码
httpPost.setEntity(new UrlEncodedFormEntity(paramsList, CHARSET));
CloseableHttpResponse response = httpClient.execute(httpPost);
int statusCode = response.getStatusLine().getStatusCode();
if(statusCode != HttpStatus.SC_OK){
httpPost.abort();
logger.error("bi接口请求失败," + "url:" + url + ",返回状态信息:" + statusCode);
throw new RuntimeException("bi接口请求失败," + "url:" + url + ",返回状态信息:" + statusCode);
}
HttpEntity entity = response.getEntity();
if (entity != null){
result = EntityUtils.toString(entity, CHARSET);
}
EntityUtils.consume(entity);
response.close();
if (!StringUtil.isEmpty(result)) {
result = URLDecoder.decode(result, CHARSET);
}
} catch (Exception e) {
logger.error("bi接口请求失败," + "url:" + url + ",异常信息:" + e.getMessage());
throw new RuntimeException("bi接口请求失败," + "url:" + url + ",异常信息:" + e.getMessage());
}
return result;
}
/**
* 得到配置文件的属性值
* @param key
* @return
*/
public static String getString(String key) {
return configCache.get(key);
}
public static void main(String[] args) throws Exception {
/*System.out.println(configCache.get("bi.appId"));
JSONObject jsonParam = new JSONObject();
JSONObject obj = new JSONObject();
obj.put("id", 0001);
jsonParam.put("obj", obj.toString());
jsonParam.put("code", 0);
jsonParam.put("success", true);
jsonParam.put("message", "sssss");
JSONObject jsonObject = JSONObject.fromObject(jsonParam.toString());
SaveTaskRetVo str = new SaveTaskRetVo();
if(!StringUtil.isEmpty(jsonParam.toString()) && (0 == jsonObject.getInt("code"))){
JSONObject responseBody = (JSONObject) jsonObject.get("obj");
str=(SaveTaskRetVo) JSONObject.toBean(responseBody, SaveTaskRetVo.class);
str.setCode(jsonObject.getInt("code"));
str.setSuccess(jsonObject.getBoolean("success"));
str.setMessage(jsonObject.getString("message"));
} else {
logger.info("调用erp 接口 通过组织获取用户 返回失败 !response:" + " url:" + "ss" );
}
int jobType = 5;
jsonParam = new JSONObject();
jsonParam.put("jobName", "");
jsonParam.put("jobType", jobType);
jsonParam.put("marketCode",configCache.get("marketCode"));
jsonParam.put("dbName", configCache.get("dbName"));
jsonParam.put("content", "");
jsonParam.put("erp", "");
jsonParam.put("targetFileType", configCache.get("targetFileType"));
jsonParam.put("targetFileName", "");
jsonParam.put("spaceMark", configCache.get("spaceMark"));
jsonParam.put("targetType", Integer.valueOf(configCache.get("targetType")));
jsonParam.put("jssResourceId", configCache.get("jssResourceId"));
jsonParam.put("groupName", configCache.get("groupName"));
System.out.println("====" + jsonParam.toString());*/
QueryTaskRetVo obj = BiTaskUtil.queryTask(192903);
System.out.println(obj.getCode());
}
}
补充:每次新建一个httpclient时,会发生connection reset异常;原因如下(来源:http://blog.csdn.net/lcx46/article/details/38984335)mark一下。
HttpClient引起的TCP连接数高的问题分析
博客分类: Java网络编程
.
【问题现象】
系统上线后出现TCP连接数超过预期阀值,最高值达到8K左右,新上线代码中包含了一文件上传操作,使用的是apache的commons-httpclient包。
【问题分析】
1、先确认是否存在连接未关闭问题引起的。
观察发现,TCP连接数不是一直在增长,而是会有所下降。并且当业务低峰期TCP连接数TCP连接数会降到100左右,这说明TCP连接还是会关闭。
2、确定居高不下的TCP使用情况
使用"netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'"命令发现,处于ESTABLISHED状态的连接数最多,在查看了一下处于ESTABLISHED状态的目的IP,基本上都是文件服务器的IP,这说明还是跟新增加的文件上传操作有关。但是按照代码的逻辑来看,文件上传操作是多线程处理的,一个线程处理一个上传操作,线程池中一共有10个线程,照此分析正常的话应该有10个左右与文件服务器的链接,不应该出现几千个链接。因此怀疑是连接没有主动释放,而是等待连接超时才开始释放。
3、为什么会连接超时
查看了文件上传部分代码,主要代码如下:
Java代码
1.HttpClient client = new HttpClient();
2.MultipartPostMethod method = new MultipartPostMethod(config .getUploadInterface());
3.try{
4. client.executeMethod(method);
5.}catch (Exception e){
6. throw e;
7.}finally{
8. method.releaseConnection();
9.}
从代码里看是已经释放连接了,但是从结果上看没有释放连接,那就产生一个问题,这个地方真的能释放连接吗?我们在释放连接后面增加一行测试代码来看看:
Java代码
1.HttpConnection conn = client.getHttpConnectionManager().getConnection(client.getHostConfiguration());
2.System.out.println(conn.isOpen());
打印出的结果是true,也就是说虽然调用了releaseConnection,但是并没有释放连接!!
4、分析commons-httpclient相关代码
现在怀疑是我们使用的方式不对了,继续分析一下commons-https包中相关代码,首先看一下method.releaseConnection()的代码实现:
Java代码
1.public void releaseConnection() {
2. try {
3. if (this.responseStream != null) {
4. try {
5. // FYI - this may indirectly invoke responseBodyConsumed.
6. this.responseStream.close();
7. } catch (IOException ignore) {
8. }
9. }
10. } finally {
11. ensureConnectionRelease();
12. }
13. }
Java代码
1.private void ensureConnectionRelease() {
2. if (responseConnection != null) {
3. responseConnection.releaseConnection();
4. responseConnection = null;
5. }
6. }
经过debug发现responseStream为null,并且responseConnection也为null,这样改调用就没有实际意义。那么我们应该怎么来释放连接呢?
5、继续分析代码
我们发现在org.apache.commons.httpclient.HttpMethodDirector类的第208行已经在finally中释放连接了:
Java代码
1.finally {
2. if (this.conn != null) {
3. this.conn.setLocked(false);
4. }
5. // If the response has been fully processed, return the connection
6. // to the pool. Use this flag, rather than other tests (like
7. // responseStream == null), as subclasses, might reset the stream,
8. // for example, reading the entire response into a file and then
9. // setting the file as the stream.
10. if (
11. (releaseConnection || method.getResponseBodyAsStream() == null)
12. && this.conn != null
13. ) {
14. this.conn.releaseConnection();
15. }
16. }
Java代码
1.public void releaseConnection(HttpConnection conn) {
2. if (conn != httpConnection) {
3. throw new IllegalStateException("Unexpected release of an unknown connection.");
4. }
5.
6. finishLastResponse(httpConnection);
7.
8. inUse = false;
9.
10. // track the time the connection was made idle
11. idleStartTime = System.currentTimeMillis();
12. }
这个地方我们可以看到了所谓的释放连接并不是真的释放,还是return the connection to pool,照此分析,我们每个线程中new了一个HttpClient类,而每个HttpClient类中的链接都是没有close的,只是归还到httpClient中的pool而已,这些连接也必须等到连接超时才会被释放,由此可以分析出来连接数上涨的原因。那么我们应该怎么使用呢?按照代码的设计,看起来httpclient应该是单例的,但是在httpClient类的javadoc中并没有关于线程安全方面的说明,为此我们再回到官网上看相关文档,在文档(http://hc.apache.org/httpclient-3.x/performance.html)上我们看到如下的说明:
Java代码
1.HttpClient is fully thread-safe when used with a thread-safe connection manager such as MultiThreadedHttpConnectionManager
这说明在多线程环境下应该使用一个全局单例的HttpClient,并且使用MultiThreadHttpConnectionManager来管理Connection。
【相关结论】
1、HttpClient内部使用了池化技术,内部的链接是为了复用。在多线程条件下,可以使用一个全局的HttpClient实例,并且使用MultiThreadHttpConnectionManager来管理Connection。
2、使用开源软件之前一定要读读相关代码,看看官方推荐使用方式。
3、在解决此问题后,读了读httpclient中其他包中的代码,在读的时候发现对于理解http协议帮助很大,特别是文件上传,长连接,auth鉴权等。
4、相对于httpurlconnection ,httpclient更加丰富,也更加强大,其中apache有两个项目都是httpclient,一个是commonts包下的,这个是通用的,更专业的是org.apache.http.包下的,所以我一般用后者;对于httpclient4.5,连接池管理变更为如下
PoolingClientConnectionManager conMgr = new PoolingClientConnectionManager();
conMgr.setMaxTotal(200); //设置整个连接池最大连接数 根据自己的场景决定
//是路由的默认最大连接(该值默认为2),限制数量实际使用DefaultMaxPerRoute并非MaxTotal。
//设置过小无法支持大并发(ConnectionPoolTimeoutException: Timeout waiting for connection from pool),路由是对maxTotal的细分。
conMgr.setDefaultMaxPerRoute(conMgr.getMaxTotal());//(目前只有一个路由,因此让他等于最大值)
//另外设置http client的重试次数,默认是3次;当前是禁用掉(如果项目量不到,这个默认即可)
httpClient.setHttpRequestRetryHandler(new DefaultHttpRequestRetryHandler(0, false));
此处解释下MaxtTotal和DefaultMaxPerRoute的区别:
1、MaxtTotal是整个池子的大小;
2、DefaultMaxPerRoute是根据连接到的主机对MaxTotal的一个细分;比如:
MaxtTotal=400 DefaultMaxPerRoute=200
而我只连接到http://sishuok.com时,到这个主机的并发最多只有200;而不是400;
而我连接到http://sishuok.com 和 http://qq.com时,到每个主机的并发最多只有200;即加起来是400(但不能超过400);所以起作用的设置是DefaultMaxPerRoute。
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLDecoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import net.sf.json.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jd.framework.util.string.StringUtil;
public class BiTaskUtil {
private static final Logger logger = LoggerFactory.getLogger(BiTaskUtil.class);
private static final Map<String, String> configCache = new HashMap<String, String>();
private static final CloseableHttpClient httpClient;
private static final String CHARSET = "UTF-8";
private static ObjectMapper objectMapper = null;
static {
RequestConfig config = RequestConfig.custom().setConnectTimeout(60000).setSocketTimeout(15000).build();
httpClient = HttpClientBuilder.create().setDefaultRequestConfig(config).build();
String filePath = BiTaskUtil.class.getResource("/").getPath() + "biconfig/bi-config.properties";
Properties pps = new Properties();
try {
pps.load(new FileInputStream(filePath));
Enumeration<?> enum1 = pps.propertyNames();//得到配置文件的名字
while(enum1.hasMoreElements()) {
String strKey = (String) enum1.nextElement();
if (!StringUtil.isEmpty(strKey)) {
String strValue = pps.getProperty(strKey);
strValue = (strValue == null ? "" : strValue.trim());
configCache.put(strKey.trim(), strValue);
}
}
objectMapper = new ObjectMapper();
// 设置输入时忽略在JSON字符串中存在但Java对象实际没有的属性
objectMapper.disable(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES);
objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
} catch (IOException e) {
logger.error("bi接口API调用工具类在初始化时,解析配置文件[" + filePath + "]异常:", e);
throw new RuntimeException("bi接口API调用工具类在初始化时,解析配置文件[" + filePath + "]异常:", e);
}
}
/**
* 调用BI保存任务的接口 http:aa/api/task/save?time="1234765464"&data="{}"
* @return SaveTaskRetVo
* @throws Exception
*/
public static SaveTaskRetVo saveTask(String erp, String sqlNum, String sqlContent, String jobName, String targetFileName) throws Exception {
logger.warn("调用bi保存任务接口执行开始!");
SaveTaskRetVo str = null;
List<NameValuePair> paramsList = new ArrayList<NameValuePair>();
Date now = new Date();
paramsList.add(new BasicNameValuePair("time", String.valueOf(now.getTime())));
String url = configCache.get("saveUrl");
JSONObject jsonParam = new JSONObject();
jsonParam.put("jssResourceId", configCache.get("jssResourceId"));
if (!StringUtil.isEmpty(configCache.get("groupName")))
jsonParam.put("groupName", configCache.get("groupName"));
paramsList.add(new BasicNameValuePair("data", jsonParam.toString()));
// 执行保存API接口请求
String result = post(url, paramsList);
if (!StringUtil.isEmpty(result)) {
JSONObject jsonObject = JSONObject.fromObject(result);
str = new SaveTaskRetVo();
if(0 == jsonObject.getInt("code")){
JSONObject responseBody = (JSONObject) jsonObject.get("obj");
str = objectMapper.readValue(responseBody.toString(), SaveTaskRetVo.class);
} else {
logger.warn("调用bi保存任务接口失败 ! url:" + url + ",SQL_NUM=" + sqlNum);
}
str.setCode(jsonObject.getInt("code"));
str.setSuccess(jsonObject.getBoolean("success"));
str.setMessage(jsonObject.getString("message"));
} else {
logger.warn("调用bi保存任务接口返回数据为空 ! url:" + url + ",SQL_NUM=" + sqlNum);
str = new SaveTaskRetVo();
str.setCode(-1);
str.setSuccess(false);
str.setMessage("调用bi保存任务接口,返回值为空!");
}
logger.warn("调用bi保存任务接口执行结束!");
return str;
}
/**
* 调用BI查询任务的接口 http:aa/api/task/query?time="1234765464"&data="{}"
* @return
* @throws Exception
*/
public static QueryTaskRetVo queryTask( int id) throws Exception {
logger.warn("调用bi查询任务接口执行开始!");
QueryTaskRetVo str = null;
List<NameValuePair> paramsList = new ArrayList<NameValuePair>();
Date now = new Date();
paramsList.add(new BasicNameValuePair("time", String.valueOf(now.getTime())));
String url = configCache.get("queryUrl");
JSONObject jsonParam = new JSONObject();
jsonParam.put("id", id);
paramsList.add(new BasicNameValuePair("data", jsonParam.toString()));
// 执行查询API接口请求
String result = post(url, paramsList);
if (!StringUtil.isEmpty(result)) {
JSONObject jsonObject = JSONObject.fromObject(result);
str = new QueryTaskRetVo();
if(0 == jsonObject.getInt("code")){
JSONObject responseBody = (JSONObject) jsonObject.get("obj");
str = objectMapper.readValue(responseBody.toString(), QueryTaskRetVo.class);
} else {
logger.warn("调用bi查询任务接口失败 ! url:" + url + ",任务ID:" + id);
}
str.setCode(jsonObject.getInt("code"));
str.setSuccess(jsonObject.getBoolean("success"));
str.setMessage(jsonObject.getString("message"));
} else {
logger.warn("调用bi查询任务接口返回数据为空 ! url:" + url + ",任务ID:" + id);
str = new QueryTaskRetVo();
str.setCode(-1);
str.setSuccess(false);
str.setMessage("调用bi查询任务接口,返回值为空!");
}
logger.warn("调用bi查询任务接口执行结束!");
return str;
}
/**
* 执行post请求
* @param url 请求url
* @param paramsList 参数列表
* @return
*/
public static String post(String url, List<NameValuePair> paramsList) {
String result = null;
try {
HttpPost httpPost = new HttpPost(url);
//添加参数 , 设置编码
httpPost.setEntity(new UrlEncodedFormEntity(paramsList, CHARSET));
CloseableHttpResponse response = httpClient.execute(httpPost);
int statusCode = response.getStatusLine().getStatusCode();
if(statusCode != HttpStatus.SC_OK){
httpPost.abort();
logger.error("bi接口请求失败," + "url:" + url + ",返回状态信息:" + statusCode);
throw new RuntimeException("bi接口请求失败," + "url:" + url + ",返回状态信息:" + statusCode);
}
HttpEntity entity = response.getEntity();
if (entity != null){
result = EntityUtils.toString(entity, CHARSET);
}
EntityUtils.consume(entity);
response.close();
if (!StringUtil.isEmpty(result)) {
result = URLDecoder.decode(result, CHARSET);
}
} catch (Exception e) {
logger.error("bi接口请求失败," + "url:" + url + ",异常信息:" + e.getMessage());
throw new RuntimeException("bi接口请求失败," + "url:" + url + ",异常信息:" + e.getMessage());
}
return result;
}
/**
* 得到配置文件的属性值
* @param key
* @return
*/
public static String getString(String key) {
return configCache.get(key);
}
public static void main(String[] args) throws Exception {
/*System.out.println(configCache.get("bi.appId"));
JSONObject jsonParam = new JSONObject();
JSONObject obj = new JSONObject();
obj.put("id", 0001);
jsonParam.put("obj", obj.toString());
jsonParam.put("code", 0);
jsonParam.put("success", true);
jsonParam.put("message", "sssss");
JSONObject jsonObject = JSONObject.fromObject(jsonParam.toString());
SaveTaskRetVo str = new SaveTaskRetVo();
if(!StringUtil.isEmpty(jsonParam.toString()) && (0 == jsonObject.getInt("code"))){
JSONObject responseBody = (JSONObject) jsonObject.get("obj");
str=(SaveTaskRetVo) JSONObject.toBean(responseBody, SaveTaskRetVo.class);
str.setCode(jsonObject.getInt("code"));
str.setSuccess(jsonObject.getBoolean("success"));
str.setMessage(jsonObject.getString("message"));
} else {
logger.info("调用erp 接口 通过组织获取用户 返回失败 !response:" + " url:" + "ss" );
}
int jobType = 5;
jsonParam = new JSONObject();
jsonParam.put("jobName", "");
jsonParam.put("jobType", jobType);
jsonParam.put("marketCode",configCache.get("marketCode"));
jsonParam.put("dbName", configCache.get("dbName"));
jsonParam.put("content", "");
jsonParam.put("erp", "");
jsonParam.put("targetFileType", configCache.get("targetFileType"));
jsonParam.put("targetFileName", "");
jsonParam.put("spaceMark", configCache.get("spaceMark"));
jsonParam.put("targetType", Integer.valueOf(configCache.get("targetType")));
jsonParam.put("jssResourceId", configCache.get("jssResourceId"));
jsonParam.put("groupName", configCache.get("groupName"));
System.out.println("====" + jsonParam.toString());*/
QueryTaskRetVo obj = BiTaskUtil.queryTask(192903);
System.out.println(obj.getCode());
}
}
补充:每次新建一个httpclient时,会发生connection reset异常;原因如下(来源:http://blog.csdn.net/lcx46/article/details/38984335)mark一下。
HttpClient引起的TCP连接数高的问题分析
博客分类: Java网络编程
.
【问题现象】
系统上线后出现TCP连接数超过预期阀值,最高值达到8K左右,新上线代码中包含了一文件上传操作,使用的是apache的commons-httpclient包。
【问题分析】
1、先确认是否存在连接未关闭问题引起的。
观察发现,TCP连接数不是一直在增长,而是会有所下降。并且当业务低峰期TCP连接数TCP连接数会降到100左右,这说明TCP连接还是会关闭。
2、确定居高不下的TCP使用情况
使用"netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'"命令发现,处于ESTABLISHED状态的连接数最多,在查看了一下处于ESTABLISHED状态的目的IP,基本上都是文件服务器的IP,这说明还是跟新增加的文件上传操作有关。但是按照代码的逻辑来看,文件上传操作是多线程处理的,一个线程处理一个上传操作,线程池中一共有10个线程,照此分析正常的话应该有10个左右与文件服务器的链接,不应该出现几千个链接。因此怀疑是连接没有主动释放,而是等待连接超时才开始释放。
3、为什么会连接超时
查看了文件上传部分代码,主要代码如下:
Java代码
1.HttpClient client = new HttpClient();
2.MultipartPostMethod method = new MultipartPostMethod(config .getUploadInterface());
3.try{
4. client.executeMethod(method);
5.}catch (Exception e){
6. throw e;
7.}finally{
8. method.releaseConnection();
9.}
从代码里看是已经释放连接了,但是从结果上看没有释放连接,那就产生一个问题,这个地方真的能释放连接吗?我们在释放连接后面增加一行测试代码来看看:
Java代码
1.HttpConnection conn = client.getHttpConnectionManager().getConnection(client.getHostConfiguration());
2.System.out.println(conn.isOpen());
打印出的结果是true,也就是说虽然调用了releaseConnection,但是并没有释放连接!!
4、分析commons-httpclient相关代码
现在怀疑是我们使用的方式不对了,继续分析一下commons-https包中相关代码,首先看一下method.releaseConnection()的代码实现:
Java代码
1.public void releaseConnection() {
2. try {
3. if (this.responseStream != null) {
4. try {
5. // FYI - this may indirectly invoke responseBodyConsumed.
6. this.responseStream.close();
7. } catch (IOException ignore) {
8. }
9. }
10. } finally {
11. ensureConnectionRelease();
12. }
13. }
Java代码
1.private void ensureConnectionRelease() {
2. if (responseConnection != null) {
3. responseConnection.releaseConnection();
4. responseConnection = null;
5. }
6. }
经过debug发现responseStream为null,并且responseConnection也为null,这样改调用就没有实际意义。那么我们应该怎么来释放连接呢?
5、继续分析代码
我们发现在org.apache.commons.httpclient.HttpMethodDirector类的第208行已经在finally中释放连接了:
Java代码
1.finally {
2. if (this.conn != null) {
3. this.conn.setLocked(false);
4. }
5. // If the response has been fully processed, return the connection
6. // to the pool. Use this flag, rather than other tests (like
7. // responseStream == null), as subclasses, might reset the stream,
8. // for example, reading the entire response into a file and then
9. // setting the file as the stream.
10. if (
11. (releaseConnection || method.getResponseBodyAsStream() == null)
12. && this.conn != null
13. ) {
14. this.conn.releaseConnection();
15. }
16. }
Java代码
1.public void releaseConnection(HttpConnection conn) {
2. if (conn != httpConnection) {
3. throw new IllegalStateException("Unexpected release of an unknown connection.");
4. }
5.
6. finishLastResponse(httpConnection);
7.
8. inUse = false;
9.
10. // track the time the connection was made idle
11. idleStartTime = System.currentTimeMillis();
12. }
这个地方我们可以看到了所谓的释放连接并不是真的释放,还是return the connection to pool,照此分析,我们每个线程中new了一个HttpClient类,而每个HttpClient类中的链接都是没有close的,只是归还到httpClient中的pool而已,这些连接也必须等到连接超时才会被释放,由此可以分析出来连接数上涨的原因。那么我们应该怎么使用呢?按照代码的设计,看起来httpclient应该是单例的,但是在httpClient类的javadoc中并没有关于线程安全方面的说明,为此我们再回到官网上看相关文档,在文档(http://hc.apache.org/httpclient-3.x/performance.html)上我们看到如下的说明:
Java代码
1.HttpClient is fully thread-safe when used with a thread-safe connection manager such as MultiThreadedHttpConnectionManager
这说明在多线程环境下应该使用一个全局单例的HttpClient,并且使用MultiThreadHttpConnectionManager来管理Connection。
【相关结论】
1、HttpClient内部使用了池化技术,内部的链接是为了复用。在多线程条件下,可以使用一个全局的HttpClient实例,并且使用MultiThreadHttpConnectionManager来管理Connection。
2、使用开源软件之前一定要读读相关代码,看看官方推荐使用方式。
3、在解决此问题后,读了读httpclient中其他包中的代码,在读的时候发现对于理解http协议帮助很大,特别是文件上传,长连接,auth鉴权等。
4、相对于httpurlconnection ,httpclient更加丰富,也更加强大,其中apache有两个项目都是httpclient,一个是commonts包下的,这个是通用的,更专业的是org.apache.http.包下的,所以我一般用后者;对于httpclient4.5,连接池管理变更为如下
PoolingClientConnectionManager conMgr = new PoolingClientConnectionManager();
conMgr.setMaxTotal(200); //设置整个连接池最大连接数 根据自己的场景决定
//是路由的默认最大连接(该值默认为2),限制数量实际使用DefaultMaxPerRoute并非MaxTotal。
//设置过小无法支持大并发(ConnectionPoolTimeoutException: Timeout waiting for connection from pool),路由是对maxTotal的细分。
conMgr.setDefaultMaxPerRoute(conMgr.getMaxTotal());//(目前只有一个路由,因此让他等于最大值)
//另外设置http client的重试次数,默认是3次;当前是禁用掉(如果项目量不到,这个默认即可)
httpClient.setHttpRequestRetryHandler(new DefaultHttpRequestRetryHandler(0, false));
此处解释下MaxtTotal和DefaultMaxPerRoute的区别:
1、MaxtTotal是整个池子的大小;
2、DefaultMaxPerRoute是根据连接到的主机对MaxTotal的一个细分;比如:
MaxtTotal=400 DefaultMaxPerRoute=200
而我只连接到http://sishuok.com时,到这个主机的并发最多只有200;而不是400;
而我连接到http://sishuok.com 和 http://qq.com时,到每个主机的并发最多只有200;即加起来是400(但不能超过400);所以起作用的设置是DefaultMaxPerRoute。
发表评论
-
log4j实现特定功能的日志单独输出到指定的日志文件
2015-09-14 14:50 7914如何实现按需要,使某个功能的日志单独输出到指定的日志文件呢? ... -
mysql表(全是varchar)的一行允许的最大长度_建表测试脚本
2015-09-08 13:27 2596import java.sql.Connection; imp ... -
AOP实现自我调用的事物嵌套问题
2014-07-03 16:28 1242最近在开发中遇到了下面这个异常(错误代码【错误写法2】): j ... -
ligerGrid列表Demo
2014-07-01 16:46 1263项目中用到ligerGrid列表插件,第一次用,所以笔记一下。 ... -
spring RMI
2014-06-24 15:06 0http://aswater.iteye.com/blog/4 ... -
JSONOBject中保存的Date值
2014-06-23 17:44 1981JSONObject在装载Date类型数据时,会把一个Dat ... -
webservice简单运用_笔记
2014-06-09 11:26 6101、导入cxf的jar包; 2、在项目的web.xml中 ... -
java项目导出可执行jar包_笔记
2014-06-06 16:38 563最近做一个导出数据的工具,要在linux下执行,因为要导出 ...
相关推荐
在这个例程中,我们将深入探讨如何利用HttpClient来调用一个天气预报接口,并解析返回的JSON数据。以下是一些关键知识点: 1. **HttpClient库**:HttpClient是Apache提供的一个开源库,它允许开发者构建HTTP客户端...
ETL KETTLE 中利用Httpclient 调用webservice接口获取XML数据,并解析XML 数据。 完整的KTR实例
总的来说,使用Java的HttpClient进行HTTPS接口调用涉及到配置SSL上下文、初始化HttpClient、创建请求对象以及处理响应。了解这些知识点对于开发安全的、能够与HTTPS服务进行通信的Java应用程序至关重要。通过实践和...
"JAVA利用HttpClient进行POST请求(HTTPS)" JAVA HttpClient是Apache软件基金会提供的一个开源实现HTTP客户端的Java库,能够帮助开发者轻松地与HTTP服务器进行交互。在实际项目中,我们经常需要使用HttpClient来发送...
在IT行业中,HttpClient和WCF...综上所述,HttpClient调用WCF涉及的服务创建、配置、客户端请求、响应处理以及数据转换等多个步骤,是跨平台通信的一个常见场景。理解这些知识点对于实现Java调用.NET服务至关重要。
总之,通过HttpClient调用Web服务是一个涉及网络通信和HTTP协议理解的过程。在实际开发中,了解HttpClient的使用方法和最佳实践,可以帮助我们构建稳定、高效的Web服务客户端。结合Maven构建工具,可以轻松管理项目...
PB调用HTTP API接口是PowerBuilder(PB)应用程序与远程服务交互的一种常见方式,这使得PB应用能够利用Web服务或RESTful API获取和传递数据。在这个过程中,PB不仅需要发送HTTP请求,还需要处理响应,尤其是当响应...
### Java HttpClient 发送GET请求和带有表单参数的POST请求详解 ...通过上述示例和解释,你应该能够理解和掌握如何使用Java HttpClient库来发送GET和POST请求,这对于开发Web应用程序或与API接口交互至关重要。
而"httpclient-demo"项目则是一个客户端应用,它的任务是利用Apache HttpClient库来构建和发送POST请求到"post-demo"项目提供的接口。HttpClient库提供了丰富的API,允许开发者灵活地设置请求头、主体内容、超时等...
接下来,我们需要编写一个类来利用HttpClient发送POST请求。这个类可以接收参数,如URL、请求头和请求体,然后使用HttpClient发送POST请求。以下是一个简单的示例: ```java import org.apache.http.HttpEntity; ...
本教程将深入探讨如何利用HttpClient类在WPF应用中异步调用Web API,确保不阻塞主线程,提供流畅的用户体验。 HttpClient是一个强大的工具,用于发送HTTP请求并接收响应。在WPF应用中,我们可以使用异步编程技术,...
"JAVA利用HttpClient进行HTTPS接口调用的方法" 知识点1:HttpClient的基本概念 HttpClient是一个开放源代码的Java库,用于提供高效、灵活和可扩展的HTTP客户端解决方案。HttpClient支持HTTP/1.1和HTTP/2.0协议,...
本文将详细介绍如何使用Java语言来调用HTTP REST接口,并提供具体的POST与GET请求的实现示例。 #### 二、HTTP REST简介 REST (Representational State Transfer) 是一种设计网络应用程序的架构风格,其核心原则之...
通过HTTPClient实现HTTP请求,利用RESTful接口定义清晰的操作方式,可以构建高效、灵活的文件传输方案。同时,理解并遵循良好的编程实践,如编写详细注释,对于提高代码质量和团队效率也是十分重要的。
本篇文章将详细介绍如何在Spring MVC应用中利用HttpClient来调用外部服务。 首先,让我们理解Spring MVC的基本工作流程。Spring MVC接收HTTP请求,通过DispatcherServlet进行分发,然后由Controller层处理业务逻辑...
`RestClient`可能是一个更高级别的抽象,专门针对RESTful API设计,利用HttpClient来实现对REST服务的调用。 HttpClient库支持各种HTTP方法,如GET、POST、PUT、DELETE等,同时也支持HTTP/1.1和HTTP/2协议。在`...
HttpClient还支持异步操作,通过`Future<HttpResponse>`接口可以实现非阻塞调用,提高系统性能。 9. **安全性考虑**: 如果Servlet需要HTTPS(SSL/TLS)加密,HttpClient需要配置SSLContext和TrustStrategy,处理...
总之,"REST调用有参数接口"涉及到利用HTTP方法和适当的参数传递机制与服务器进行通信。`HttpRestClientRequest.java`可能是这样一个工具类,简化了这个过程,使得开发者可以更方便地在Java应用中集成RESTful服务。...
POST请求常用于向服务器发送数据,如表单提交或API调用。 首先,理解HTTP协议的基础是必要的。HTTP(超文本传输协议)是互联网上应用最为广泛的一种网络协议,用于从万维网服务器传输超文本到本地浏览器。POST是...