• Java高并发专题之27、实战:你的接口太慢了需要优化


    电商接口案例讲解

    电商app都有用过吧,商品详情页,需要给他们提供一个接口获取商品相关信息:

    1. 商品基本信息(名称、价格、库存、会员价格等)
    2. 商品图片列表
    3. 商品描述信息(描述信息一般是由富文本编辑的大文本信息)

    数据库中我们用了3张表存储上面的信息:

    1. 商品基本信息表:t_goods(字段:id【商品id】、名称、价格、库存、会员价格等)
    2. 商品图片信息表:t_goods_imgs(字段:id、goods_id【商品id】、图片路径),一个商品会有多张图片
    3. 商品描述信息表:t_goods_ext(字段:id,goods_id【商品id】、商品描述信息【大字段】)

    这需求对于大家来说很简单吧,伪代码如下:

    1. public Map<String,Object> detail(long goodsId){
    2. //创建一个map
    3. //step1:查询商品基本信息,放入map
    4. map.put("goodsModel",(select * from t_goods where id = #gooldsId#));
    5. //step2:查询商品图片列表,返回一个集合放入map
    6. map.put("goodsImgsModelList",(select * from t_goods_imgs where goods_id = #gooldsId#));
    7. //step3:查询商品描述信息,放入map
    8. map.put("goodsExtModel",(select * from t_goods_ext where goods_id = #gooldsId#));
    9. return map;
    10. }

    上面这种写法应该很常见,代码很简单,假设上面每个步骤耗时200ms,此接口总共耗时>=600毫秒,其他还涉及到网络传输耗时,估计总共会在700ms左右,此接口有没有优化的空间,性能能够提升多少?我们一起来挑战一下。

    在看一下上面的逻辑,整个过程是按顺序执行的,实际上3个查询之间是没有任何依赖关系,所以说3个查询可以同时执行,那我们对这3个步骤采用多线程并行执行,看一下最后什么情况,代码如下:

    1. package com.itsoku.chat26;
    2. import java.util.Arrays;
    3. import java.util.HashMap;
    4. import java.util.List;
    5. import java.util.Map;
    6. import java.util.concurrent.*;
    7. /**
    8. * 跟着阿里p7学并发,微信公众号:javacode2018
    9. */
    10. public class Demo1 {
    11. /**
    12. * 获取商品基本信息
    13. *
    14. * @param goodsId 商品id
    15. * @return 商品基本信息
    16. * @throws InterruptedException
    17. */
    18. public String goodsDetailModel(long goodsId) throws InterruptedException {
    19. //模拟耗时,休眠200ms
    20. TimeUnit.MILLISECONDS.sleep(200);
    21. return "商品id:" + goodsId + ",商品基本信息....";
    22. }
    23. /**
    24. * 获取商品图片列表
    25. *
    26. * @param goodsId 商品id
    27. * @return 商品图片列表
    28. * @throws InterruptedException
    29. */
    30. public List<String> goodsImgsModelList(long goodsId) throws InterruptedException {
    31. //模拟耗时,休眠200ms
    32. TimeUnit.MILLISECONDS.sleep(200);
    33. return Arrays.asList("图1", "图2", "图3");
    34. }
    35. /**
    36. * 获取商品描述信息
    37. *
    38. * @param goodsId 商品id
    39. * @return 商品描述信息
    40. * @throws InterruptedException
    41. */
    42. public String goodsExtModel(long goodsId) throws InterruptedException {
    43. //模拟耗时,休眠200ms
    44. TimeUnit.MILLISECONDS.sleep(200);
    45. return "商品id:" + goodsId + ",商品描述信息......";
    46. }
    47. //创建个线程池
    48. ExecutorService executorService = Executors.newFixedThreadPool(10);
    49. /**
    50. * 获取商品详情
    51. *
    52. * @param goodsId 商品id
    53. * @return
    54. * @throws ExecutionException
    55. * @throws InterruptedException
    56. */
    57. public Map<String, Object> goodsDetail(long goodsId) throws ExecutionException, InterruptedException {
    58. Map<String, Object> result = new HashMap<>();
    59. //异步获取商品基本信息
    60. Future<String> gooldsDetailModelFuture = executorService.submit(() -> goodsDetailModel(goodsId));
    61. //异步获取商品图片列表
    62. Future<List<String>> goodsImgsModelListFuture = executorService.submit(() -> goodsImgsModelList(goodsId));
    63. //异步获取商品描述信息
    64. Future<String> goodsExtModelFuture = executorService.submit(() -> goodsExtModel(goodsId));
    65. result.put("gooldsDetailModel", gooldsDetailModelFuture.get());
    66. result.put("goodsImgsModelList", goodsImgsModelListFuture.get());
    67. result.put("goodsExtModel", goodsExtModelFuture.get());
    68. return result;
    69. }
    70. public static void main(String[] args) throws ExecutionException, InterruptedException {
    71. long starTime = System.currentTimeMillis();
    72. Map<String, Object> map = new Demo1().goodsDetail(1L);
    73. System.out.println(map);
    74. System.out.println("耗时(ms):" + (System.currentTimeMillis() - starTime));
    75. }
    76. }

    输出:

    1. {goodsImgsModelList=[图1, 2, 3], gooldsDetailModel=商品id:1,商品基本信息...., goodsExtModel=商品id:1,商品描述信息......}
    2. 耗时(ms):208

    可以看出耗时200毫秒左右,性能提升了2倍,假如这个接口中还存在其他无依赖的操作,性能提升将更加显著,上面使用了线程池并行去执行3次查询的任务,最后通过Future获取异步执行结果。

    整个优化过程

    1. 先列出无依赖的一些操作
    2. 将这些操作改为并行的方式

    用到的技术有

    1. 线程池相关知识
    2. Executors、Future相关知识

    总结

    1. 对于无依赖的操作尽量采用并行方式去执行,可以很好的提升接口的性能
    2. 大家可以在你们的系统中试试这种方法,感受一下效果,会让你感觉很爽
    来源:http://itsoku.com/course/1/27
  • 相关阅读:
    线上故障排查 | 烂代码引发的血案
    源码分析系列 | 从零开始写MVC框架
    (原创干货)市场、销售-日常思考实战框架学习第三天
    (原创干货)企业管理-日常思考实战框架学习第二天
    (原创干货)人事管理-日常思考实战框架学习第一天
    带你认识物理机服务器(非原创)
    JSP高级功能-java之JSP学习第三天(非原创)
    常见指令与功能介绍-java之JSP学习第二天(非原创)
    JSP介绍与语法-java之JSP学习第一天(非原创)
    java之properties和yml两种配置文件比较(非原创)
  • 原文地址:https://www.cnblogs.com/konglxblog/p/16222379.html
Copyright © 2020-2023  润新知