当前位置: 首页 > article >正文

服务端接口性能优化有哪些方案?

目录

使用单例

批量操作

使用Future模式

使用线程池

使用NIO

锁优化

压缩传输

缓存结果

SQL优化


作为一个Java后台开发,我们写出的大部分代码都决定着用户的使用体验。如果我们的后端代码性能不好,那么用户在访问我们的网站时就要浪费一些时间等待服务器的响应。这可能导致用户投诉甚至用户的流失。

关于性能优化是一个很大的话题。《Java程序性能优化》说性能优化包含五个层次:设计调优、代码调优、JVM调优、数据库调优、操作系统调优等。而每一个层次又包含很多方法论和最佳实践。本文不想大而广的概述这些内容。只是举几个常用的Java代码优化方案,读者看完之后可以真正的实践到自己代码中的方案。

使用单例

对于IO处理、数据库连接、配置文件解析加载等一些非常耗费系统资源的操作,我们必须对这些实例的创建进行限制,或者是始终使用一个公用的实例,以节约系统开销,这种情况下就需要用到单例模式。

批量操作

有100个请求,每个请求单独执行那肯定很慢,如果有办法把这个100个请求合并成一个请求,进行批量操作,那么效率就会高很多。 尤其是在数据库操作的时候,批量操作不仅比单条执行效率高,而且还能有效的降低数据库连接数,提升应用的QPS上限。

使用Future模式

假设一个任务执行起来需要花费一些时间,为了省去不必要的等待时间,可以先获取一个“提货单”,即Future,然后继续处理别的任务,直到“货物”到达,即任务执行完得到结果,此时便可以用“提货单”进行提货,即通过Future对象得到返回值。

public class RealData implements Callable<String> {
    protected String data;

    public RealData(String data) {
        this.data = data;
    }

    @Override
    public String call() throws Exception {
        //利用sleep方法表示真是业务是非常缓慢的
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return data;
    }
}

public class Application {
    public static void main(String[] args) throws Exception {
        FutureTask<String> futureTask = new FutureTask<>(new RealData("name"));
        ExecutorService executorFixedThreadPool = Executors.newFixedThreadPool(1); //使用线程池
        //执行FutureTask,相当于上例中的client.request("name")发送请求
        executor.submit(futureTask);

        //这里可以做一个耗时操作,比如处理其他业务逻辑
        //在处理这些业务逻辑过程中,RealData也在创建,从而充分了利用等待时间

        //使用真实数据
        //如果call()没有执行完成依然会等待
        System.out.println("数据=" + futureTask.get());
    }
}

使用线程池

合理利用线程池能够带来三个好处。第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配、调优和监控。

在 Java 5 之后,并发编程引入了一堆新的启动、调度和管理线程的API。Executor 框架便是 Java 5 中引入的,其内部使用了线程池机制,它在 java.util.cocurrent 包下,通过该框架来控制线程的启动、执行和关闭,可以简化并发编程的操作。 以下是Java代码实现:

public class MultiThreadTest {
    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 5, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new ThreadFactoryBuilder().setNameFormat("thread-%d");
        executor.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello world !");
            }
        });
        System.out.println("==> main Thread!");
    }
}

使用NIO

JDK自1.4起开始提供全新的I/O编译类库,简称NIO,其不但引入了全新高效的Buffer和Channel,同时还引入了基于Selector的非阻塞 I/O机制,将多个异步的I/O操作集中到一个或几个线程当中进行处理,使用NIO代替阻塞式I/O能提高程序的并发吞吐能力,降低系统的开销。

对于每一个请求,如果单独开一个线程进行相应的逻辑处理,当客户端的数据传递并不是一直进行,而是断断续续的,则相应的线程需要 I/O等待,并进行上下文切换。而使用NIO引入的Selector机制后,可以提升程序的并发效率,改善这一状况。 以下是Java代码实现:

