HTTP异步连接池和多线程实践

今天在查询一个列表的时候,突然发现列表由于之前压测导致几万条脏数据积累。导致找一个数据比较麻烦,由于项目没有提供批量删除的功能,所以想了个办法通过接口把数据挨个删除。

思路如下:先去请求分页列表,然后解析数据,通过请求删除接口去一条一条的删除。

虽然比较简单,但是几万条数据还是耗费了比较长的时间,中间进行了一些优化,所以分成了好几个版本来完成。

第一版:串行请求

脚本如下:

public static void main(String[] args) {
    def base = getBase()
    def manager = new TeacherManager(base)
    3.upto(1000) {
        def list = manager.verifyList(it)
        list.getJSONObject("data")?.getJSONArray("list").each { x ->
            manager.verify(x.id, x.tel)
        }
    }
    allOver()
}

查询列表和删除记录的方法如下:

    public JSONObject verifyList(int page = 3) {
        String url = TeacherManagerApi.VERIFY_LIST;
        JSONObject params = getParams();
        params.put("page", page);
        params.put("page_size", 50);
        JSONObject response = getPostResponse(url, params);
        output(response);
        return response;
    }
    
        public JSONObject verify(int id = 0, String tel = "") {
        String url = TeacherManagerApi.VERIFY;
        JSONObject params = getParams();
        params.put("action", 2);//2:拒绝
        params.put("id", id);
        params.put("tel", tel);
        params.put("refused_result", "清空脏数据");
        JSONObject response = getPostResponse(url, params);
        return response;
    }

第二版:HTTP异步请求优化

主要是优化了verify方法,每次可以串行获取完列表之后,删除的接口请求就通过异步方法调用。方法如下:

    public JSONObject verify(int id = 0, String tel = "") {
        String url = TeacherManagerApi.VERIFY;
        JSONObject params = getParams();
        params.put("action", 2);//2:拒绝
        params.put("id", id);
        params.put("tel", tel);
        params.put("refused_result", "清空脏数据");
        output(params.toString());
        HttpPost post = getPost(url, params);
        setHeaders(post);
        FanLibrary.excuteSyncWithResponse(post);
        return null;
    }

异步连接池的方法如下:

    /**
     * 异步发送请求
     *
     * @param request
     */
    public static void excuteSync(HttpRequestBase request) {
        if (!ClientManage.httpAsyncClient.isRunning()) ClientManage.httpAsyncClient.start();
        ClientManage.httpAsyncClient.execute(request, null);
    }

    /**
     * 异步发送请求获取影响Demo
     * <p>经过测试没卵用</p>
     *
     * @param request
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public static JSONObject excuteSyncWithResponse(HttpRequestBase request) {
        if (!ClientManage.httpAsyncClient.isRunning()) ClientManage.httpAsyncClient.start();
        Future<HttpResponse> execute = ClientManage.httpAsyncClient.execute(request, null);
        try {
            HttpResponse httpResponse = execute.get();
            String content = getContent(httpResponse);
            return getJsonResponse(content, null);
        } catch (Exception e) {
            logger.error("异步请求获取响应失败!", e);
        }
        return new JSONObject();
    }

获取异步连接池的方法:

    /**
     * 通过连接池获取https协议请求对象
     * <p>
     * 增加默认的请求控制器,和请求配置,连接控制器,取消了cookiestore,单独解析响应set-cookie和发送请求的header,适配多用户同时在线的情况
     * </p>
     *
     * @return
     */
    private static CloseableHttpAsyncClient getCloseableHttpAsyncClient() {
        return HttpAsyncClients.custom().setConnectionManager(NconnManager).setSSLHostnameVerifier(SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER).setSSLContext(sslContext).build();
    }

经过测试,异步发送请求的效率果然有所提高,但是有个问题就是不能立刻关闭连接池,不然会导致请求失败,提示连接池已经关闭。

第三版:多线程

核心代码如下:

public static void main(String[] args) {
    def base = getBase()
    def manager = new TeacherManager(base)
    3.upto(100) {
        new Thread({ ->
            def list = manager.verifyList(it)
            list.getJSONObject("data")?.getJSONArray("list").each { x ->
                manager.verify(x.id, x.tel)
            }
        }).start()
    }
    allOver()
}

经过测试,多线程比异步效率高太多了,而且异步总会出现一些问题,比如不成功,由于不关心返回了,很多情况也无法调试,如果使用异步加上获取响应值,有会其他操作,我觉得有点绕远路了。最后采取了多线程这个方案。一秒钟能删掉上百条数据,一会儿就删完了。


  • 公众号FunTester首发,更多原创文章:440+原创文章,欢迎关注、交流,禁止第三方擅自转载。