异步Http请求封装工具类AsyncHttpClientUtil

这里有两种封装,分别是:

    1:建立异步Http请求客户端之后,所有http请求都复用该HttpAsyncClient,内部只对应一个Reactor I/O线程。

请求的回调函数中不关闭HttpAsyncClient。

    2:每次请求都建立一个新的HttpAsyncClient,请求回调结束后关闭HttpAsyncClient,释放资源。

在我的项目中,我选择第一种:复用单例HttpAsyncClient,因为一个Reactor I/O线程其实可以

同时处理成千上万个连接请求,完全满足我的项目需要。

 

一、单例HttpAsyncClient

package com.peas.pay.util;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
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.client.methods.HttpUriRequest;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.impl.nio.reactor.IOReactorConfig;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;

import lombok.extern.log4j.Log4j;

/**
 * Created by xiekunliang on 2018/3/24. 异步Http请求封装工具类
 */
@Log4j
public class AsyncHttpClientUtil
{
    private static CloseableHttpAsyncClient httpClient;
    private static volatile boolean isClientStart;

    /**
     * 创建CloseableHttpAsyncClient
     * @return
     */
    private static CloseableHttpAsyncClient createCustomAsyncClient()
    {
        Preconditions.checkState(!isClientStart, "客户端HttpAsyncClient已经建立过了");
        IOReactorConfig ioReactorConfig = IOReactorConfig.custom()
                .setIoThreadCount(Runtime.getRuntime().availableProcessors())
                .setConnectTimeout(60000)
                .setSoTimeout(60000)
                .build();
        // 设置超时时间 毫秒为单位
        RequestConfig requestConfig = RequestConfig
                .copy(RequestConfig.DEFAULT)
                .setConnectTimeout(60000)
                .build();
        return HttpAsyncClients
                .custom()
                .setDefaultIOReactorConfig(ioReactorConfig)
                .setDefaultRequestConfig(requestConfig)
                .build();

    }


    public static void startHttpClient()
    {
        httpClient = createCustomAsyncClient();
        httpClient.start();
        isClientStart = true;
    }

