`

利用httpclient调用http请求接口

 
阅读更多
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。
分享到:
评论

相关推荐

    一个使用HttpClient调用天气预报接口的例程

    在这个例程中,我们将深入探讨如何利用HttpClient来调用一个天气预报接口,并解析返回的JSON数据。以下是一些关键知识点: 1. **HttpClient库**:HttpClient是Apache提供的一个开源库,它允许开发者构建HTTP客户端...

    ETL KETTLE 中利用Httpclient 调用webservice接口获取XML数据,并解析XML 数据

    ETL KETTLE 中利用Httpclient 调用webservice接口获取XML数据,并解析XML 数据。 完整的KTR实例

    JAVA利用HttpClient进行HTTPS接口调用

    总的来说,使用Java的HttpClient进行HTTPS接口调用涉及到配置SSL上下文、初始化HttpClient、创建请求对象以及处理响应。了解这些知识点对于开发安全的、能够与HTTPS服务进行通信的Java应用程序至关重要。通过实践和...

    (完整版)JAVA利用HttpClient进行POST请求(HTTPS).doc

    "JAVA利用HttpClient进行POST请求(HTTPS)" JAVA HttpClient是Apache软件基金会提供的一个开源实现HTTP客户端的Java库,能够帮助开发者轻松地与HTTP服务器进行交互。在实际项目中,我们经常需要使用HttpClient来发送...

    httpClient调用wcf

    在IT行业中,HttpClient和WCF...综上所述,HttpClient调用WCF涉及的服务创建、配置、客户端请求、响应处理以及数据转换等多个步骤,是跨平台通信的一个常见场景。理解这些知识点对于实现Java调用.NET服务至关重要。

    webservice调用实例,通过HttpClient调用

    总之,通过HttpClient调用Web服务是一个涉及网络通信和HTTP协议理解的过程。在实际开发中,了解HttpClient的使用方法和最佳实践,可以帮助我们构建稳定、高效的Web服务客户端。结合Maven构建工具,可以轻松管理项目...

    httpclient发送post请求

    而"httpclient-demo"项目则是一个客户端应用,它的任务是利用Apache HttpClient库来构建和发送POST请求到"post-demo"项目提供的接口。HttpClient库提供了丰富的API,允许开发者灵活地设置请求头、主体内容、超时等...

    JAVA利用HttpClient进行POST请求(HTTPS)实例

    接下来,我们需要编写一个类来利用HttpClient发送POST请求。这个类可以接收参数,如URL、请求头和请求体,然后使用HttpClient发送POST请求。以下是一个简单的示例: ```java import org.apache.http.HttpEntity; ...

    java HttpClient 发送GET请求和带有表单参数的POST请求教程例子

    ### Java HttpClient 发送GET请求和带有表单参数的POST请求详解 ...通过上述示例和解释,你应该能够理解和掌握如何使用Java HttpClient库来发送GET和POST请求,这对于开发Web应用程序或与API接口交互至关重要。

    JAVA利用HttpClient进行HTTPS接口调用的方法

    "JAVA利用HttpClient进行HTTPS接口调用的方法" 知识点1:HttpClient的基本概念 HttpClient是一个开放源代码的Java库,用于提供高效、灵活和可扩展的HTTP客户端解决方案。HttpClient支持HTTP/1.1和HTTP/2.0协议,...

    Java 调用Http Rest接口 例子说明

    本文将详细介绍如何使用Java语言来调用HTTP REST接口,并提供具体的POST与GET请求的实现示例。 #### 二、HTTP REST简介 REST (Representational State Transfer) 是一种设计网络应用程序的架构风格,其核心原则之...

    PB调用http api接口 PB解析json

    PB调用HTTP API接口是PowerBuilder(PB)应用程序与远程服务交互的一种常见方式,这使得PB应用能够利用Web服务或RESTful API获取和传递数据。在这个过程中,PB不仅需要发送HTTP请求,还需要处理响应,尤其是当响应...

    如何在WPF应用程序中通过HttpClient调用Web API

    本教程将深入探讨如何利用HttpClient类在WPF应用中异步调用Web API,确保不阻塞主线程,提供流畅的用户体验。 HttpClient是一个强大的工具,用于发送HTTP请求并接收响应。在WPF应用中,我们可以使用异步编程技术,...

    httpclient和RestfuL风格上传下载文件

    通过HTTPClient实现HTTP请求,利用RESTful接口定义清晰的操作方式,可以构建高效、灵活的文件传输方案。同时,理解并遵循良好的编程实践,如编写详细注释,对于提高代码质量和团队效率也是十分重要的。

    springmvc实现httpclient调用服务

    本篇文章将详细介绍如何在Spring MVC应用中利用HttpClient来调用外部服务。 首先,让我们理解Spring MVC的基本工作流程。Spring MVC接收HTTP请求,通过DispatcherServlet进行分发,然后由Controller层处理业务逻辑...

    httpClient请求

    `RestClient`可能是一个更高级别的抽象,专门针对RESTful API设计,利用HttpClient来实现对REST服务的调用。 HttpClient库支持各种HTTP方法,如GET、POST、PUT、DELETE等,同时也支持HTTP/1.1和HTTP/2协议。在`...

    rest 调用有参数接口

    总之,"REST调用有参数接口"涉及到利用HTTP方法和适当的参数传递机制与服务器进行通信。`HttpRestClientRequest.java`可能是这样一个工具类,简化了这个过程,使得开发者可以更方便地在Java应用中集成RESTful服务。...

    使用httpclient访问servlet

    HttpClient还支持异步操作,通过`Future&lt;HttpResponse&gt;`接口可以实现非阻塞调用,提高系统性能。 9. **安全性考虑**: 如果Servlet需要HTTPS(SSL/TLS)加密,HttpClient需要配置SSLContext和TrustStrategy,处理...

    C#利用post访问接口

    POST请求常用于向服务器发送数据,如表单提交或API调用。 首先,理解HTTP协议的基础是必要的。HTTP(超文本传输协议)是互联网上应用最为广泛的一种网络协议,用于从万维网服务器传输超文本到本地浏览器。POST是...

Global site tag (gtag.js) - Google Analytics