请求合并

高并发场景中,调用批量接口相比调用非批量接口有更大的性能优势。但有时候,请求更多的是单个接口,不能够直接调用批量接口,如果这个接口是高频接口,对其做请求合并就很有必要了。比如电影网站的获取电影详情接口,APP的一次请求是单个接口调用,用户量少的时候请求也不多,完全没问题;但同一时刻往往有大量用户访问电影详情,是个高并发的高频接口,如果都是单次查询,后台就不一定能hold住了。为了优化这个接口,后台可以将相同的请求进行合并,然后调用批量的查询接口。如下图所示

或者下图

设计决绝方案

代码实现

@Test
public void benchmark() throws IOException {
    // 创建 并不是马上发起请求
    for (int i = 0; i < THREAD_NUM; i++) {
        final String code = "code-" + (i + 1); // 番号
        // 多线程模拟用户查询请求
        Thread thread = new Thread(() -> {
            try {
                // 代码在这里等待,等待countDownLatch为0,代表所有线程都start,再运行后续的代码
                countDownLatch.await();
                // http请求,实际上就是多线程调用这个方法
                Map<String, Object> result = commodityService.queryCommodity(code);
                 System.out.println(Thread.currentThread().getName() + " 查询结束,结果是:" + result);
            } catch (Exception e) {
                System.out.println(Thread.currentThread().getName() + " 线程执行出现异常:" + e.getMessage());
            }
        });
        thread.setName("price-thread-" + code);
        thread.start();
        // 田径。启动后,倒计时器倒计数 减一,代表又有一个线程就绪了
        countDownLatch.countDown();
    }

    // 输入任意内容退出
    System.in.read();
}

 

@Service
public class CommodityService {
    class Request {
        String commdityCode;
        CompletableFuture<Map<String, Object>> future; // 接受结果
    }

    // 积攒 请求。(每隔N毫秒批量处理一次)
    LinkedBlockingQueue<Request> queue = new LinkedBlockingQueue<>();

    // 定时任务的实现,N秒钟处理一次数据
    @PostConstruct
    public void init() {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
        scheduledExecutorService.scheduleAtFixedRate(() -> {
            // 1、 取出queue的请求,生成一次批量查询
            int size = queue.size();
            if (size == 0) {
                return;
            }
            ArrayList<Request> requests = new ArrayList<>();
            for (int i = 0; i < size; i++) {
                Request request = queue.poll();
                requests.add(request);
            }
            System.out.println("批量处理数据量:" + size);
            // 2、 组装一个批量查询(一定需要 目的资源能够支持批量查询。 http)
            ArrayList<String> commodityCodes = new ArrayList<>();
            for (Request request : requests) {
                commodityCodes.add(request.commdityCode);
            }
            List<Map<String, Object>> responses = queryServiceRemoteCall.queryCommodityByCodeBatch(commodityCodes);

            // 3、将结果响应 分发给每一个单独的用户请求。  由定时任务处理线程 --> 1000个用户的请求线程
            // [
            // {"code":"500",star: tony}
            // {"code":"600",star: tony}
            // ]
            HashMap<String, Map<String, Object>> responseMap = new HashMap<>();
            for (Map<String, Object> response : responses) {
                String code = response.get("code").toString();
                responseMap.put(code, response);
            }
            for (Request request : requests) {
                // 根据请求中携带的能表示唯一参数,去批量查询的结果中找响应
                Map<String, Object> result = responseMap.get(request.commdityCode);
                // 将结果返回到对应的请求线程
                request.future.complete(result);
            }
        }, 0, 10, TimeUnit.MILLISECONDS);
    }

    @Autowired
    QueryServiceRemoteCall queryServiceRemoteCall;

    // 1000 用户请求,1000个线程
    public Map<String, Object> queryCommodity(String movieCode) throws ExecutionException, InterruptedException {
        // 1000次 怎么样才能变成  更少的接口
        // 思路: 将不同用户的同类请求合并起来
        // 并非立刻发起接口调用,请求 收集起来,再进行
        Request request = new Request();
        request.commdityCode = movieCode;
        // 异步编程: 获取异步处理的结果
        CompletableFuture<Map<String, Object>> future = new CompletableFuture<>();
        request.future = future;
        queue.add(request);
        return future.get(); // 此处get方法,会阻塞线程运行,直到future有返回
        // 什么时候返回结果? 批量查询之后。 怎么进行等待
        // return queryServiceRemoteCall.queryMovieInfoByCode(movieCode);
    }


}
@Service
public class QueryServiceRemoteCall {

