批量数据请求问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
u3f3o3333
V2EX    Java

批量数据请求问题

  •  
  •   u3f3o3333 2023-10-17 19:32:30 +08:00 1669 次点击
    这是一个创建于 726 天前的主题,其中的信息可能已经有所发展或是发生改变。

    背景

    Java 小白,刚入门 SpringBoot 项目练手,现有一个需求要将本地数据库中的设备信息表批量发送给第三方 API 接口,本地设备信息表由用户导入,表结构如下:

    id 为主键,device_ip 为设备 IP 地址信息,host_id 为第三方系统 API 返回的设备 ID 值,其他列为设备的相关属性参数。

    id device_ip host_id device_attr_1
    1 x.x.x.x 1359652 aaaaaa ……
    2 y.y.y.y 8496143 bbbbb ……

    当用户点击“插入”操作后,需要将表中所有的数据,按照规定的 JSON 格式,将其发送给第三方系统的 API 接口。

    如果执行成功,第三方系统 API 接口会返回设备 ID 值,将其保存到表中对应设备条目的 host_id 值中。

    如果执行失败,第三方系统 API 接口会返回 error 消息,需要记录未成功添加的设备清单,供用户核查。

    目前是写了一个异步方法,读取全表的设备信息为放入一个 List ,根据要求构造 JSON 数据,一条一条发送,每次 API 请求预计消耗 0.1-0.2s ,当数据量达到万级以上时,整个添加过程会达到半小时,时间太长。

    现在想缩短整个添加过程的时长,目前在认知范围内考虑两种改进方向:

    方案一

    将这个 List 通过多线程的方式访问 API 接口传送数据,但因为对于多线程模式不是很了解,不知道会不会造成数据不一致的问题,因为需要记录 API 返回的 host_id 值。虽然初步查了一下资料,但是还是没有很清楚的概念具体应该如何实现。

    方案二

    该第三方 API 接口,允许一次传入多个设备的数据,但一次最多只能传 100 条左右的数据。考虑将整个 List 拆成 100 个一组,每组里面包含 100 个设备信息的 JSON 数据,将这 100 个数据给 API 发送一次。

    但是这 100 条信息如果其中有任意一台添加失败,会导致所有这 100 台全都添加失败,API 只会返回添加失败信息,并不会指明哪一条数据添加异常,所以考虑此方法还需要在发生 API 请求失败时,切换成一条一条添加,这样就可以判断出哪一条数据添加异常,以便将无法添加的条目反馈供用户核验,这样也能保证无异常的设备都可以被添加进第三方系统中。这种方法时间起来感觉逻辑较复杂。

    求助

    因为新手没什么项目经验,不知道那种方案比较合适,或者是否有其他更优的解决方案,谢谢!

    目前 Service 的部分逻辑代码如下:

    import ... @EnableAsync @Service public class MonitorServiceImpl implements IMonitorService { @Autowired private MonitorInfoMapper monitorInfoMapper; @Async @Override public void startMonitor() { List<Equipment> equipmentList = monitorInfoMapper.selectEquipmentList();//获取数据表中所有设备清单 for (Equipment equip : equipmentList) { //.............. //处理数据,构造 JSON 数据格式,最后将一台设备信息放入 hostInfoJSON 变量 //iApiService 写的第三方访问接口 String creatHostResp = iApiService.insertHostList(authToken, hostInfoJSON); String hostid; if (creatHostResp.contains("error")) { hostid = "-1"; JSONObject resposeJson = JSONObject.parseObject(creatHostResp); String errorString = resposeJson.getJSONObject("error").getString("data"); log.info("添加失败!错误信息:" + errorString); } else { JSONObject resposeJson = JSONObject.parseObject(creatHostResp); List<String> hostIdList = new ArrayList<>(); JSONArray resposeHostIdList = resposeJson.getJSONObject("result").getJSONArray("hostids").getString(0); log.info("添加成功,hostid:" + hostIdList); } equip.setHostId(hostid); monitorInfoMapper.updateEquipment(equip); } //...... } 
    8 条回复    2023-10-18 16:40:44 +08:00
    pddgoods
        1
    pddgoods  
       2023-10-17 19:39:25 +08:00
    核心点,总共多少数据。
    u3f3o3333
        2
    u3f3o3333  
    OP
       2023-10-17 19:40:11 +08:00
    @pddgoods 一次需要插入数据量在万级左右
    pocketz
        3
    pocketz  
       2023-10-17 21:05:29 +08:00
    你不是会写异步方法吗。。。
    直接把 http 请求的部分拆出来走原生 executor 线程池
    lsk569937453
        4
    lsk569937453  
       2023-10-18 08:44:08 +08:00
    1.最好还是按照批次发送,能节省不少网络请求,如果担心失败后需要重试的条数过多,可以把 100 个一组,改为 10 个一组。
    2.可以 startMonitor 里面把 equipmentList 切分好(按照 10 个一组),然后把每组的数据丢到线程池去请求 API(前提对面没有并发数的限制)

    其实这个需求还有几点需要明确:
    1.是否允许用户在发送数据期间重复点击"插入"。如果允许的话,是不是需要存储每个批次的任务状态?
    2.如果任务失败后需要重试,那么何为失败,网络请求失败?http 状态码非 200 ?失败后需要重试几次?
    timtraveler
        5
    timtraveler  
       2023-10-18 15:11:42 +08:00
    方案一 + 方案二,多线程+批量插入 100 条数据。
    1 ,单独创建一张第三方调用结果表( A 表),保存所有设备数据 id 字段、插入成功或失败的状态字段、失败次数字段。
    2 ,在第一轮插入之后,查询 A 表中的所有失败的设备 id ,关联到总表,根据失败次数,将插入数量改为 50 (第一次失败)、10 (第二次失败)、1 (第三次失败)重新进行插入,最后剩下的为无效数据,额外进行处理
    xiaohang427
        6
    xiaohang427  
       2023-10-18 15:43:55 +08:00
    1 、使用游标查询,防止一下查询的数据太大吃内存
    2 、开线程池处理第三方通讯,并且根据结果更新数据库
    u3f3o3333
        7
    u3f3o3333  
    OP
       2023-10-18 16:39:17 +08:00
    @lsk569937453 感谢大佬建议!关于需要明确的 2 点:1 、目前简单考虑是不允许用户在发送数据期间重复点击"插入",会提示数据正在处理。2 、除了常规网络问题可能导致的任务失败外,其他主要的一种原因是,第三方系统之前有垃圾数据没清理干净,会提示设备添加失败,比如之前在第三方系统有一个 192.168.1.1 的设备数据条目没有删除,而本次导入数据中也有一个 192.168.1.1 设备条目,此时第三方系统并不会覆盖,而是会返回报错添加设备失败。当发送批量数据时,若有一个数据出现冲突,它并不会明确提示哪个设备添加失败,只会提示本次导入任务失败。目前是遇到失败不重试,返回导入失败的条目,给用户核实后,下次再进行单独的添加操作。
    u3f3o3333
        8
    u3f3o3333  
    OP
       2023-10-18 16:40:44 +08:00
    @pocketz
    @timtraveler
    @xiaohang427
    感谢诸位大佬建议和指导!我再思考研究下
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1349 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 34ms UTC 16:46 PVG 00:46 LAX 09:46 JFK 12:46
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86