httpclient 4.X 异步 async util

自己利用httpClient4.x 写了一个异步 工具

异步请求适用于与用户无关的批量操作,比如发短信,或者请求第三方等。

依赖:


 <httpclient.version>4.5</httpclient.version>
        <asyncclient.version>4.1.2</asyncclient.version>
        <httpmime.version>4.5</httpmime.version>
        <httpcore.version>4.4.1</httpcore.version>

<dependency>
                <groupId>org.apache.httpcomponents</groupId>
                <artifactId>httpmime</artifactId>
                <version>${httpmime.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.httpcomponents</groupId>
                <artifactId>httpclient</artifactId>
                <version>${httpclient.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.httpcomponents</groupId>
                <artifactId>httpasyncclient</artifactId>
                <version>${asyncclient.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.httpcomponents</groupId>
                <artifactId>httpcore</artifactId>
                <version>${httpcore.version}</version>
            </dependency>

util:


/**
 *
 */
package com.xxx.xxx.common.util;/**
 * Created by lxn on 2016/8/16.
 */

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.ReentrantLock;

import javax.net.ssl.SSLContext;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
import org.apache.http.util.EntityUtils;

/**
 * 异步http工具类,
 * 注意事项:
 * 在全部回调完成后,请执行close(),否则线程不会关闭
 *
 * @author lxn
 * @version Id: HttpAsyncUtil.java, v 0.1 2016/8/16 16:08 lxn Exp $$
 */
public class HttpAsyncUtil {

    private final static int SOCKET_TIMEOUT = 10000;//单位ms 从服务器读取数据的timeout
    private final static int CONNECTION_TIMEOUT = 20000;//单位ms 和服务器建立连接的timeout
    private final static int CONNECTION_REQUEST_TIMEOUT = 3000;//单位ms 从连接池获取连接的timeout
    private final static boolean KEEP_ALIVE=false;
    private RequestConfig requestConfig = RequestConfig.custom()
            .setSocketTimeout(SOCKET_TIMEOUT)
            .setConnectTimeout(CONNECTION_TIMEOUT)
            .setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT)
            .build();

    private CloseableHttpAsyncClient httpclient;
    private CloseableHttpAsyncClient httpsclient;
    private static HttpAsyncUtil instance;


    private static  ReentrantLock startLock = new ReentrantLock();


    private void httpStart(){
        if(httpclient==null){
            httpclient= HttpAsyncClients.createDefault();
        }
        if(httpclient.isRunning()){
            return;
        }
        httpclient.start();
    }

    public void close(){
        try {
            if(httpclient!=null&&httpclient.isRunning()){
                httpclient.close();
            }

            if(httpsclient!=null&&httpsclient.isRunning()){
                httpsclient.close();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private void httpsStart(){
        if(httpsclient == null) {
            try {
                SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                    @Override
                    public boolean isTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) throws java.security.cert.CertificateException {
                        return true;
                    }
                }).build();
                httpsclient = HttpAsyncClients.custom().setSSLContext(sslContext).build();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if(httpsclient.isRunning()){
            return;
        }
        httpsclient.start();
    }


    public static HttpAsyncUtil getInstance(){
        if(instance==null){
            instance = new HttpAsyncUtil();
        }
        return  instance;
    }


    /**
     * 发送 post请求
     * @param httpUrl 地址
     */
    public void sendHttpPost(String httpUrl,FutureCallback<HttpResponse> callback) throws IOException, ClientProtocolException {
        HttpPost httpPost = new HttpPost(httpUrl);// 创建httpPost
        sendHttpPost(httpPost,callback);
    }

    /**
     * 发送 post请求
     * @param httpUrl 地址
     * @param params  请求参数
     * @param charset 字符编码
     * @param contentType  contentType跟请求参数要对应如:params是json根式,contentType为application/json
     */
    public void sendHttpPost(String httpUrl, String params,String charset,String contentType,FutureCallback<HttpResponse> callback)throws IOException, ClientProtocolException {
        HttpPost httpPost = new HttpPost(httpUrl);// 创建httpPost
        //设置参数
        StringEntity stringEntity = new StringEntity(params, charset);
        stringEntity.setContentType(contentType);
        httpPost.setEntity(stringEntity);
        sendHttpPost(httpPost,callback);
    }

    /**
     * 发送 post请求
     * @param httpUrl 地址
     * @param maps    参数
     */
    public void sendHttpPost(String httpUrl, Map<String, String> maps,FutureCallback<HttpResponse> callback) throws UnsupportedEncodingException, IOException, ClientProtocolException{
        HttpPost httpPost = new HttpPost(httpUrl);// 创建httpPost
        // 创建参数队列
        List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
        for (String key : maps.keySet()) {
            nameValuePairs.add(new BasicNameValuePair(key, maps.get(key)));
        }
        httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, "UTF-8"));
        sendHttpPost(httpPost,callback);
    }


    /**
     * 发送 post请求(带文件)
     * @param httpUrl   地址
     * @param maps      参数
     * @param fileLists 附件
     */
    public void sendHttpPost(String httpUrl, Map<String, String> maps, List<File> fileLists,FutureCallback<HttpResponse> callback)throws IOException, ClientProtocolException{
        HttpPost httpPost = new HttpPost(httpUrl);// 创建httpPost
        MultipartEntityBuilder meBuilder = MultipartEntityBuilder.create();
        for (String key : maps.keySet()) {
            meBuilder.addPart(key, new StringBody(maps.get(key), ContentType.TEXT_PLAIN));
        }
        for (File file : fileLists) {
            FileBody fileBody = new FileBody(file);
            meBuilder.addPart("files", fileBody);
        }
        HttpEntity reqEntity = meBuilder.build();
        httpPost.setEntity(reqEntity);
        sendHttpPost(httpPost,callback);
    }


    /**
     * 发送 get请求
     * @param httpUrl
     */
    public void sendHttpGet(String httpUrl,FutureCallback<HttpResponse> callback) throws IOException, ClientProtocolException{
        HttpGet httpGet = new HttpGet(httpUrl);// 创建get请求
        sendHttpGet(httpGet,callback);
    }
    public void sendHttpsGet(String url,FutureCallback<HttpResponse> callback){
        HttpGet httpGet =new HttpGet(url);
        sendHttpsGet(httpGet,callback);
    }



    /**
     * 发送 post请求
     * @param httpUrl 地址
     */
    public void sendHttpsPost(String httpUrl,FutureCallback<HttpResponse> callback) throws IOException, ClientProtocolException {
        HttpPost httpPost = new HttpPost(httpUrl);// 创建httpPost
        sendHttpsPost(httpPost,callback);
    }

    /**
     * 发送 post请求
     * @param httpUrl 地址
     * @param params  请求参数
     * @param charset 字符编码
     * @param contentType  contentType跟请求参数要对应如:params是json根式,contentType为application/json
     */
    public void sendHttpsPost(String httpUrl, String params,String charset,String contentType,FutureCallback<HttpResponse> callback)throws IOException, ClientProtocolException {
        HttpPost httpPost = new HttpPost(httpUrl);// 创建httpPost
        //设置参数
        StringEntity stringEntity = new StringEntity(params, charset);
        stringEntity.setContentType(contentType);
        httpPost.setEntity(stringEntity);
        sendHttpsPost(httpPost,callback);
    }

    /**
     * 发送 post请求
     * @param httpUrl 地址
     * @param maps    参数
     */
    public void sendHttpsPost(String httpUrl, Map<String, String> maps,FutureCallback<HttpResponse> callback) throws UnsupportedEncodingException, IOException, ClientProtocolException{
        HttpPost httpPost = new HttpPost(httpUrl);// 创建httpPost
        // 创建参数队列
        List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
        for (String key : maps.keySet()) {
            nameValuePairs.add(new BasicNameValuePair(key, maps.get(key)));
        }
        httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, "UTF-8"));
        sendHttpsPost(httpPost,callback);
    }


    /**
     * 发送 post请求(带文件)
     * @param httpUrl   地址
     * @param maps      参数
     * @param fileLists 附件
     */
    public void sendHttpsPost(String httpUrl, Map<String, String> maps, List<File> fileLists,FutureCallback<HttpResponse> callback)throws IOException, ClientProtocolException{
        HttpPost httpPost = new HttpPost(httpUrl);// 创建httpPost
        MultipartEntityBuilder meBuilder = MultipartEntityBuilder.create();
        for (String key : maps.keySet()) {
            meBuilder.addPart(key, new StringBody(maps.get(key), ContentType.TEXT_PLAIN));
        }
        for (File file : fileLists) {
            FileBody fileBody = new FileBody(file);
            meBuilder.addPart("files", fileBody);
        }
        HttpEntity reqEntity = meBuilder.build();
        httpPost.setEntity(reqEntity);
        sendHttpsPost(httpPost,callback);
    }



    public void sendHttpPost(HttpPost httpPost,FutureCallback<HttpResponse> callback) {
        httpPost.setConfig(requestConfig);
        httpStart();
        httpclient.execute(httpPost, callback);
    }
    public void sendHttpGet(HttpGet httpGet,FutureCallback<HttpResponse> callback){
        httpGet.setConfig(requestConfig);
        httpStart();
        httpclient.execute(httpGet, callback);
    }

    public void sendHttpsGet(HttpGet httpGet,FutureCallback<HttpResponse> callback){
        httpGet.setConfig(requestConfig);
        httpsStart();
        httpsclient.execute(httpGet,callback);
    }
    public void sendHttpsPost(HttpPost httpPost,FutureCallback<HttpResponse> callback){
        httpPost.setConfig(requestConfig);
        httpsStart();
        httpsclient.execute(httpPost,callback);
    }









    public static void main(String[] argv)throws Exception {
        //利用计数器阻塞主线程,在全部回调结束后再close
        final CountDownLatch countDownLatch = new CountDownLatch(4);

        //测试回调
        FutureCallback<HttpResponse> callback =  new FutureCallback<HttpResponse>() {
            public void completed(final HttpResponse response) {
                //计数器减
                countDownLatch.countDown();
                System.out.println(" callback thread id is : " + Thread.currentThread().getId());
                try {
                    String content = EntityUtils.toString(response.getEntity(), "UTF-8");
                    System.out.println(" response content is : " + content);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            public void failed(final Exception ex) {
                //计数器减
                countDownLatch.countDown();
                System.out.println(" callback thread id is : " + Thread.currentThread().getId());
            }
            public void cancelled() {
                //计数器减
                countDownLatch.countDown();
                System.out.println(" callback thread id is : " + Thread.currentThread().getId());
            }
        };

        final HttpGet httpGet = new HttpGet("http://www.sohu.com");
        final HttpGet httpsGet = new HttpGet("https://www.hao123.com");
        final HttpPost httpPost = new HttpPost("http://php.weather.sina.com.cn/xml.php?city=%B1%B1%BE%A9&password=DJOYnieT8234jlsK&day=0");
        final HttpPost httpsPost = new HttpPost("https://www.hao123.com");


        HttpAsyncUtil util =  HttpAsyncUtil.getInstance();



        util.sendHttpGet(httpGet,callback);
        util.sendHttpPost(httpPost,callback);

        util.sendHttpsGet(httpsGet,callback);
        util.sendHttpsPost(httpsPost,callback);

        //阻塞主线程
        countDownLatch.await();
        util.close();
    }

}


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一个工作时写的工具包。实现了Java版的Promise 和 HttpClientHttpClient 支持同步和异步两种方式,也支持多种不同实现。目前有Netty 和 Apache Compoenet两种实现。本次上传移除了Netty实现。主要解决生产环境中同步httpclient造成的IO阻塞问题。同步http请求将导致 tomcat 的业务线程被阻塞。一旦某接口网络出现问题,可能会阻塞tomcat业务线程,从而无法处理正常业务。很多公司使用另开线程池的方式进行异步调用来解决tomcat线程阻塞问题。但由于本系统中接口网络太不稳定,使用线程池也将导致线程池中的线程不断加大,不管使用怎样的线程池策略,最终要么线程池线程全部挂起,要么部分任务被延迟执行,要么丢失部分任务。这在我们的系统中仍然不能接受。因此才有了这个组件的开发。该组件是单线程非阻塞式的,类似于JS中的ajax请求。都使用单线程异步回调的方式。目前该组件已经初步测试通过。如果大家也需要这样的组件,可以下载尝试一下。所有关键注释都已经写了,如有不明白可以发送邮件 [email protected] 代码分为3个maven模块。 commons-ext : 实现Promise commons-tools: 实现 异步httpclient commons-parent:父模块 测试代码在 commons-tools/src/test/java/HttpTest.java 中. 要求至少Java 8 版本。 注释已经写好。这里贴出异步的http部分测试代码。 /** * 异步方法的Fluent写法 */ public void testAsyncHttpFluent() { SimpleRequest.Get("http://www.baidu.com") .header("h1", "hv1") .header("h2", "hv2") .parameter("p1", "pv1") .parameter("p2", "pv2") .chartUTF8() .build() .asyncExecute() .then(SimpleAsyncHttpClient::asString) .then(html -> { System.out.println(html); }) .catching(Throwable::printStackTrace);//如果有异常,则打印异常 }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值