Java拷贝网站的文本信息,两万次循环,用了一天时间

 工作中领导给的一个任务,让把一个网站的一些文本数据批量保存下来,每个数据都有json格式和xml格式,找了一段时间发现只有办法批量下载各个数据的id,但是每个json和xml数据后面都跟着id,就想到用链接头+id的方法来下载,代码如下:

    @Autowired
    private ResMapper resMapper ;

    // OkHttpClient是一个HTTP客户端,用于发送HTTP请求和接收HTTP响应。
    private static final OkHttpClient client = new OkHttpClient();
    //@Qualifier("dataSource")


    @Override
    public void add(){

        String jsonMsg = "";
        String xmlMsg = "";
        String depId = "";

        for (int id = 1; id < 22344; id++) {
//        和数据库交互我用的mybatis,sql语句在下面
            depId = resMapper.selectById(id);

            String url = "https://链接头——————/" + depId;
            String url2 = "https://链接头————————/" + depId;

            Request jsonRequest = new Request.Builder()
                    .url(url)
                    .build();

            try (Response jsonResponse = client.newCall(jsonRequest).execute()) {

                if (!jsonResponse.isSuccessful()) continue;

                String jsonResponseBody = jsonResponse.body().string();
                // 解析JSON响应(示例使用Gson)
                JsonElement jsonElement = JsonParser.parseString(jsonResponseBody);
                jsonMsg = jsonElement.toString();


                // 获取xml数据
                Request xmlRequest = new Request.Builder()
                        .url(url2)
                        .build();

                try (Response xmlResponse = client.newCall(xmlRequest).execute()) {
                    if (xmlResponse.isSuccessful()) {
                        String xmlResponseBody = xmlResponse.body().string();
                        xmlMsg = xmlResponseBody;

                        resMapper.save(id, jsonMsg, xmlMsg);
//                        System.out.println(id+"_"+depId+"成功");
                    }else {

                        System.out.println("xml失败");
                    }

                } catch (Exception e) {
//本来是抛出异常,但是中间会有保存失败的时候,就改成了失败就输出到控制台,然后第二次我把这些id加到一个集合里,重新添加失败的
                    System.out.println("id = " + id + ";depId = " + depId);
                }
            } catch (Exception e) {
                System.out.println("id = " + id + ";depId = " + depId);
            }

        }
    }

 2万多次循环还是很臃肿,但是从网上下载这些数据还是很快,毕竟只有文本数据,再数据库中做更新操作很慢(save方法是做update更新),不知道有没有大哥还有更好的方法。
虽然下载了一天,但是也省去了自己一条一条去拷贝的时间,毕竟程序后台跑着也不耽误我同时做别的工作

<mapper namespace="com.example.demo.demos.mapper.ResMapper">
    <update id="save">
        update res set json_msg = #{jsonMsg}, xml_msg = #{xmlMsg} where id = #{id}
    </update>

    <select id="selectById" resultType="java.lang.String">
        select dep_id from res where id = #{id}
    </select>
</mapper>

 数据库表:

遇到的其他问题:
1.不小心加了事务注解,因为数据量太大导致一个失败都失败,没办法实时更新数据库
2.mybatis连接问题,发现是文件夹名字没对应好
3.lombok的Maven依赖导入不了,没解决,最后是重新初始化项目直接idea可以选择自动导入lombok了,但是我这个程序用不到,只是导入了
4.文本信息太长,varchar已经满足不了,用的mediumtext
5.还有一个问题就是String这里要做4万多次拼接字符串,肯定是用StringBuffer更好,但是我这里没改,直接让他跑起来了


 


再更新

    public void getListAsync() {
        ExecutorService executorService = Executors.newFixedThreadPool(20); // 根据实际情况调整线程池大小
        SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
        PengMapper pengMapper = sqlSession.getMapper(PengMapper.class);

        List<String> depIds = pengMapper.selectAll();

/*        List<String> depIds = new ArrayList<>();
        depIds.add("10087163");*/

        List<CompletableFuture<Void>> futures = depIds.parallelStream().map(depId -> {
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                String jsonMsg = "";
                String xmlMsg = "";

                String urlJson = "链接头" + depId;
                String urlXml = "链接头" + depId;

                try {
                    // 异步获取JSON数据
                    String jsonResponseBody = client.newCall(new Request.Builder().url(urlJson).build()).execute().body().string();
                    JsonElement jsonElement = JsonParser.parseString(jsonResponseBody);
                    jsonMsg = jsonElement.toString();

                    // 异步获取XML数据
                    String xmlResponseBody = client.newCall(new Request.Builder().url(urlXml).build()).execute().body().string();
                    xmlMsg = xmlResponseBody;

                    Peng msg = new Peng(depId, jsonMsg, xmlMsg);
                    pengMapper.insert(msg);

                } catch (Exception e) {
                    System.err.println("Error processing depId=" + depId + ": " + e.getMessage());
                }
            }, executorService);

            return future;
        }).collect(Collectors.toList());

        // 等待所有异步任务完成
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

        // 提交事务
        sqlSession.commit();

        sqlSession.close();
        executorService.shutdown(); // 关闭线程池

    }

下面是逐行的详细解释:
1.创建线程池:通过 Executors.newFixedThreadPool(20) 创建一个固定大小为20的线程池,用于并行处理多个任务。
2.打开MyBatis的SqlSession:使用 sqlSessionFactory.openSession(ExecutorType.BATCH, false) 打开一个新的SqlSession,设置为批量执行模式且非自动提交,以便手动控制事务。
3.获取Mapper实例:通过SqlSession获取PengMapper的实例,用于执行SQL操作。
4.查询部门ID列表:调用 pengMapper.selectAll() 获取所有部门ID的列表。
5-6. /注释部分/ 是一个示例代码,展示如果直接给定一个部门ID列表而非从数据库查询的情况。
7.并行流处理部门ID:对depIds列表使用 parallelStream() 创建并行流,然后使用 map() 方法为每个depId创建一个异步任务。
8-13. 创建异步任务:对于每个depId,创建一个 CompletableFuture<Void>,表示一个没有返回值的异步操作。该操作在 executorService 线程池中执行。
14-15. 初始化消息变量:为JSON和XML消息分配空字符串。
16-17. 构建URL:根据部门ID构造获取JSON和XML数据的URL。
18-27. 异步获取数据并存储:
使用OkHttp客户端异步请求JSON和XML数据。
解析JSON响应并将其转换为字符串。
直接存储XML响应内容。
创建一个 Peng 对象,并使用 pengMapper.insert(msg) 将其插入数据库。
28-30. 异常处理:捕获执行过程中可能出现的任何异常,并打印错误信息。
返回Future对象:每个映射操作返回一个 CompletableFuture<Void> 实例。
32-33. 收集Future列表:将所有异步任务的Future收集到一个列表中。
34-35. 等待所有任务完成:使用 CompletableFuture.allOf() 方法等待所有异步任务完成,然后调用 join() 阻塞直到它们都完成。
36.提交事务:调用 sqlSession.commit() 提交之前所有批量插入操作。
27.关闭SqlSession:完成操作后关闭SqlSession。
38.关闭线程池:通过 executorService.shutdown() 平滑地关闭线程池。

速度加快了6倍,本来12个小时完成的,加快到2个小时完成,但是感觉还是不够快,有没有更好的办法去优化 

  • 11
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值