高性能API设计

news2024/11/19 9:39:02

背景

设计出一个高性能的API,需要综合网络、业务、数据库的优化。一下是我在实际的开发过程中总结的优化思想和一些效率提升的技巧。

批量思想

很多的数据库操作都含有batch或者bulk的api,如我最近常使用的mybatismybatis plus以及``elastic Search的数据操作API。从单条循环插入到批量插入,减少了session` 的链接,更高的效率。

# 优化前
for (User user: UserList) {
  mapper.insert(user);
}

# 优化后
mapper.batchInsert(UserList);

对应的实际sql为:

insert into user(id, name) values(1,2) 
insert into user(id, name) values(1,2) 
insert into user(id, name) values(1,2) ,(3,4), (5,6)

所以java的开发也有一种规范:禁止在循环中操作数据库

异步思想

对于服务的调用链路特别长的情况,接口的响应时间也特别的长。如果前端再去控制timeout的时间,直接出现接口超时的异常。于是异步的思想就出来了,允许耗时长的操作异步的执行。这类一般见于电商服务的业务流程中。

空间换时间

提到这个有点像算法了,空间复杂度和时间复杂度之间的一个权衡。这里的空间,指的是类似于redis的缓存 中间件。以查询数据为例:

池化思想

在这里不得不提到线程池了,这是多少人的噩梦!面试要问,项目要用,用不好服务直接搞挂!

我们常见的线程池类型有:数据库连接池、线程池、redis连接池

总结下来的功能有:

  • 避免线程的频繁创建和销毁
 Thread t = new Thread(() -> {System.out.println("hello world")});
 t.start();

瞧瞧这new的多恐怖。spring bean都交给容器管理了,线程还要单独new?来看看一段优雅的代码:

    @Override
    public int executeNotify() throws InterruptedException {
        // 获得需要通知的任务
        List<PayNotifyTaskDO> tasks = payNotifyTaskMapper.selectListByNotify();
        if (CollUtil.isEmpty(tasks)) {
            return 0;
        }

        // 遍历,逐个通知
        CountDownLatch latch = new CountDownLatch(tasks.size());
        tasks.forEach(task -> threadPoolTaskExecutor.execute(() -> {
            try {
                executeNotifySync(task);
            } finally {
                latch.countDown();
            }
        }));
        // 等待完成
        awaitExecuteNotify(latch);
        // 返回执行完成的任务数(成功 + 失败)
        return tasks.size();
    }

优雅在任务直接往池子里塞,具体的什么时候完成一直等着就好了。

  • 预分配
  • 循环使用

在这里,我也必须补充一下线程池的构造方法:

    /**
     * 用给定的初始参数创建一个新的ThreadPoolExecutor。
     */
    public ThreadPoolExecutor(int corePoolSize,//线程池的核心线程数量
                              int maximumPoolSize,//线程池的最大线程数
                              long keepAliveTime,//当线程数大于核心线程数时,多余的空闲线程存活的最长时间
                              TimeUnit unit,//时间单位
                              BlockingQueue<Runnable> workQueue,//任务队列,用来储存等待执行任务的队列
                              ThreadFactory threadFactory,//线程工厂,用来创建线程,一般默认即可
                              RejectedExecutionHandler handler//拒绝策略,当提交的任务过多而不能及时处理时,我们可以定制策略来处理任务
                               ) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

线程池的逻辑如下:

远程调用串行改并行

对于远程调用一系列接口,可以使用异步调用的方式,减少时间消耗。

为了实现以上的效果,我也结合线程池,用到了异步的方式模拟以上两种方式的效果。

  public int getCoreSize() {
    return Runtime.getRuntime().availableProcessors();
  }

  public static void someMethod(Long minutes) {
    try {
      Thread.sleep(minutes);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println(Thread.currentThread().getName() + " is running.....");
  }

  public void serial() {
    Date start = new Date();
    someMethod(1000L);
    someMethod(1000L);
    someMethod(300L);
    System.out.println(DateUtil.between(start, new Date(), DateUnit.MS));
  }

  public void parallel() {
    Date start = new Date();
    CompletableFuture<Void> completableFutureOne = CompletableFuture.runAsync(() -> someMethod(1000L), poolExecutor);
    CompletableFuture<Void> completableFutureTwo = CompletableFuture.runAsync(() -> someMethod(1000L), poolExecutor);
    CompletableFuture<Void> completableFutureThree = CompletableFuture.runAsync(() -> someMethod(300L), poolExecutor);
    try {
      // get()方法会阻塞线程
      CompletableFuture.allOf(completableFutureOne, completableFutureTwo, completableFutureThree).get();
    } catch (InterruptedException | ExecutionException e) {
      e.printStackTrace();
    }
    System.out.println(DateUtil.between(start, new Date(), DateUnit.MS));
    poolExecutor.shutdown();
  }

以上就是《高性能API设计》的第一部分了,时间和篇幅原因,剩下的部分将在下一期展开。

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

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

相关文章

怎么让表格中的一行数据 转置 为一列数据 (WPS )

例如 我现在有一列数据 我想要 变成一行 数据 1.首先选中想要转置的数据&#xff0c;然后control C 2.接着 点击你想放置数据的位置 右键 其实 关键是 找到 选择性复制 3. 找到转置&#xff0c;勾选 最后 确定 反之亦然

GD32F103VET输出PWM波形

GD32F103VET将TIMER0_CH3映射到PE14引脚&#xff0c;使其输出PWM波形。测试时&#xff0c;使用示波器看PE14引脚输出的波形&#xff0c;效果更直观。 TIMER0之PWM输出引脚映射如下: TIMER0_REMAP[1:0]"00"(没有映射): TIMER0_CH0默认被映射到PA8引脚 TIMER0_CH1默认…

计算机网络 深入理解HTTPS协议证书

文章目录 一、HTTPS协议二、对称加密三、非对称加密&对称加密(混合加密)三、加密证书四、HTTPS双刃性 一、HTTPS协议 之前介绍了HTTP协议&#xff0c;它给我们带来很大便利&#xff0c;但是也能看到他的不足。由于其本身通信使用明文&#xff0c;没有进行加密&#xff0c;…

MATLAB /Simulink 快速开发STM32(使用st官方工具 STM32-MAT/TARGET),以及开发过程

配置好环境以后就是开发&#xff1a; stm32cube配置芯片&#xff0c;打开matlab添加ioc文件&#xff0c;写处理逻辑&#xff0c;生成代码&#xff0c;下载到板子中去。 配置需要注意事项&#xff1a; STM32CUBEMAX6.5.0 MABLAB2022BkeilV5.2 Matlab生成的代码CTRLB 其中关键的…

Apache RocketMQ 命令注入

漏洞简介 RocketMQ 5.1.0及以下版本&#xff0c;在一定条件下&#xff0c;存在远程命令执行风险。RocketMQ的NameServer、Broker、Controller等多个组件外网泄露&#xff0c;缺乏权限验证&#xff0c;攻击者可以利用该漏洞利用更新配置功能以RocketMQ运行的系统用户身份执行命令…

java+springboot+mysql个人日记管理系统

项目介绍&#xff1a; 使用javaspringbootmysql开发的个人日记管理系统&#xff0c;系统包含超级管理员、管理员、用户角色&#xff0c;功能如下&#xff1a; 超级管理员&#xff1a;管理员管理&#xff1b;用户管理&#xff1b;反馈管理&#xff1b;系统公告&#xff1b;个人…

银河麒麟V10 wireshark安装说明(断网离线)

下载离线安装包 链接&#xff1a;https://pan.baidu.com/s/11QFRmCGlIJrJaiKcHh9Hag?pwdu9wv 提取码&#xff1a;u9wv 安装步骤 tar zxvf wireshark.tar.gz cd wireshark sudo dpkt -i *.deb wireshark

关于CORS的笔记

CORS目录 一、SpringBoot 跨域设置二、CORS&#xff08;1&#xff09;总结的图如下&#xff08;2&#xff09;简单请求满足的条件&#xff08;3&#xff09;响应头&#xff08;4&#xff09;请求头&#xff08;5&#xff09;使用XMLHttpRequest进行跨域访问1. Access-Control-A…

html学习9(脚本)

1、<script>标签用于定义客户端脚本&#xff0c;比如JavaScript&#xff0c;既可包含脚本语句&#xff0c;也可通过src属性指向外部文件。 2、JavaScript最常用于图片操作、表单验证及内容动图更新。 3、<noscript>标签用于在浏览器禁用脚本或浏览器不支持脚本&a…

Java课题笔记~ 关联映射

一、MyBatis关联查询 在关系型数据库中&#xff0c;表与表之间存在着3种关联映射关系&#xff0c;分别为一对一、一对多、多对多。 一对一&#xff1a;一个数据表中的一条记录最多可以与另一个数据表中的一条记录相关。列如学生与学号就属于一对一关系。 一对多&#xff1a;主…

学习gRPC (三)

测试gRPC例子 编写proto文件实现服务端代码实现客户端代码 通过gRPC 已经编译并且安装好之后&#xff0c;就可以在源码目录下找到example 文件夹下来试用gRPC 提供的例子。 在这里我使用VS2022来打开仓库目录下example/cpp/helloworld目录 编写proto文件 下面是我改写的exa…

领域驱动设计(六) - 架构设计浅谈

单用一篇文章很难把这个主题描述的清楚&#xff0c;但为了系列的完整性&#xff0c;笔者会围绕DDD中所介绍的内容做下初步总结&#xff0c;使读者有一个连续性。 一、概述 现在不是局部解决问题的时代了要运用新的技术创造新的效率提升&#xff0c;需要整个商业链条一起前进。…

粉末治金液压系统伺服阀控制器

粉末冶金液压系统是一种应用于粉末治金工艺的液压系统。该系统由液压泵、压力调节器、液压缸、液压管道、电气控制系统等组成。 该系统的优点包括&#xff1a; 工艺动作可靠&#xff1a;粉末冶金液压系统能够精确控制压力和流量&#xff0c;保证工艺动作的可靠性。 提高生产…

分布式存储系统中一致性与可用性核心实战

《高并发系统实战派》-- 你值得拥有 文章目录 副本的喜与忧什么是一致性和可用性&#xff1f;一致性与可用性的较量如何有效权衡&#xff0c;提高系统性能和稳定性&#xff1f;带入实际场景场景案例CAP BASE 双轮指导CAP指导BASE指导 副本的喜与忧 我们要知道&#xff0c;无…

CSDN竞赛68期题解

总结 近几期的题目质量有所提升&#xff0c;数据范围还是一如既往的没给。对于算法题&#xff0c;给定详细的数据范围&#xff0c;规范输入输出&#xff0c;再多给出几个样例以及样例说明&#xff0c;参赛的体验感才会提升。 题目列表 1.小球游戏 题目描述 某台有10个小球的…

[Python] Pylance 插件打开 Python 的类型检查

安装 Python 插件 2.打开一个 Python 文件 可以看到右下角有一个花括号和 Python 字样&#xff0c;点击花括号&#xff08;不是 Python 字样&#xff09;打开类型检查即可&#xff1a;

【问题随记】

ubuntu 14.04源更新(sources.list) deb http://mirrors.aliyun.com/ubuntu/ trusty main restricted universe multiverse deb http://mirrors.aliyun.com/ubuntu/ trusty-security main restricted universe multiverse deb http://mirrors.aliyun.com/ubuntu/ trusty-update…

echart图表X轴文字太长被隐藏标签解决方案

在Echart图标中&#xff0c;X轴的标签文字间隔默认是自动计算的&#xff0c;在标签文字长度太长的情况下&#xff0c;有可能标签会被隐藏掉&#xff0c;如图 这种显示显然是不符合严谨的业务需求。以下提供三种解决方案 第一种&#xff1a;竖排显示 效果&#xff1a; 在高度一…

汽车EBSE测试流程分析(四):反思证据及当前问题解决

EBSE专题连载共分为“五个”篇章。此文为该连载系列的“第四”篇章&#xff0c;在之前的“篇章&#xff08;三&#xff09;”中已经结合具体研究实践阐述了“步骤二&#xff0c;通过系统调研确定改进方案”等内容。那么&#xff0c;在本篇章&#xff08;四&#xff09;中&#…

web爬虫第五弹 - JS逆向入门(猿人学第一题)

0- 前言 爬虫是一门需要实战的学问。 而对于初学者来说&#xff0c;要想学好反爬&#xff0c;js逆向则是敲门砖。今天给大家带来一个js逆向入门实例&#xff0c;接下来我们一步一步来感受下入门的逆向是什么样的。该案例选自猿人学练习题。猿人学第一题 1- 拿到需求 进入页面…