使用CountdownLatch和线程池批量处理http请求,并处理响应数据

news2024/10/6 8:35:19

背景和问题

​ 背景:最近项目的一个接口数据,需要去请求其他多个服务器的数据,然后统一返回;
问题点:如果遍历所有的服务器地址,然后串行请求就会出现请求时间过长,加入需要请求十个服务器,一个服务器是1s那么请求服务器数据总时间就需要10s,导致响应时间太长,所以需要使用多线程。如果直接使用多线程去请求,那么没法知道是否所有接口是否都请求结束,所以用到了技术门闩CountdownLatch,每一个接口请求结束之后都会调用CountdownLatchcount方法进行计数,当归零后就会唤醒主线程进行后续逻辑,并且使用ConcurrentLinkedQueue记录响应结果。

话不多说,直接上代码!!

准备数据:

​ 四个接口(三个模拟请求服务器接口,一个直接访问的接口),由于我是本地环境,所以在每个接口中设置了不同的休眠时间,来模拟不同服务器场景

代码展示

  1. 模拟接口

    	@RequestMapping("/hello")
        public String hello(@RequestParam(value = "id") Long id, @RequestBody User params) {
            if (id != 1) {
                return null;
            }
            System.out.println(params.toString());
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            List<User> users = new ArrayList<>();
            users.add(new User("张三", 1, "男"));
            users.add(new User("李四", 2, "男"));
            users.add(new User("王五", 3, "女"));
    
            return JSON.toJSONString(users);
        }
    
        @RequestMapping("/hello1")
        public String hello1(@RequestParam(value = "id") Long id) {
            if (id != 2) {
                return null;
            }
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            List<User> users = new ArrayList<>();
            users.add(new User("张三1", 11, "男"));
            users.add(new User("李四1", 21, "男"));
            users.add(new User("王五1", 31, "女"));
    
            return JSON.toJSONString(users);
        }
    
        @RequestMapping("/hello2")
        public String hello2(@RequestParam(value = "id") Long id) {
            if (id != 3) {
                return null;
            }
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            List<User> users = new ArrayList<>();
            users.add(new User("张三2", 12, "男"));
            users.add(new User("李四2", 22, "男"));
            users.add(new User("王五2", 32, "女"));
    
            return JSON.toJSONString(users);
        }	
    
  2. 直接访问接口数据

     @RequestMapping("/demo")
        public String demo() throws InterruptedException, IOException {
    
            OkHttpClient client = new OkHttpClient();
            final CountDownLatch countDownLatch = new CountDownLatch(3);
            // 使用ConcurrentLinkedQueue 存储每个请求的响应结果,ConcurrentLinkedQueue 是一个线程安全的
            final ConcurrentLinkedQueue<Response> responses = new ConcurrentLinkedQueue<>();
            ExecutorService executor = Executors.newFixedThreadPool(10);
            long start = System.currentTimeMillis();
            for (int i = 1; i <= urls.size(); i++) {
                String url = urls.get(i - 1);
                int finalI = i;
                executor.submit(() -> {
                    //构建请求中需要的请求参数 id 通过 RequestParam获取
                    HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
                    urlBuilder.addQueryParameter("id", String.valueOf(finalI));
                    String newUrl = urlBuilder.build().toString();
                    // 表单提交
    //                FormBody formBody = new FormBody.Builder().add("id", String.valueOf(finalI)).build();
    //                Request request = new Request.Builder().url(newUrl).post(formBody).build();
                    // 构建参数,通过@RequestBody取出参数
                    User user = new User("1", 2, "男");
                    okhttp3.RequestBody requestBody = okhttp3.RequestBody.create(MediaType.parse("application/json; charset=utf-8"),JSON.toJSONString(user));
                    Request request = new Request.Builder().url(newUrl).post(requestBody).build();
                    try {
                        Response response = client.newCall(request).execute();
                        if (!response.isSuccessful()) {
                            // 可以在单独记录下,然后做异常处理
                            throw new IOException("请求失败:" + response);
                        } else {
                            responses.add(response);
                        }
    
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    } finally {
                        // 执行一个请求进行一次计数
                        countDownLatch.countDown();
                    }
                });
            }
    
            //等待countDownlatch 计数门闩归零即所有线程请求完成,然后唤醒线程,但是会设置一个最长等待时长 10s
            boolean await = countDownLatch.await(10, TimeUnit.SECONDS);
            executor.shutdown();
            long end = System.currentTimeMillis();
            System.out.println("http的整个请求时间为:" + DateUtil.formatBetween(end - start));
            Map<String,  List<User>> res = new HashMap<>();
            if (!responses.isEmpty()) {
                int i = 0;
                for (Response response : responses) {
                    URL url = response.request().url().url();
                    String string = response.body().string();
                    List<User> users = JSON.parseArray(string, User.class);
                    res.put(url.toString(),users);
                }
            } else {
                System.out.println("无响应结果!");
            }
    
            System.out.println(res);
            return "demo";
        }
    
  3. 其他相关信息

      private static final List<String> urls = new ArrayList<>();
    
        static {
            urls.add("http://localhost:8080/hello");
            urls.add("http://localhost:8080/hello1");
            urls.add("http://localhost:8080/hello2");
        }
    
    
        public static class User {
            private String name;
    
            private Integer age;
    
            private String sex;
    
            public User(String name, Integer age, String sex) {
                this.name = name;
                this.age = age;
                this.sex = sex;
            }
    
            public String getName() {
                return name;
            }
    
            public void setName(String name) {
                this.name = name;
            }
    
            public Integer getAge() {
                return age;
            }
    
            public void setAge(Integer age) {
                this.age = age;
            }
    
            public String getSex() {
                return sex;
            }
    
            public void setSex(String sex) {
                this.sex = sex;
            }
    
            @Override
            public String toString() {
                return "User{" +
                        "name='" + name + '\'' +
                        ", age=" + age +
                        ", sex='" + sex + '\'' +
                        '}';
            }
    
            //相关依赖
    		<dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>5.8.21</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp -->
            <dependency>
                <groupId>com.squareup.okhttp3</groupId>
                <artifactId>okhttp</artifactId>
                <version>3.5.0</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>2.0.38</version>
            </dependency>
    

结果

在这里插入图片描述

结果解释

  1. 第一行:是/hello接口中@RequestBody的参数打印。
  2. 第二行是整个请求时间,因为我设置的/hello2接口时间为5s,可以看到这里的接口请求时间是最长的接口时间,而不是所有时间相加。
  3. 可以看到设置的参数能够成功获取,并且数据能成功接收。

注意事项

在这里插入图片描述

  1. response中body体的数据只能取一次,取出之后就会将其设置为closed,所以建议使用变量进行接收之后再做处理。

  2. 这个唤醒时间最好设置一个默认值,免得程序出问题主线程一直卡死在这里。

    countDownLatch.await(10, TimeUnit.SECONDS);
    
  3. 这个count一定不能忘了,不然这个主线程也就卡死了

在这里插入图片描述

最后,这只是一个简单的demo程序,真实的项目情况肯定是不一样的,所以只能做一个参考,具体情况还需做具体处理!

源码已提交gitee

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1120248.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Xray联动crawlergo自动扫描漏洞教程

xray和crawlergo联动 xray下载: https://github.com/chaitin/xray/releases crawlergo下载:360-crawlergo&#xff1a; github&#xff1a;https://github.com/Qianlitp/crawlergo/releases 联动脚本&#xff1a; github&#xff1a;https://github.com/timwhitez/crawlergo_x_…

Qt扫盲-QColor 理论使用总结

QColor 理论使用总结 一、概述1. 构造函数2. 合法性校验3. 组成值检索 二、整数与浮点精度三、Alpha-Blended Alpha混合图四、预定义颜色五、扩展的RGB颜色模型六、HSV颜色模型七、HSL颜色模型八、CMYK颜色模型 一、概述 QColor 是Qt里面描绘颜色的一个属性类&#xff0c;这个…

【算法练习Day25】 重新安排行程N 皇后 解数独

​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;练题 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 重新安排行程N 皇后解数独总…

图论05-【无权无向】-图的广度优先BFS遍历-路径问题/检测环/二分图/最短路径问题

文章目录 1. 代码仓库2. 单源路径2.1 思路2.2 主要代码 3. 所有点对路径3.1 思路3.2 主要代码 4. 联通分量5. 环检测5.1 思路5.2 主要代码 6. 二分图检测6.1 思路6.2 主要代码6.2.1 遍历每个联通分量6.2.2 判断相邻两点的颜色是否一致 7. 最短路径问题7.1 思路7.2 代码 1. 代码…

使用 Visual Studio Code (VS Code) 作为 Visual C++ 6.0 (VC6) 的编辑器

使用 Visual Studio Code (VS Code) 作为 Visual C 6.0 (VC6) 的编辑器 由于一些众所周知的原因&#xff0c;我们不得不使用经典&#xff08;过时&#xff09;的比我们年龄还大的已有 25 年历史的 VC 6.0 来学习 C 语言。而对于现在来说&#xff0c;这个经典的 IDE 过于简陋&a…

python:红狐优化算法(Red fox optimization,RFO)求解23个基本函数

一、红狐优化算法 红狐优化算法&#xff08;Red fox optimization&#xff0c;RFO&#xff09;由Dawid Połap和 Marcin Woźniak于2021年提出&#xff0c;该算法模拟了红狐的狩猎行为&#xff0c;具有收敛速度快&#xff0c;寻优精度高等优势。 参考文献&#xff1a; Poap D …

华为OD技术面试-最短距离矩阵(动态规划、广度优先)

背景 记录2023-10-21 晚华为OD三面的手撕代码题&#xff0c;当时没做出来&#xff0c;给面试官说了我的想法&#xff0c;评价&#xff1a;解法复杂了&#xff0c;只是简单的动态规范 或 广度优先算法&#xff0c;事后找资料记录实现方式。 题目 腐烂的橘子 问题描述&#xff…

【项目实战】从零开始设计并实现一个接口异常链路分析器

这不是马上要到1024了吗&#xff0c;这不得弄个什么工具给部门项目提提效&#x1f62f;&#xff1f; 1. 背景 在我们服务端应用当中&#xff0c;我们往往会要求更高的性能和更高的稳定性&#xff0c;但实际开发的过程中&#xff0c;可能会出现很多赶时间的情况&#xff08;也…

RustDay06------Exercise[91-100]

91.将指针还原成指定类型 因为指针不知道里面具体有什么,所以一般约定打上unsafe 申明开发者自己对该部分可用性负责,且在调试的时候也能起强调作用 // tests6.rs // // In this example we take a shallow dive into the Rust standard librarys // unsafe functions. Fix …

大疆智图(PC):新一代高效率高精度摄影测量软件

大疆智图是一款以二维正射影像与三维模型重建为主的软件&#xff0c;同时提供二维多光谱重建、激光雷达点云处理、精细化巡检等功能。它能够将无人机采集的数据可视化&#xff0c;实时生成高精度、高质量三维模型&#xff0c;满足事故现场、工程监测、电力巡线等场景的展示与精…

42914-2023 铝合金产品断裂韧度试验方法

1 范围 本文件描述了铝合金产品断裂韧度的试验方法。 本文件适用于铝合金轧制板材、挤压棒材、挤压板材、挤压管材、挤压型材和锻件产品的平面应变断 裂韧度和平面应力断裂韧度的测定。 2 规范性引用文件 下列文件中的内容通过文中的规范性引用而构成本文件必不可少的条款…

Bootstrap的卡片组件相关知识

Bootstrap的卡片组件 01-卡片介绍及常用场合 Bootstrap的卡片组件&#xff08;Card&#xff09;是一种常用的UI元素&#xff0c;或者也可称为一种常用的结构&#xff0c;用于呈现信息和内容&#xff0c;通常在网页和应用程序中用于以下情况&#xff1a; 博客文章和新闻文章&a…

Linux自有服务与软件包管理

服务是一些特定的进程&#xff0c;自有服务就是系统开机后就自动运行的一些进程&#xff0c;一旦客户发出请求&#xff0c;这些进程就自动为他们提供服务&#xff0c;windows系统中&#xff0c;把这些自动运行的进程&#xff0c;称为"服务" 举例&#xff1a;当我们使…

经管博士科研基础【27】如何判断正定矩阵或者负定矩阵?

在【26】一章中,我们学习到可以通过判断海塞矩阵是正定矩阵或负定矩阵来判断函数的极值问题,为此,我们今天就回顾一下怎么判断海塞矩阵或者说任意一个矩阵是一个正定矩阵或者负定矩阵。 一、正定矩阵的定义 其实,我们可以看到上面的任意非零向量x可以更换为“单位向量”。…

多继承的实例介绍

一、多继承同名覆盖 子类中的成员与父类中的成员同名问题&#xff0c;通过作用域分辨符&#xff08;&#xff1a;&#xff1a;&#xff09;进行限定类的访问&#xff0c;从而实现对不同类中的同名成员各自赋值。 #include<iostream> using namespace std; class A{//父…

一起学数据结构(10)——排序

从本文开始&#xff0c;通过若干篇文章展开对于数据结构中——排序的介绍。 1. 排序的概念&#xff1a; 将一堆杂乱无章的数据&#xff0c;通过一定的规律顺序排列起来。即将一个无序序列排列成一个有序序&#xff08;由小到大或者由大到小&#xff09;的运算。 在数据的排序中…

小微企业需要认定吗?怎么认定?

小微企业在方便人民群众生活&#xff0c;解决就业&#xff0c;活跃市场经济方面发挥了巨大作用。我国对小微企业也有相应的划分标准和税收优惠政策&#xff0c;那么小微企业需要认定吗&#xff1f;认定小微企业需要哪些资料&#xff1f;下面玖邀开业小编给大家做一个简单说明。…

BUUCTF N种方法解决 1

BUUCTF:https://buuoj.cn/challenges 题目描述&#xff1a; 下载附件&#xff0c;解压得到一个.exe文件 密文&#xff1a; 解题思路&#xff1a; 1、双击.exe文件&#xff0c;出现一个错误&#xff0c;切换其他的方法。 2、将KEY.exe文件放到010 Editor&#xff0c;分析这个…

Delphi : 在 SDK 管理器中添加其他 iOS 框架

在用Delphi开发IOS程序时&#xff0c;有时候需要添加其他的iOS框架&#xff0c;也就是说在默认的SDK中没有包含的iOS框架&#xff08;frameworks&#xff09;。 如果您希望利用 Delphi 提供支持之外的 iOS 框架&#xff0c;则需要在 SDK 管理器中添加框架的路径。 为此&#…

使用Python打造微信高效自动化操作教程

引言 在如今数字化时代&#xff0c;人们对于效率的追求越来越强烈&#xff0c;尤其是在工作和学习中。自动化操作成为了提高生产力的有效途径之一&#xff0c;而PyAutoGUI和Pyperclip作为Python中的两个强大库&#xff0c;为我们实现自动化操作提供了便利。本文将向大家介绍如…