接口优化方案总结第一弹

206 阅读5分钟
  • 1.批处理

    批量思想:批量操作数据库,这个很好理解,我们在循环插入场景的接口中,可以在批处理执行完成后一次性插入或更新数据库,避免多次IO。 批处理案例,创建一个文件夹并将一些文件移动到该文件夹中:

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;

public class BatchExample {
    public static void main(String[] args) {
        // 设置文件夹路径和文件扩展名
        String folderPath = "C:/Test";
        String extension = "txt";

        // 创建文件夹
        File folder = new File(folderPath);
        if (!folder.exists()) {
            folder.mkdir();
        }

        // 移动具有指定文件扩展名的文件到文件夹中
        File[] files = new File(".").listFiles((dir, name) -> name.toLowerCase().endsWith("." + extension));
        if (files != null) {
            for (File file : files) {
                try {
                    Path source = file.toPath();
                    Path destination = new File(folderPath, file.getName()).toPath();
                    Files.move(source, destination, StandardCopyOption.REPLACE_EXISTING);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        System.out.println("操作完成");
    }
}

**

这段代码使用Java的FileFiles类来创建文件夹和移动文件。首先,它设置了要创建的文件夹路径和文件的扩展名。然后,它通过File类创建了指定路径下的文件夹。接下来,使用File类的listFiles()方法获取当前目录下指定扩展名的文件列表。然后,使用Files类的move()方法将每个文件移动到刚创建的文件夹中。最后,输出一条消息表示操作完成。

2.异步处理

异步思想:针对耗时比较长且不是结果必须的逻辑,我们可以考虑放到异步执行,这样能降低接口耗时。

异步处理的案例,演示了如何使用CompletableFuture进行异步处理:

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class AsyncExample {
    public static void main(String[] args) {
        // 启动异步任务
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            try {
                // 模拟耗时操作
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "Hello, world!";
        });

        // 异步任务完成后的回调处理
        future.thenAccept(result -> {
            System.out.println("异步任务完成,结果为:" + result);
        });

        // 主线程继续执行其他操作
        System.out.println("正在执行其他操作...");

        try {
            // 阻塞等待异步任务完成
            future.get();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

**

上述代码中,使用CompletableFuture.supplyAsync()方法创建了一个异步任务,在任务中进行耗时操作并返回结果。然后,使用thenAccept()方法设置了异步任务完成后的回调处理,输出任务结果。接着,主线程继续执行其他操作,并通过future.get()方法阻塞等待异步任务的完成

3.空间换时间

一个很好理解的空间换时间 的例子是合理使用缓存,针对一些频繁使用且不频繁变更的数据,可以提前缓存起来,需要时直接查缓存,避免频繁地查询数据库或者重复计算。

需要注意的事,这里用了合理二字,因为空间换时间也是一把双刃剑,需要综合考虑你的使用场景,毕竟缓存带来的数据一致性问题也挺令人头疼。

这里的缓存可以是R2M,也可以是本地缓存、memcached,或者Map。

4.预处理

也就是预取思想,就是提前要把查询的数据,提前计算好,放入缓存或者表中的某个字段,用的时候会大幅提高接口性能。跟上面那个例子很像,但是关注点不同。

5.池化思想

我们都用过数据库连接池,线程池等,这就是池思想的体现,它们解决的问题就是避免重复创建对象或创建连接,可以重复利用,避免不必要的损耗,毕竟创建销毁也会占用时间。

池化思想的案例代码,它演示了如何使用对象池来重复使用对象,避免频繁创建和销毁对象的开销:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class ObjectPool<T> {
    private BlockingQueue<T> objectPool;

    public ObjectPool(int poolSize) {
        objectPool = new ArrayBlockingQueue<>(poolSize);

        for (int i = 0; i < poolSize; i++) {
            objectPool.offer(createObject());
        }
    }

    public T borrowObject() {
        try {
            return objectPool.take();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }

    public void returnObject(T object) {
        objectPool.offer(object);
    }

    private T createObject() {
        // 在这里进行对象的创建逻辑
        // 返回一个新的对象实例
        return null;
    }

    // 示例代码,模拟使用对象池的场景
    public static void main(String[] args) {
        ObjectPool<MyObject> objectPool = new ObjectPool<>(10);

        // 借用对象
        MyObject obj1 = objectPool.borrowObject();
        MyObject obj2 = objectPool.borrowObject();

        // 使用对象
        if (obj1 != null) {
            obj1.doSomething();
        }
        if (obj2 != null) {
            obj2.doSomething();
        }

        // 归还对象
        objectPool.returnObject(obj1);
        objectPool.returnObject(obj2);

        // 再次借用对象,此时应该是重复利用之前归还的对象
        MyObject obj3 = objectPool.borrowObject();
        MyObject obj4 = objectPool.borrowObject();

        // 使用对象
        if (obj3 != null) {
            obj3.doSomething();
        }
        if (obj4 != null) {
            obj4.doSomething();
        }
    }

    private static class MyObject {
        public void doSomething() {
            System.out.println("Doing something...");
        }
    }
}

**

在上述代码中,我们定义了一个名为ObjectPool的泛型类。它包含了一个阻塞队列objectPool来存储对象实例。在构造函数中,我们初始化了一个固定大小的对象池,并通过createObject()方法创建了一组对象实例放入池中。

borrowObject()方法从对象池中借用对象,它从队列中取出一个对象实例,并在需要时阻塞等待可用对象。

returnObject()方法用于将对象归还到对象池中,将对象实例放回队列中,以备后续重复使用。

main()方法是一个示例场景,展示了如何使用对象池借用和归还对象。在示例中,我们借用了两个对象obj1obj2,使用完毕后将它们归还到对象池中。然后,再次借用对象时,应该是重复利用之前归还的对象。

代码中的createObject()方法和MyObject类都需要根据实际需求来具体实现。createObject()方法用于创建对象的逻辑,可以根据需要添加新的实例化逻辑。MyObject是一个示例对象类,具体需要根据实际业务来定义和实现。

6.串行改并行

串行就是,当前执行逻辑必须等上一个执行逻辑结束之后才执行,并行就是两个执行逻辑互不干扰,所以并行相对来说就比较节省时间,当然是建立在没有结果参数依赖的前提下。