    public static void closeHttpClient()
    {
        try {
            httpClient.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        isClientStart = false;
    }

    public static void http(String method, 
                            String url, 
                            Object parameter, 
                            StringFutureCallback callback)
    {
        Preconditions.checkNotNull(method);
        if ("GET".equals(method))
        {
            get(url, callback);
        }
        else if ("POST".equals(method))
        {
            post(url, parameter, callback);
        }
    }
    

    public static void get(String url, StringFutureCallback callback)
    {
        Preconditions.checkArgument(isClientStart, "还没有建立Http Client");
        HttpUriRequest request = new HttpGet(url);
        httpClient.execute(request, new DefaultFutureCallback(callback));
    }

    public static void post(String url, Object parameter, StringFutureCallback callback)
    {
        Preconditions.checkArgument(isClientStart, "还没有建立Http Client");
        HttpPost httpPost = new HttpPost(url);
        if (parameter != null)
        {
            List<BasicNameValuePair> pairs = Lists.newArrayList();
            UrlEncodedFormEntity entity = null;
            try {
                if (parameter instanceof HashMap)
                {
                    Map<String, String> parameters = (Map<String, String>) parameter;
                    parameters.forEach((k, v) -> pairs.add(new BasicNameValuePair(k,v)));
                    entity = new UrlEncodedFormEntity(pairs, "UTF-8");
                }
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            httpPost.setEntity(entity);
        }
        httpClient.execute(httpPost, new DefaultFutureCallback(callback));
    }


    /**
     * 字符串类型结果回调
     */
    public interface StringFutureCallback
    {
        void success(String content);
    }


    public static class DefaultFutureCallback implements FutureCallback<HttpResponse>
    {
        private StringFutureCallback callback;
        public DefaultFutureCallback(StringFutureCallback callback)
        {
            this.callback = callback;
        }
        @Override
        public void completed(HttpResponse httpResponse)
        {
            HttpEntity entity = httpResponse.getEntity();
            String content = "";
            try
            {
                content = EntityUtils.toString(entity, "UTF-8");
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
            callback.success(content);
        }

        @Override
        public void failed(Exception e)
        {
            e.printStackTrace();
        }

        @Override
        public void cancelled()
        {
            log.debug("http request cancelled");
        }
    }
}

 

二、每次都新建立一个HttpAsyncClient

package com.peas.pay.util;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
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.client.methods.HttpUriRequest;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.impl.nio.reactor.IOReactorConfig;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;

/**
 * Created by xiekunliang on 2018/3/24. 异步Http请求封装工具类
 */
public class AsyncHttpClientUtil
{

    public interface StringFutureCallback
    {
        void success(String content);
    }

    public static void http(String method, String url, Object parameter, StringFutureCallback callback)
    {
        Preconditions.checkNotNull(method);
        if ("GET".equals(method))
        {
            get(url, callback);
        }
        else if ("POST".equals(method))
        {
            post(url, parameter, callback);
        }
    }


    private static CloseableHttpAsyncClient createCustomAsyncClient() {

        IOReactorConfig ioReactorConfig = IOReactorConfig.custom()
                .setIoThreadCount(Runtime.getRuntime().availableProcessors())
                .setConnectTimeout(60000)
                .setSoTimeout(60000)
                .build();
        // 设置超时时间 毫秒为单位
        RequestConfig requestConfig = RequestConfig.copy(RequestConfig.DEFAULT).setConnectTimeout(60000).build();

        return HttpAsyncClients.custom().setDefaultIOReactorConfig(ioReactorConfig).setDefaultRequestConfig(requestConfig).build();
    }


    public static void get(String url, StringFutureCallback callback)
    {
        CloseableHttpAsyncClient asyncClient = createCustomAsyncClient();
        asyncClient.start();
        HttpUriRequest request = new HttpGet(url);
        asyncClient.execute(request, new DefaultFutureCallback(asyncClient, callback));
    }

    public static void post(String url, Object parameter, StringFutureCallback callback)
    {
        CloseableHttpAsyncClient asyncClient = createCustomAsyncClient();
        asyncClient.start();
        HttpPost httpPost = new HttpPost(url);
        if (parameter != null)
        {
            List<BasicNameValuePair> pairs = Lists.newArrayList();
            UrlEncodedFormEntity entity = null;
            try {
                if (parameter instanceof HashMap)
                {
                    Map<String, String> parameters = (Map<String, String>) parameter;
                    parameters.forEach((k, v) -> pairs.add(new BasicNameValuePair(k,v)));
                    entity = new UrlEncodedFormEntity(pairs, "UTF-8");
                }
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            httpPost.setEntity(entity);
        }
        asyncClient.execute(httpPost, new DefaultFutureCallback(asyncClient, callback));
    }


    public static class DefaultFutureCallback implements FutureCallback<HttpResponse>
    {
        private CloseableHttpAsyncClient client;

        private StringFutureCallback callback;

        public DefaultFutureCallback(CloseableHttpAsyncClient client, StringFutureCallback callback)
        {
            this.client = client;
            this.callback = callback;
        }

        @Override
        public void completed(HttpResponse httpResponse)
        {
            HttpEntity entity = httpResponse.getEntity();
            String content = "";
            try
            {
                content = EntityUtils.toString(entity, "UTF-8");
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
            try
            {
                callback.success(content);
            }
            finally
            {
                try
                {
                    client.close();
                }
                catch (IOException e1)
                {
                    e1.printStackTrace();
                }
            }
        }

        @Override
        public void failed(Exception e)
        {
            e.printStackTrace();
            Thread t1 = new Thread(() -> {
                try {
                    // 发生连接超时等异常时,failed回调执行到client.close()方法;
                    // 但是可能会在this.reactorThread.join()这里无限期阻塞。
                    // 可能哪个地方形成了死锁,所以这里用一个超时中断,结束Reactor I/O线程的死锁。
                    client.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            });
            t1.start();
            try {
                t1.join(30000);
                Thread.currentThread().interrupt();
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
        }

        @Override
        public void cancelled()
        {
            try
            {
                client.close();
            }
            catch (IOException e1)
            {
                e1.printStackTrace();
            }
        }
    }
}

第二种封装代码有可能写的不是很完善,主要是在failed()回调方法中,手动调用client.close()方法后,

经过测试:Reactor I/O线程无限期阻塞,可能是哪里造成了死锁。本代码中为了使线程资源得到释放,采用中断的方式处理。

可能不是很妥当,但是还没有想到其他更好的办法。

希望有经验的大神指正该怎么做。

pom文件主要有:

<dependencies>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>19.0</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.6</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpasyncclient</artifactId>
            <version>4.1.3</version>
        </dependency>
    </dependencies>

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值