• 通过代理的方式实现对httpClient的监控,超时回调


    通过代理的方式实现对httpClient的监控,超时回调

    实现的功能

    1.记录请求时间
    2.记录请求内容
    3.简化回调
    4.重时重试功能

    通过静态代理的方式实现

    代理类

    /**
     * 1.记录请求时间
     * 2.记录请求内容
     * 3.简化回调
     */
    @Slf4j
    public class RetryFutureProxy implements FutureCallback<HttpResponse>, RetryFutureCallBack {
        protected static final Logger requestLogger = LoggerFactory.getLogger("requestLogger");
        /**
         * 已重试次数
         */
        int retryUse = 0;
        /**
         * 总重试次数
         */
        int retryCount;
        /**
         * 请求
         */
        protected HttpUriRequest request;
        /**
         * 日志体
         */
        private RequestLogEntity requestLogEntity;
    
        /**
         * 当前重试
         */
        private RequestLogEntity nowRetryEntity;
    
        //回调接口
        protected RetryFutureCallBack callBack;
    
        //
        public RetryFutureProxy(HttpUriRequest request){
            this(request,new EmptyRetryFutureCallBack());
        }
        /**
         * 默认为不重试的请求
         *
         * @param request 请求体
         */
        public RetryFutureProxy(HttpUriRequest request, RetryFutureCallBack callBack) {
            this(-1, request,callBack);
        }
    
        /**
         * @param count   重试次数
         * @param request 请求
         */
        public RetryFutureProxy(int count, HttpUriRequest request, RetryFutureCallBack callBack) {
            this.callBack = callBack;
            this.retryCount = count;
            this.request = request;
            this.requestLogEntity = new RequestLogEntity();
            this.requestLogEntity.setStartTime(new Date());
            this.requestLogEntity.generateUrlInfo(request.getURI().toString());
            this.requestLogEntity.setStatus(RequestEnum.FAIL);
            this.requestLogEntity.setRetryCount(0);
            if (retryCount > 0) {
                this.requestLogEntity.setRetryList(new ArrayList<>());
            }
        }
    
    
        //记录请求结果和打印日志
        private void logEndAndPrint(String result) {
            requestLogEntity.setUseTime(requestLogEntity.getEndTime().getTime() - requestLogEntity.getStartTime().getTime());
            requestLogEntity.setResult(result);
            log.info("{}请求总用时:{}ms", requestLogEntity.getUrl(), requestLogEntity.getUseTime());
            requestLogger.info(JacksonUtil.toJson(requestLogEntity));
        }
    
    
    
        //记录成功时的日志信息
        public void doSuccess(String result) {
            this.requestLogEntity.setEndTime(new Date());
            this.requestLogEntity.setStatus(RequestEnum.SUCCESS);
            try {
                //自定义回调
                success(result);
    			//处理请求
                handleRetryEntity(RequestEnum.SUCCESS);
            } catch (Exception e) {
                this.requestLogEntity.setStatus(RequestEnum.FAIL);
                handleRetryEntity(RequestEnum.FAIL);
                throw e;
            }finally {
                logEndAndPrint(result);
            }
        }
    
    
    	//记录失败时的信息
        public void doFail(String result) {
            this.requestLogEntity.setEndTime(new Date());
            handleRetryEntity(RequestEnum.FAIL);
            this.requestLogEntity.setStatus(RequestEnum.FAIL);
            try {
                //失败回调
                fail(result);
            } finally {
                logEndAndPrint(result);
            }
        }
    
       
        public void doFail() {
            doFail(null);
        }
    
        //HttpClinet FutureCallback的回调
        @Override
        public void completed(HttpResponse result) {
            try {
                HttpEntity entity = result.getEntity();
                String string = EntityUtils.toString(entity);
                //自定义错误处理,以<html> 开头则为nginx失败页面
                if (HttpUtil.isError(string)) {
                    doFail(string);
                } else {
                    doSuccess(string);
                }
            } catch (Exception e) {
                e.printStackTrace();
                log.error("completed error", e);
                doFail();
            }
        }
    
    
        //日志处理 填入请求结束时间,请求用时等数据
        protected void handleRetryEntity(RequestEnum requestEnum){
            if (this.nowRetryEntity != null){
                this.nowRetryEntity.setEndTime(new Date());
                this.nowRetryEntity.setUseTime(this.nowRetryEntity.getEndTime().getTime() - this.nowRetryEntity.getStartTime().getTime());
                this.nowRetryEntity.setStatus(requestEnum);
            }
        }
    
        //失败重试
        protected void doRetry() {
            handleRetryEntity(RequestEnum.FAIL);
            retryUse++;
            RequestLogEntity retryEntity = new RequestLogEntity();
            retryEntity.setStartTime(new Date());
            retryEntity.setRetryCount(retryUse);
            this.requestLogEntity.getRetryList().add(retryEntity);
            this.requestLogEntity.setRetryCount(retryUse);
            this.nowRetryEntity = retryEntity;
    
            log.info("{}超时重试第{}次", request.getURI().toString(), retryUse);
            HttpUtil.asyncExecute(request, this);
        }
    
        @Override
        public void failed(Exception ex) {
            if (ex instanceof SocketTimeoutException && retryUse < retryCount && retryCount != -1) {
                //超时并且总请求次数小于3,执行重试
                doRetry();
            } else {
                ex.printStackTrace();
                //超时后的异常处理
                doFail();
            }
        }
    
        //按失败处理
        @Override
        public void cancelled() {
            doFail();
        }
    
        //调用实现类的success的方法
        @Override
        public void success(String result) {
            callBack.success(result);
        }
    
        //调用实现类的fail的方法
        @Override
        public void fail(String result) {
            callBack.fail(result);
        }
    
        public HttpUriRequest getRequest() {
            return request;
        }
    

    简化的回调接口

    public interface RetryFutureCallBack {
        //成功回调
        void success(String result);
        //失败回调
        void fail(String result);
    }
    

    不通过回调,通过阻塞的方式获取结果

    public class EmptyRetryFutureCallBack implements RetryFutureCallBack {
        private String result;
    
        private boolean completed;
    
        
        public synchronized String getResult() throws InterruptedException {
            while (!completed) {
                //超时等待3s
                wait(1000 * 3);
            }
            return result;
        }
    
        private synchronized void handleResult(String result) {
            this.completed = true;
            this.result = result;
            notifyAll();
        }
    
        @Override
        public void success(String result) {
            handleResult(result);
        }
    
        @Override
        public void fail(String result) {
            handleResult(result);
        }
    }
    
    

    日志类

    @Data
    @Slf4j
    public class RequestLogEntity {
        //请求开始时间
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
        private Date startTime;
        //请求结束时间
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
        private Date endTime;
       	//用时ms
        private Long useTime;
        //请求域名或者主机ip
        private String host;
        //请求url参数
        private Map<String, Object> params;
        @JsonIgnore
        //请求url
        private String url;
        //请求结果
        private String result;
        //重试次数
        private Integer retryCount;
        //解析数据后,数据的结果集大小
        private int resultSize;
        /**
         * 状态
         */
        private RequestEnum status;
        private List<RequestLogEntity> retryList;
    
        public void generateUrlInfo(String url) {
            this.url = url;
            //获取host
            if (StringUtils.isBlank(url))
                return;
            try {
                String[] split = StringUtils.split(url, "?");
                if (split == null)
                    return;
                if (split.length > 0) {
                    this.host = split[0];
                }
                if (split.length > 1) {
                    generateParam(split[1]);
                }
    
            } catch (Exception e) {
                log.warn("解析url失败", e);
            }
    
        }
    
        private void generateParam(String paramString) {
            String[] split = StringUtils.split(paramString, "&");
            this.params = new HashMap<>();
            for (String item : split) {
                String[] itemSplit = StringUtils.split(item, "=");
                if (itemSplit == null || itemSplit.length == 0)
                    continue;
                this.params.put(itemSplit[0], itemSplit.length == 2 ? itemSplit[1] : "");
            }
    
        }
    }
    
    

    自定义回调类实现

    @Sl4j
    public abstract class MyCallBack implements RetryFutureCallBack {
         public void success(String result) {
           	log.info("成功请求结果:{}",result);
        }
        
        public void fail(String result){
            log.error("请求失败结果:{}",result);
        }
    
    }
    

    HttpUtil类

    
    @Slf4j
    public class HttpUtil {
    
        private static final String APPLICATION_JSON = "application/json";
    
        private static final String CONTENT_TYPE_FORM_URL = "application/x-www-form-urlencoded";
    
        private static volatile CloseableHttpAsyncClient client = null;
    
        private static final PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
    
        // setConnectTimeout表示设置建立连接的超时时间
        // setConnectionRequestTimeout表示从连接池中拿连接的等待超时时间
        // setSocketTimeout表示发出请求后等待对端应答的超时时间
        public static int CONNECT_TIMEOUT = 3_000;
        public static int CONNECTION_REQUEST_TIMEOUT = 3_000;
        public static int SOCKET_TIMEOUT = 3_000;
        private static final RequestConfig requestConfig = RequestConfig
                .custom()
                .setConnectTimeout(CONNECT_TIMEOUT)
                .setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT)
                .setSocketTimeout(SOCKET_TIMEOUT)
                .build();
    
    
        static {
            // 总连接池数量
            connectionManager.setMaxTotal(1000);
            //将每个路由的默认最大连接数增加到500
            connectionManager.setDefaultMaxPerRoute(500);
            //连接存活时间
            connectionManager.setValidateAfterInactivity(60_000);
        }
    
    
        private static final Object lock = new Object();
    
        public static CloseableHttpAsyncClient getHttpAsyncClient() {
            if (client == null) {
                synchronized (lock) {
                    if (client == null) {
    
    
                        //配置io线程
                        IOReactorConfig ioReactorConfig = IOReactorConfig.custom().
                                setIoThreadCount(Runtime.getRuntime().availableProcessors() * 2)
                                .setSoKeepAlive(true)
                                .build();
                        //设置连接池大小
                        ConnectingIOReactor ioReactor = null;
                        try {
                            ioReactor = new DefaultConnectingIOReactor(ioReactorConfig);
                        } catch (IOReactorException e) {
                            e.printStackTrace();
                        }
    
                        PoolingNHttpClientConnectionManager connManager = new PoolingNHttpClientConnectionManager(ioReactor);
                        connManager.setMaxTotal(1000);//最大连接数设置
                        connManager.setDefaultMaxPerRoute(500);//per route最大连接数设置
                        client = HttpAsyncClients.custom()
                                .setConnectionManager(connManager)
    //                            .setKeepAliveStrategy(keepAliveStrategy)
                                .setDefaultRequestConfig(requestConfig)
                                .build();
                        client.start();
    
    
                        //开启监控线程,对异常和空闲线程进行关闭
                        ScheduledExecutorService monitorExecutor = Executors.newScheduledThreadPool(1);
                        monitorExecutor.scheduleAtFixedRate(new TimerTask() {
                            @Override
                            public void run() {
                                //关闭异常连接
                                connectionManager.closeExpiredConnections();
                                //关闭5s空闲的连接
                                connectionManager.closeIdleConnections(5, TimeUnit.SECONDS);
                                log.debug("close expired and idle for over 5s connection");
                            }
                        }, 3, 3, TimeUnit.SECONDS);
                    }
                }
            }
            return client;
        }
    
    
        public static Future<HttpResponse> asyncExecute(RetryFutureProxy retryFutureProxy) {
    
            CloseableHttpAsyncClient httpAsyncClient = getHttpAsyncClient();
            return httpAsyncClient.execute(retryFutureProxy.request, retryFutureProxy);
        }
    
        public static Future<HttpResponse> asyncExecute(HttpUriRequest request, FutureCallback<HttpResponse> callback) {
            CloseableHttpAsyncClient httpAsyncClient = getHttpAsyncClient();
            return httpAsyncClient.execute(request, callback);
        }
    
    
        public static boolean isError(String result) {
            /*
             * nginx 500 页面
             */
            if (result != null && result.startsWith("<html>")) {
                log.info(result);
                return true;
            } else {
                return false;
            }
        }
    
        //阻塞方式获取结果内容
        private static String httpRequest(String urlString) {
            HttpGet httpGet = new HttpGet(urlString);
            EmptyRetryFutureCallBack callBack = new EmptyRetryFutureCallBack();
            asyncExecute(httpGet, new RetryFutureProxy(httpGet, callBack));
            String result = null;
            try {
                //HttpEntity被EntityUtils.toString(entity)消费过了,采用这种方式获取结果集
                result = callBack.getResult();
            } catch (Exception e) {
                e.printStackTrace();
                log.error("httpUtil exception", e);
            }
            return result;
    
        }
    
    
    
    }
    
    

    调用

    public static void main(String[] args){
    	HttpGet httpGet =new HttpGet("http://www.baidu.com");
    	RetryFutureProxy retryFutureProxy = new RetryFutureProxy(2,httpGet,new MyCallBack() );
    	HttpUtil.asyncExecute(retryFutureProxy);
    	//防止主线程结束,回调还没结束
    	Thread.sleep(3000);
    }
    

    阻塞式调用

    public static void main(String[] args){
    	String result = HttpUtil.httpRequest("http://www.baidu.com");
    	System.out.println(result);
    }
    
  • 相关阅读:
    【JavaScript】Object 实例属性
    【JavaScript】Object 构造函数和属性
    【JavaScript】Object 静态方法(三)
    PTA 乙级 1051 复数乘法 (15分) Python
    PTA 乙级 1050 螺旋矩阵 (25分) C++
    PTA 乙级 1049 数列的片段和 (20分) C/C++ (更新OJ导致测试点2无法通过,已解决)
    对象的深拷贝和浅拷贝和拷贝方法
    linux(腾讯云服务器)上安装mysql数据库
    SQLyog远程连接mysql数据库
    linux中搭建tomcat环境
  • 原文地址:https://www.cnblogs.com/huisunan/p/14794128.html
Copyright © 2020-2023  润新知