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

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

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

第一版:串行请求

脚本如下:

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()
}

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


热文精选


↙↙↙阅读原文可查看相关链接,并与作者交流