Java接口异步调用优化技巧详解

 更新时间:2023年05月06日 11:30:53   作者:浮生夢  
本文详细介绍了在Java开发中,如何通过异步调用等技巧来优化接口的性能,有效避免阻塞和提高并发处理能力,提升系统的稳定性和响应速度

在日常项目中,我们经常采用多线程异步调用的方式来提高接口的响应时间。

在实际情况下,我们如何通过异步方式优化我们的接口呢,有以下几种常见思路

1,自己new线程或者线程池

如下我们把三个耗时操作交给新的线程或者线程池执行。

当请求过来的时候tomcat线程会等待子线程全部执行完成,然后汇总结果进行返回。

 /**
     * 这里会阻塞tomcat的线程
     */
    @GetMapping("getAllEgOne")
    public Map<String, Object> getAllEgOne() throws ExecutionException, InterruptedException {
        FutureTask<String> stringFutureTaskOne = new FutureTask<>(asyncService::getOne);
        FutureTask<String> stringFutureTaskTwo = new FutureTask<>(asyncService::getTwo);
        FutureTask<String> stringFutureTaskThree = new FutureTask<>(asyncService::getThree);
        new Thread(stringFutureTaskOne).start();
        new Thread(stringFutureTaskTwo).start();
        new Thread(stringFutureTaskThree).start();
        Map<String, Object> result = new HashMap<>();
        result.put("one", stringFutureTaskOne.get());
        result.put("two", stringFutureTaskTwo.get());
        result.put("three", stringFutureTaskThree.get());
        return result;
    }

2,Sping Mvc

我们返回一个Callable 这时候会开启一个新的线程不会阻塞tomcat的线程

 /**
     * 这里不会阻塞tomcat的线程
     */
    @GetMapping("getAllEgTwo")
    public Callable<Map<String, Object>> getAllEgTwo() {
        return () -> {
            FutureTask<String> stringFutureTaskOne = new FutureTask<>(asyncService::getOne);
            FutureTask<String> stringFutureTaskTwo = new FutureTask<>(asyncService::getTwo);
            FutureTask<String> stringFutureTaskThree = new FutureTask<>(asyncService::getThree);
            new Thread(stringFutureTaskOne).start();
            new Thread(stringFutureTaskTwo).start();
            new Thread(stringFutureTaskThree).start();
            Map<String, Object> result = new HashMap<>(3);
            result.put("one", stringFutureTaskOne.get());
            result.put("two", stringFutureTaskTwo.get());
            result.put("three", stringFutureTaskThree.get());
            return result;
        };
    }

3,修改单个任务为批量任务

在项目中我们有很多数据库的查询,批量查询要快于单个查询,中间省了很多io操作。

思考能不能吧单个调用转换成批量呢,针对并发比较高的接口。合并多个用户的调用,转换成一批进行查询。

把一个时间段内的请求放进队列,然后通过定时任务进行批量查询,然后进行响应分发。

import com.example.demo.conf.SnowFlake;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
/**
 * @author liwenchao
 */