public class NioTest {
    static public void main( String args [] ) throws Exception {
        FileInputStream fin = new FileInputStream("c:\\test.txt");
        // 获取通道
        FileChannel fc = fin.getChannel();
        // 创建缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        // 读取数据到缓冲区
        fc.read(buffer);
        buffer.flip();
        while (buffer.remaining()>0){
            byte b = buffer.get();
            System.out.print((char)b);
        }
        fin.close();
    }
}

锁优化

在开发场景中,我们的代码中经常会被锁所困扰。存在锁,就必然存在锁的竞争,存在锁的竞争,就会消耗很多资源。 那么,如何优化我们Java代码中的锁呢?主要可以从以下几个方面考虑:

  • 减少锁持有时间
    • 可以使用同步代码块来替代同步方法。这样可以减少锁持有的时间。
  • 减少锁粒度
    • 要在开发场景中使用Map的时候,记得使用ConcurrentHashMap来代替HashTable和HashMap。(CHM采用分段锁,锁的粒度会更小)
  • 锁分离
    • 普通锁(如synchronized)会导致读阻塞写、写也会阻塞读,同时读读与写写之间也会进行阻塞,可以想办法将读操作和写操作分开。
  • 锁粗化
    • 有些情况下我们希望把很多次锁的请求合并成一个请求,以降低短时间内大量锁请求、同步、释放带来的性能损耗。
  • 锁消除
    • 锁消除是Java虚拟机在JIT编译是,通过对运行上下文的扫描,去除不可能存在共享资源竞争的锁,通过锁消除,可以节省毫无意义的请求锁时间。

压缩传输

在进行数据传输之前,先将数据进行压缩,以减少网络传输的字节数,提升数据传输的速度,接收端将数据进行解压,还原出传递的数据,并且,经过压缩的数据还可以节约所耗费的存储介质(磁盘或内存)的空间以及网络带宽,降低成本。当然,压缩也不是没有开销的,数据压缩需要大量的CPU计算,并且,根据压缩算法的不同,计算的复杂度以及数据的压缩比也存在较大差异。一般情况下,需要根据不同业务场景,选择不同的压缩算法。

缓存结果

对于相同的用户请求,如果每次都重复的查询数据库,重复的进行计算,浪费很多的时间和资源。将计算后的结果缓存到本地内存,或者通过分布式缓存来进行结果的缓存,可以节约宝贵的CPU计算资源,减少重复的数据库查询或者是磁盘I/O,将原本磁头的物理转动变成内存的电子运动,提高响应速度,并且线程的迅速释放也使得应用的吞吐能力得到提升。

SQL优化

略...


http://www.kler.cn/a/308186.html

相关文章:

  • 《ElementPlus 与 ElementUI 差异集合》Icon 图标 More 差异说明
  • Jmeter基础篇(22)服务器性能监测工具Nmon的使用
  • react动态路由
  • 软件测试面试题(800道)【附带答案】持续更新...
  • ubuntu cmake CPack将第三方库进行打包
  • 券商隔夜单自动下单交易接口
  • 安卓在子线程中使用Toast
  • [Linux]:进程间通信(下)
  • vue2、vue3生成二维码
  • 算法 | 基础排序算法:插入排序、选择排序、交换排序、归并排序
  • xml重点笔记(尚学堂 3h)
  • 使用 GaLore 预训练LLaMA-7B
  • 72、结合无人机进行rk3588oak-lite跟踪目标物体进行识别、跟踪、保持距离
  • Java数据结构应用(力扣题20. 有效的括号)
  • Excel 基础知识-操作手册1
  • 2024/9/16 dataloader、tensorboard、transform
  • 三十八、Go-redis快速入门
  • Celery的使用
  • C语言-结构体-详解
  • 阿里云 Quick BI使用介绍
  • 【系统架构设计师-2014年真题】案例分析-答案及详解
  • HTTPX 与 AIOHTTP 与 Requests:选择哪个?
  • 【个人博客hexo版】hexo安装时会出现的一些问题
  • 【Qt笔记】QToolBox控件详解
  • STL相关简介
  • TeamTalk梳理概括