前后端交互接口逻辑编排复盘

发布时间:2024年01月16日

一、背景

在笔者工作中遇到的前后端交互的项目中,后端处理并返回数据,前端进行显示数据时,会有核心数据和非核心数据的区别。对于核心数据应该强保障,而对于非核心数据则弱保障即可。

那么核心数据和非核心数据的处理方式和逻辑编排则是不同。刚步入职场时,最得心应手的写法就是一把梭。不管数据核心与否,全部是同步调用和无异常处理。

二、演进

观察

一把梭的写法

public class ToBShowQueryTest {
    //http的响应接口
    public static void main(String[] args) throws InterruptedException {
        String httpRes = getHttpRes();
        System.out.println(httpRes);
    }


    
    //服务编排接口
    public static String getHttpRes() throws InterruptedException {
        String codeData = queryCodeData();
        String nonCodeData1 = queryNonCodeData1();
        String nonCodeData2 = queryNonCodeData2();
        return codeData+"+"+nonCodeData1+"+"+nonCodeData2;
    }



    //原子能力查询接口
    public static String queryCodeData() throws InterruptedException {
        Thread.sleep(1500);
        return "核心数据";
    }

    public static String queryNonCodeData1() throws InterruptedException {
        Thread.sleep(3000);
        return "非核心数据1";
    }

    public static String queryNonCodeData2() throws InterruptedException {
        Thread.sleep(3000);
        return "非核心数据2";
    }

}

反思

一把梭写法的时序图如下:

其中查询核心数据需要1.5s,查询非核心数据1和非核心数据2则都需要3s,故一次http请求则需要7.5s。如果查询核心数据失败(例如超时异常)那么http请求就会被阻断,符合预期;如果查询非核心数据1或者非核心数据2失败,那么http请求也会被阻断,当前逻辑是不符合正常预期的。对于非核心的数据,在页面上的显示维度应该具备一定的容错。

行动&观察

对于非核心数据的处理

查询非核心数据1失败也不会阻断整个http的请求

public class ToBShowQueryTest {
    //http的响应接口
    public static void main(String[] args) throws InterruptedException {
        String httpRes = getHttpRes();
        System.out.println(httpRes);
    }



    //服务编排接口
    public static String getHttpRes() throws InterruptedException {
        String codeData = queryCodeData();
        String nonCodeData1 = "";
        try {
             nonCodeData1 = queryNonCodeData1();
        }catch (Exception e){
            Log.error("查询非核心数据1失败");
        }
        String nonCodeData2 = "";
        try {
            nonCodeData2 = queryNonCodeData2();
        }catch (Exception e){
            Log.error("查询非核心数据2失败");
        }
        return codeData+"+"+nonCodeData1+"+"+nonCodeData2;
    }



    //原子能力查询接口
    public static String queryCodeData() throws InterruptedException {
        Thread.sleep(1500);
        return "核心数据";
    }

    public static String queryNonCodeData1() throws InterruptedException {
        Thread.sleep(3000);
        if (true){
            throw new RuntimeException("抛出异常");
        }
        return "非核心数据1";
    }

    public static String queryNonCodeData2() throws InterruptedException {
        Thread.sleep(3000);
        return "非核心数据2";
    }

}

反思

页面的HTTP请求时间对于用户体验的影响通常不是孤立考虑的,而是结合整个页面加载过程中的各个阶段来评估。根据上述信息和业界公认的最佳实践:

  1. 首次渲染时间(即用户能够看到内容开始显示的时间)如果能在1秒内完成,可以提供良好的用户体验。
  2. 首屏加载时间(即用户第一眼能看到的内容完全加载完毕的时间),若能控制在1.5秒以内,则被百度搜索等研究认为会带给用户流畅快捷的体验。

在上述优化之后的一把梭写法中,http请求的显示已经排除了非核心数据的影响。但是查询性能依然没有任何提升,依然至少需要7.5s的查询时间。页面端查询理论上能够接受的最长时间也就1.5s,才处于用户的体验良好区,每新增1s,体验都会呈现指数下降。

根据上述的结论,非核心的数据在页面上的显示维度应该具备一定的容错。那么是否可以对非核心数据使用异步线程查询数据,即可大幅度降低查询时间。即http请求时间 = 核心数据查询时间 + 非核心数据查询的最大时间 = 1.5 + 3 = 4.5s

时序图如下

行动

异步查询非核心数据

public class ToBShowQueryTest {
    //http的响应接口
    public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
        String httpRes = getHttpRes();
        System.out.println(httpRes);
    }



    //服务编排接口
    public static String getHttpRes() throws InterruptedException, ExecutionException, TimeoutException {
        StringBuilder codeData = new StringBuilder(queryCodeData());

        List<Future<String>> futureList = new ArrayList<>();

        CompletableFuture<String> nonCodeData1Future = CompletableFuture.supplyAsync(() -> {
            String nonCodeData1 = "";
            try {
                nonCodeData1 = queryNonCodeData1();
            } catch (Exception e) {
                Log.error("查询非核心数据1失败");
            }
            return nonCodeData1;
        });
        futureList.add(nonCodeData1Future);


        CompletableFuture<String> nonCodeData2Future = CompletableFuture.supplyAsync(() -> {
            String nonCodeData2 = "";
            try {
                nonCodeData2 = queryNonCodeData2();
            } catch (Exception e) {
                Log.error("查询非核心数据2失败");
            }
            return nonCodeData2;
        });
        futureList.add(nonCodeData2Future);

        //此处获取结果有两种形式
        //方法1:等待查询非核心数据1异步任务和查询非核心数据2异步任务都执行完成,然后获取数据
        //当前获取方式的http请求时间 = 核心数据查询时间 + 非核心数据查询的最大时间 = 1.5 + 3 = 4.5s (最少需要4.5s,rpc请求有波动)
//        for (Future<String> stringFuture : futureList) {
//            String strRes = stringFuture.get();
//            codeData.append("+").append(strRes);
//        }

        //方法2:查询非核心数据1异步任务和查询非核心数据2异步任务都等待3秒,然后获取数据
        //当前获取方式的http请求时间 = 核心数据查询时间 + 非核心数据查询的等待结果时间 = 1.5 + 3 = 4.5s
        for (Future<String> stringFuture : futureList) {
            String strRes = "";
            try {
                strRes = stringFuture.get(3 , TimeUnit.SECONDS);
            }catch (Exception e){
                Log.error("非核心数据查询超时");
            }
            codeData.append("+").append(strRes);
        }

        return codeData.toString();
    }



    //原子能力查询接口
    public static String queryCodeData() throws InterruptedException {
        Thread.sleep(2000);
        return "核心数据";
    }

    public static String queryNonCodeData1() throws InterruptedException {
        //4s时间用于测试,等待时间超过限制等待时间(3s),
        //stringFuture.get(3 , TimeUnit.SECONDS)方法会抛出异常,需要用异常捕获
        Thread.sleep(4000);
        //异常用于测试非核心数据查询异常捕获不影响主数据
//        if (true){
//            throw new RuntimeException("抛出异常");
//        }
        return "非核心数据1";
    }

    public static String queryNonCodeData2() throws InterruptedException {
        Thread.sleep(3000);
        return "非核心数据2";
    }

}

文章来源:https://blog.csdn.net/weixin_47552725/article/details/135626017
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。