minIO分页请求maxKeys无效的问题

1. 背景

我在用minIO sdk(版本号:8.5.17)做开发,需要遍历s3服务的对象列表,希望分页请求。

按照官方API设置了maxKeys,但是发现却返回了全部。

代码简述如下:

java 复制代码
    public static List<String> getObjectsByPage(String bucketName, int pageSize) throws Exception{
     
            Iterable<Result<Item>> results = minioClient.listObjects(
                    ListObjectsArgs.builder()
                            .delimiter("/")
                            .bucket(bucketName)
                            .startAfter(null)
                            .prefix("test/a1")
                            .maxKeys(5)
                            .build());
            Iterator<Result<Item>> iterator = results.iterator();
            int count = 0;
            while (iterator.hasNext()) {
                Result<Item> next = iterator.next();
                count++;
            }
          
    


        return pageItems;
    }

结果是count是遍历了全部的值。

2. 分析

github上有人提到:

https://github.com/minio/minio/discussions/18752

再加上,看官方API的例子时,发现有这么个说法:

java 复制代码
Lazy iterator...

https://docs.min.io/enterprise/aistor-object-store/developers/sdk/java/api/#listObjects

所以,猜想是每次调用hasnext时去请求数据,倘若我在这段代码前没有制止,就会在本次拉去数据结束后,自动去拉取下一批。

因此,实现思路:

  1. 在hasnext前,制止执行

  2. 制止执行的方式可采用计数(变量A),计获取到的数据,达到一定数值终止遍历

  3. 基于2,会出现A的数值大于当前分页的数据总数,导致执行下一次请求被触发,因此设定A为pageSize的值

  4. 使用日志来查看请求进行验证

3. 验证

将上边的代码进行改造,有如下代码:

java 复制代码
public static List<String> getObjectsByPage(String bucketName, int pageSize) throws Exception{
        List<String> pageItems = new ArrayList<>();
        String nextName = null;
        while (true) {
            Iterable<Result<Item>> results = minioClient.listObjects(
                    ListObjectsArgs.builder()
                            .delimiter("/")
                            .bucket(bucketName)
                            .startAfter(nextName)
                            .prefix("test/a1")
                            .maxKeys(5)
                            .build());
            Iterator<Result<Item>> iterator = results.iterator();
            int count = 0;
            while (count < pageSize && iterator.hasNext()) {
                Result<Item> next = iterator.next();
                count++;
                nextName = next.get().objectName();
            }
            if (count <= 0) {
                break;
            }
        }


        return pageItems;
    }

其中,client创建时指定了httpClient为OKHttpClient,并给httpClient设置了interceptor。

设置client使用OKHttp

java 复制代码
       Logger.getLogger(OkHttpClient.class.getName()).setLevel(Level.FINE);
        // 2. 配置OkHttp的Dispatcher(控制并发请求)
        Dispatcher dispatcher = new Dispatcher(executorService);
        dispatcher.setMaxRequests(100);         // 最大并发请求数
        dispatcher.setMaxRequestsPerHost(20);   // 每个主机的最大并发请求数

        OkHttpClient httpClient = (new OkHttpClient()).newBuilder()
                .addInterceptor(new MinioOkHttpLogInterceptor("s3-qos.ttt.com"))
                .dispatcher(dispatcher)
                .connectTimeout(5, TimeUnit.SECONDS).writeTimeout(5, TimeUnit.SECONDS).readTimeout(5, TimeUnit.SECONDS).protocols(Arrays.asList(Protocol.HTTP_1_1)).build();


        MinioAsyncClient.Builder builder = MinioAsyncClient.builder()
                .endpoint(config.getEndpoint())
                .httpClient(httpClient)
                .credentials(config.getAccessKey(), config.getSecretKey())
                .region(config.getRegion());

OKHttp的拦截器

java 复制代码
/**
 * 自定义 OkHttp 日志拦截器,仅打印 MinIO 相关请求
 */
public class MinioOkHttpLogInterceptor implements Interceptor {
    private static final Logger logger = LoggerFactory.getLogger(MinioOkHttpLogInterceptor.class);
    private String regex;

    public MinioOkHttpLogInterceptor(String regex) {
        this.regex = regex;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        // 1. 捕获请求信息(发送前)
        Request request = chain.request();
        long startTime = System.currentTimeMillis();

        // 只打印 MinIO 相关请求(过滤其他无关请求)
        if (request.url().host().contains(regex)) {
            logger.info("===== MinIO 请求开始 =====");
            logger.info("请求方法:{}", request.method());
            logger.info("请求URL:{}", request.url());
            logger.info("请求头:{}", request.headers());
            // 若有请求体(如 POST/PUT),可打印 body(注意:body 只能读取一次)
            // logger.info("请求体:{}", request.body() != null ? request.body().toString() : "无");
        }

        // 2. 执行请求
        Response response = chain.proceed(request);

        // 3. 捕获响应信息(返回后)
        long endTime = System.currentTimeMillis();
        long duration = endTime - startTime;
        ResponseBody responseBody = response.peekBody(1024 * 1024); // 读取响应体(不消耗)

        // 只打印 MinIO 相关响应
        if (request.url().host().contains(regex)) {
            logger.info("响应状态码:{}", response.code());
            logger.info("响应耗时:{}ms", duration);
            logger.info("响应体:{}", responseBody.string());
            logger.info("===== MinIO 请求结束 =====\n");
        }

        return response;
    }

观察

每一次'1'在输出前,都有一次请求发生。

说明验证结果为猜想正确。

4. 解决方案

验证中已经写明,不赘述了。

相关推荐
青云计划6 小时前
知光项目知文发布模块
java·后端·spring·mybatis
赶路人儿6 小时前
Jsoniter(java版本)使用介绍
java·开发语言
探路者继续奋斗7 小时前
IDD意图驱动开发之意图规格说明书
java·规格说明书·开发规范·意图驱动开发·idd
消失的旧时光-19438 小时前
第十九课:为什么要引入消息队列?——异步系统设计思想
java·开发语言
A懿轩A8 小时前
【Java 基础编程】Java 面向对象入门:类与对象、构造器、this 关键字,小白也能写 OOP
java·开发语言
乐观勇敢坚强的老彭8 小时前
c++寒假营day03
java·开发语言·c++
biubiubiu07068 小时前
谷歌浏览器无法访问localhost:8080
java
大黄说说9 小时前
新手选语言不再纠结:Java、Python、Go、JavaScript 四大热门语言全景对比与学习路线建议
java·python·golang
烟沙九洲9 小时前
Java 中的 封装、继承、多态
java
识君啊9 小时前
SpringBoot 事务管理解析 - @Transactional 的正确用法与常见坑
java·数据库·spring boot·后端