    /**
     * 调用远程的商品信息查询接口
     *
     * @param code 商品编码
     * @return 返回商品信息,map格式
     */
    public HashMap<String, Object> queryCommodityByCode(String code) {
        try {
            Thread.sleep(50L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        HashMap<String, Object> hashMap = new HashMap<>();
        hashMap.put("commodityId", new Random().nextInt(999999999));
        hashMap.put("code", code);
        hashMap.put("phone", "huawei");
        hashMap.put("isOk", "true");
        hashMap.put("price","4000");
        return hashMap;
    }

    /**
     * 批量查询 - 调用远程的商品信息查询接口
     *
     * @param codes 多个商品编码
     * @return 返回多个商品信息
     */
    public List<Map<String, Object>> queryCommodityByCodeBatch(List<String> codes) {
        // 不支持批量查询 http://moviewapi.com/query.do?id=10001     --> {code:10001, star:xxxx.....}
        // http://moviewapi.com/query.do?ids=10001,10002,10003,10004   --> [{code:10001, star///}, {...},{....}]
        List<Map<String, Object>> result = new ArrayList<>();
        for (String code : codes) {
            HashMap<String, Object> hashMap = new HashMap<>();
            hashMap.put("commodityId", new Random().nextInt(999999999));
            hashMap.put("code", code);
            hashMap.put("phone", "huawei");
            hashMap.put("isOk", "true");
            hashMap.put("price","4000");
            result.add(hashMap);
        }
        return result;
    }
}

弊端:

实行合并请求实际上是执行实际路基之前增加了延迟,如果平均需要5毫秒的执行时间,放在10毫秒做一次批处理的场景下,则最坏的情况可能变成15秒(不适合低延迟的RPC场景、低并发场景)

 

需要修改4个地方1. "appid": "",为自己的2. apSsid: "***", 3. apBssid: "***", 4. apPassword: "***", ESP32是一种集成Wi-Fi和蓝牙功能的低成本、低功耗的微控制器系统级芯片(SoC),广泛应用于物联网(IoT)领域。SmartConfig则是由Espressif Systems为ESP32开发的一种智能配网技术,可以简化设备的网络配置过程,用户通过手机的微信小程序便可以方便地将设备接入网络。微信小程序是一个不需要下载安装即可使用的应用,它实现了应用“触手可及”的梦想,用户扫一扫或搜一下即可打开应用。在实际应用ESP32与微信小程序进行配网操作时,需要对特定代码段进行修改以实现配网功能。需要修改的地方包括四个核心配置项:appid、apSsid、apBssid和apPassword。appid是一个应用的唯一标识,通常由微信平台分配,开发者需要将代码中的appid替换为自己的应用ID。apSsid是用于配网的热点名称,而apBssid则是该热点的物理地址,两者配合使用可帮助设备识别正确的网络。apPassword是配网过程中热点的密码,用户需要在微信小程序中输入这个密码来实现配网。使用SmartConfig配网技术的优势在于,它可以无需用户手动输入复杂的Wi-Fi信息,如SSID和密码,只需简单几步即可完成配网。在微信小程序界面中,用户只需点击“开始配网”按钮,设备便会发出配网请求。微信小程序会从服务器获取配网所需的配置信息,并将配网命令发送到目标设备。目标设备接收到配网命令后,即可自动连接到指定的Wi-Fi网络。这一过程极大降低了设备联网的门槛,提高了用户体验,对于非技术用户尤其友好。SmartConfig配网技术在智能家居、智慧城市建设等领域具有广泛的应用前景。它能够有效解决传统配网过程中的种种问题,如密码错误、网络不兼容等,从而让配网过程更加顺畅。ESP32作为智能配网的硬件基础,其内置的Wi-Fi功能使其能够轻松接收来自手机客户端的配网命令,并且能够支持蓝牙通信,这为开发者提供了更多灵活的配网方案选择。需要注意的是,在使用SmartConfig进行配网时,确保设备与微信小程序保持技术层面的兼容性是非常重要的。开发者在进行配网功能开发时,需要遵循相关技术标准和规范,保证配网流程的安全性和稳定性。此外,由于配网涉及到设备与外部通信,因此也需要考虑到网络安全的问题,采取适当的安全措施,比如加密通讯和防止恶意攻击等。随着物联网技术的快速发展,ESP32与微信小程序结合的SmartConfig配网技术将持续演进,为更多的智能设备提供方便快捷的联网方案。这种技术的进步将推动整个物联网生态系统的成熟,使得万物互联成为现实。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值