@RestController
@RequestMapping("async")
@Slf4j
public class AsyncController {
    @Autowired
    private AsyncService asyncService;
    private final SnowFlake worker = new SnowFlake(1, 1, 1);
    private final LinkedBlockingQueue<RequestBody<Long, UserInfo>> queue = new LinkedBlockingQueue<>();
    @PostConstruct
    public void doWork() {
        ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
        service.scheduleAtFixedRate(() -> {
            //每次运行的时候 去拿MQ中的数据量
            int size = queue.size();
            if (size == 0) {
                return;
            }
            log.info("批量获取任务:{}-{}", Thread.currentThread().getName(), size);
            //多次请求收集到一起一块去批量请求下面的需要的数据
            List<Long> requestBodyList = new ArrayList<>();
            List<RequestBody<Long, UserInfo>> requestBodies = new ArrayList<>();
            for (int i = 0; i < size; i++) {
                RequestBody<Long, UserInfo> requestBody = queue.poll();
                requestBodies.add(requestBody);
                Long requestParam = requestBody.getRequestParam();
                requestBodyList.add(requestParam);
            }
            List<UserInfo> fourBatch;
            try {
                fourBatch = asyncService.getFourBatch(requestBodyList);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if (CollectionUtils.isEmpty(fourBatch)) {
                return;
            }
            for (UserInfo x : fourBatch) {
                for (RequestBody<Long, UserInfo> y : requestBodies) {
                    if (x.getId().equals(y.getRequestParam())) {
                        y.getResult().complete(x);
                        break;
                    }
                }
            }
        }, 1000L, 50L, TimeUnit.MILLISECONDS);
    }
    /**
     * ● 插入
     * 1.add(e):当阻塞队列满时,再往队列里add插入元素会抛IllegalStateException:Queue full
     * 2.offer(e):插入方法,成功true失败返回false
     * 3.put(e):当阻塞队列满时,生产者线程继续往队列里添加元素,队列会一直阻塞生产者线程。直到put数据or响应中断退出
     * 4.offer(e,time,unit):当阻塞队列满的时候,队列会阻塞生产者线程一定时间,超过限时后生产者线程会退出。
     * <p>
     * ● 移除
     * 1.remove():当队列为空的时候,再往队列里remove移除元素会抛NoSuchElementException
     * 2.poll():移除方法,成功返回出队列的元素,队列里没有就返回null。
     * 3.take():当队列为空消费者线程试图从队列里take元素,队列会一直阻塞消费者线程知道队列可用
     * 4.poll(time,unit):当队列为空的时候,会阻塞一段时间超时后消费者线程退出。
     * <p>
     * ● 检查
     * 1.element():当队列为空时直接抛出异常
     * 2.peek():当队列为空时阻塞
     * <p>
     * 这里不会阻塞tomcat的线程
     */
    @GetMapping("getAllEgFour")
    public UserInfo getAllEgFour(Long userId) throws ExecutionException, InterruptedException {
        if (userId == null) {
            userId = worker.nextId();
        }
        log.info("开始获取数据: {}: {}", Thread.currentThread().getName(), userId);
        RequestBody<Long, UserInfo> objectObjectRequestBody = new RequestBody<>();
        CompletableFuture<UserInfo> completableFuture = new CompletableFuture<>();
        objectObjectRequestBody.setRequestParam(userId);
        objectObjectRequestBody.setResult(completableFuture);
        queue.add(objectObjectRequestBody);
        UserInfo userInfo = completableFuture.get();
        log.info("完成获取数据: {}: {}", Thread.currentThread().getName(), userInfo);
        return userInfo;
    }
}

到此这篇关于Java接口异步调用优化技巧详解的文章就介绍到这了,更多相关Java接口异步调用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Sentinel的熔断降级、资源规则详解与实例

    Sentinel的熔断降级、资源规则详解与实例

    这篇文章主要介绍了Sentinel的熔断降级、资源规则详解与实例,Sentinel是阿里巴巴开源的一款流量控制和熔断降级的框架,它主要用于保护分布式系统中的服务稳定性,Sentinel通过对服务进行流量控制和熔断降级,可以有效地保护系统的稳定性,需要的朋友可以参考下
    2023-09-09
  • multi-catch和try-catch异常处理知识点详解

    multi-catch和try-catch异常处理知识点详解

    在本篇文章里我们给大家分享了一篇关于multi-catch和try-catch异常处理知识点内容,有需要的朋友们可以参考学习下。
    2019-11-11
  • Spring Lifecycle 和 SmartLifecycle区别面试精讲

    Spring Lifecycle 和 SmartLifecycle区别面试精讲

    这篇文章主要为大家介绍了Spring Lifecycle和SmartLifecycle的区别面试精讲,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-10-10
  • Java简单使用redis-zset实现排行榜

    Java简单使用redis-zset实现排行榜

    这篇文章主要介绍了Java简单使用redis-zset实现排行榜,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • 浅谈java泛型的作用及其基本概念

    浅谈java泛型的作用及其基本概念

    下面小编就为大家带来一篇浅谈java泛型的作用及其基本概念。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-08-08
  • SpringBoot利用随机盐值实现密码的加密与验证

    SpringBoot利用随机盐值实现密码的加密与验证

    这篇文章主要为大家详细介绍了SpringBoot如何利用随机盐值实现密码的加密与验证,文中的示例代码讲解详细,有需要的小伙伴可以参考下
    2024-02-02
  • 利用Spring IOC技术实现用户登录验证机制

    利用Spring IOC技术实现用户登录验证机制

    这篇文章主要为大家详细介绍了Spring IOC技术实现用户登录验证机制的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-10-10
  • Spring中BeanFactory解析bean详解

    Spring中BeanFactory解析bean详解

    本篇文章主要介绍了Spring中BeanFactory解析bean详解 ,详细的介绍了使用BeanFactory对bean进行解析的实例,有兴趣的可以了解一下。
    2017-04-04
  • Java基础之浅谈hashCode()和equals()

    Java基础之浅谈hashCode()和equals()

    今天给大家带来的是关于Java基础的相关知识,文章围绕着hashCode()和equals()展开,文中有非常详细的介绍及代码示例,需要的朋友可以参考下
    2021-06-06
  • 深入探究SpringBoot中的Elasticsearch自动配置原理及用法

    深入探究SpringBoot中的Elasticsearch自动配置原理及用法

    SpringBoot中的Elasticsearch自动配置为我们提供了一种快速集成Elasticsearch的方式,使我们可以在SpringBoot应用程序中轻松地使用Elasticsearch,本文将介绍Spring Boot中的Elasticsearch自动配置的作用、原理和使用方法
    2023-07-07

最新评论