背景

苏宁会员任务平台是覆盖聚合电商、体育、金融、PPTV、直播、红孩子等各个业态,平台会实时获取用户的画像信息来计算用户在客群中的分布及画像属性,从而实时判断用户是否满足相关场景下任务,若满足相关场景以后可以领取任务下所有奖项;任务类型包含了订单红包、母婴、Super会员、直播、双签、金融升级存等等。在大促特别是双十一期间,任务中心产品对于各个业态的引流,会员的留存及转化来说是一个重要的工具。

问题

因任务平台业务逻辑复杂、实时性要求高,涉及多个外围系统服务及数据调用;一期系统上线后部分功能遇到性能问题,例如聚合页打开时间过长,首先聚合页上要展示用户能看到的任务列表,以及当前用户是否达到领取条件,其次每个任务需要展示的状态依赖于后台多种信息的聚合,包括不在有效时间范围内、当前时段库存、可供领取的总库存、领取频次等。复杂逻辑和实时要求导致TPS在上线压测的时候没有能够达到一个理想预期效果。

即将到来的”双十一”流量高峰, 可以预见会使得超过现有的任务系统的TPS的峰值, 从而导致任务系统在”双十一”的场景下很容易触碰到性能瓶颈,影响用户体验;因此需要对苏宁任务平台的核心功能做性能优化, 提升实时性复杂业务逻辑场景下的性能, 以便于应对任务平台的流量暴涨以及双十一流量高峰。

定位

现有的每个任务可能依赖于多个异构系统的服务或者数据,例如直播任务及订单任务来自于不同的系统的服务,并且有些场景是基于外围系统的数据进行逻辑计算,有些则是通过服务接口调用的方式。

代码示例:

public ResultDTO checkAndGetInfo() {

A a = getA(); B b = getB();

ResultDTO result = computeResult(a, b, c ...);

C c = getC(); ...... return resultDTO;

}

由于页面实时性要求高,逻辑复杂,对于某个任务是否展示需要调用多个外围接口,响应时间不可控,理论上根据任务的复杂性可能涉及多个客群,调用次数及响应时间不可控。性能主要在响应时间不可控。

某个任务状态要调用多个本地接口或者外围接口。

主要思路:异步,缓存,线程池

针对以上定位到位问题,考虑到实时调用外围接口的方案会导致响应时间不可控,采用NIO的思想,对整个调用链进行梳理,尽量异步化调用,同时增加适当过期时间的缓存,达到性能优化的目的。

在一期设计的时候已经从业务逻辑的角度做了拆分,将不同生命周期的逻辑异步化处理,例如奖励是通过kafka推送到奖励资源系统异步发放的。

上述从业务生命周期角度分析,通过切分业务流程,达到优化的方式已经不能满足系统性能需求,需要从技术上考虑更细粒度的异步化处理方式。

优化方案的选择及演进

Kilim

Kilim是一个java的协程框架,利用字节码技术编织技术将普通代码转化为支持协程的代码,当时是基于同步的思路下,想利用协程优化同步并发处理的能力。经过调研业界实践应用相对较少,因此考虑到项目开发周期等因素,没有采用Kilim方案。

Guava Listenable Future

JDK 5引入了Future模式。 Future接口是Java多线程Future模式的实现,在java.util.concurrent包中,可以来进行异步计算。

Future模式是多线程设计常用的一种设计模式。Future模式可以理解成:有一个任务,提交给了Future,Future替我完成这个任务。期间我自己可以去做任何想做的事情。一段时间之后,我就便可以从Future那儿取出结果。

ExecutorService executor = ...;
Future f = executor.submit(...);
f.get();

Future接口可以构建异步应用,但依然有其局限性。它很难直接表述多个Future 结果之间的依赖性。实际开发中,我们经常需要达成以下目的:

  1. 将多个异步计算的结果合并成一个

  2. 等待Future集合中的所有任务都完成

  3. Future完成事件(即,任务完成以后触发执行动作)

Future虽然可以实现获取异步执行结果的需求,但是它没有提供通知的机制,我们无法得知Future什么时候完成。

要么使用阻塞,在future.get()的地方等待future返回的结果,这时又变成同步操作。要么使用isDone()轮询地判断Future是否完成,这样会耗费CPU的资源。

Guava的Listenable Future对其做了改进,支持注册一个任务执行结束后回调函数。

ListenableFuture<String> listenableFuture =
listeningExecutor.submit(new Callable<String>() {

@Override

return "";

public String call() throws Exception { }

});

其中FutureCallback是一个包含onSuccess(V),onFailure(Throwable)的接口:

Futures.addCallback(ListenableFuture, new FutureCallback<Object>() {

public void onSuccess(Object result) { // do something on success

// do something on failure

} public void onFailure(Throwable thrown) { }

});

这也是一开始试验的方案,确定好了异步化的思路,自然联想到了增强版的Listenable Future,虽然在任务完成时可以回调函数通知,但是仍然是阻塞的,主线程仍然要等待异步线程完成任务通知。

Completable Future

Java8的CompletableFuture参考了Guava的ListenableFuture的思路,CompletableFuture能够将回调放到与任务不同的线程中执行,也能将回调作为继续执行的同步函数,在与任务相同的线程中执行。它避免了传统回调最大的问题,那就是能够将控制流分离到不同的事件处理器中。

CompletableFuture弥补了Future模式的缺点。在异步的任务完成后,需要用其结果继续操作时,无需等待。可以直接通过thenAccept、thenApply、thenCompose等方式将前面异步处理的结果交给另外一个异步事件处理线程来处理。

CompletableFuture completableFuture = new CompletableFuture();

completableFuture.whenComplete(new BiConsumer() { @Override

}); // complete the taskcompletableFuture.complete(new Object());//api method

public void accept(Object o, Object o2) { //handle complete }

completableFuture.thenAccept(Consumer c); //api method
completableFuture.thenApply(Function f); //api method

CompletableFuture 提出了CompletionStage的概念,代表异步计算过程中的某一个阶段,一个阶段完成以后可能会触发另外一个阶段。

一个阶段的计算执行可以是一个Function,Consumer或者Runnable。比如:

stage.thenApply(x -> square(x)).thenAccept(x -> System.out.print(x)).thenRun(() -> System.out.println());

一个阶段的执行可能是被单个阶段的完成触发,也可能是由多个阶段一起触发。

与Guava ListenableFuture相比,CompletableFuture不仅可以在任务完成时注册回调通知,而且可以指定任意线程,实现了真正的异步非阻塞。

Servlet 3.0

传统Servlet 2.x web容器处理http请求时是为每一个请求分配一个线程,处理完请求再释放线程,如果请求处理的比较慢或者请求过多,就可能达到线程池达到上限,这时候后续的用户请求就会处于等待状态或者超时,这里用户请求和处理请求是一个线程,Servlet 3.0 开始提供了AsyncContext用来支持异步处理请求,主要是把请求线程和工作线程分开,将耗时的业务处理工作交给另外一个线程来完成。

@WebServlet(urlPatterns = "/servlet3",asyncSupported = true)

public class Servlet3 extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException { //在子线程中执行业务调用,并由其负责输出响应,主线程退出 AsyncContext ctx = request.startAsync();

public void doPost(HttpServletRequest request, HttpServletResponse response)

new Thread(new Executor(ctx)).start(); } throws ServletException, IOException { doGet(request, response); } } class Executor implements Runnable {

ServletRequest request = ctx.getRequest();

private AsyncContext ctx = null; public Executor(AsyncContext ctx){ this.ctx = ctx; } public void run(){ try { Thread.sleep(3000); ctx.dispatch("/index.jsp"); ctx.complete();

}

} catch (Exception e) { e.printStackTrace(); }

}

最终方案

最终选定Completable Future + Servlet 3.0的方案,前台web接口层采用Serlvet 3.0,后台服务层采用Completable Future。

验证

优化前压测数据:

图2:在访问聚合页200并发情况下的数据,TPS值3322,在用户并发量增加的时候,因依赖外部接口服务和原有的系统设计接口调用方法导致TPS基本不会随并发量的增加而提高。

优化后压测数据:

在访问聚合页100并发情况下的数据,TPS值5869,相对于优化之前的TPS有明显的提升。

在访问聚合页150并发情况下的数据TPS值8581,在提高并发量的时TPS有显著的提高,说明优化后的效果很明显,也证实了优化方案是可行的。

总结

利用异步化来提升系统性能是一个整体、全链路的工作,仅仅依靠业务上的异步化,或者服务层的异步化远远不够,随着不同技术方案的选择及演进,对异步非阻塞模型有了更深入的了解之后,从前台用户请求到后端服务层处理,根据一整条链路的上每一层场景的不同,需要选取不同的异步化技术方案,才能达到系统整体性能提升的目的。

原文发布时间为:2018-11-4

本文作者:葛苏杰

本文来自云栖社区合作伙伴“Java杂记”,了解相关信息可以关注“Java杂记”。

苏宁会员任务平台:基于异步化的性能优化实践相关推荐

  1. 基于 Nginx 的 HTTPS 性能优化实践

    前言 分享一个卓见云的较多客户遇到HTTPS优化案例. 随着相关浏览器对HTTP协议的"不安全".红色页面警告等严格措施的出台,以及向 iOS 应用的 ATS 要求和微信.支付宝小 ...

  2. 干货 | 提升50分,Trip.com 机票基于 PageSpeed 的前端性能优化实践

    作者简介 Patrick,携程资深前端开发工程师,专注于前端工程化和性能优化. 前言 网站性能对于用户体验.转化率和流失率.SEO 排名等至关重要,Trip.com 主要用户来自海外,对网站访问性能有 ...

  3. 苏宁11.11:如何基于异步化打造会员任务平台?

    本文为『InfoQ x 苏宁 2018 双十一』技术特别策划系列文章之一. 背景 苏宁会员任务平台是覆盖聚合电商.体育.金融.PPTV.直播.红孩子等各个业态,平台会实时获取用户的画像信息来计算用户在 ...

  4. 云知声 Atlas 超算平台: 基于 Fluid + Alluxio 的计算加速实践

    Fluid 是云原生基金会 CNCF 下的云原生数据编排和加速项目,由南京大学.阿里云及 Alluxio 社区联合发起并开源.本文主要介绍云知声 Atlas 超算平台基于 Fluid + Alluxi ...

  5. 企业机器学习可视化管理平台在信息 化项目管理中的实践研究——以某政务数据治理项目为例-4

    题目:企业机器学习可视化管理平台在信息 化项目管理中的实践研究--以某政务数据治理项目为例  文献引用:[1]王凯.企业机器学习可视化管理平台在信息化项目管理中的实践研究[D].昆明理工大学,2021 ...

  6. DTCC 2020 | 阿里云梁高中:DAS基于Workload的全局自动优化实践

    简介:第十一届中国数据库技术大会(DTCC2020),在北京隆重召开.在12.23日性能优化与SQL审计专场上,邀请了阿里巴巴数据库技术团队高级技术专家梁高中为大家介绍DAS之基于Workload的全 ...

  7. DTCC 2020 | 阿里云梁高中:DAS之基于Workload的全局自动优化实践

    简介: 第十一届中国数据库技术大会(DTCC2020),在北京隆重召开.在12.23日性能优化与SQL审计专场上,邀请了阿里巴巴数据库技术团队高级技术专家梁高中为大家介绍DAS之基于Workload的 ...

  8. 手机端html5 面试,今日头条 张祖俭 - H5动画在移动平台上的性能优化实践

    1.H5动画在移动平台上 的性能优化实践 今日头条 张祖俭 2.大纲 Part 1. H5动画 在移动平台上的性能问题 Part 2. 解决思路-从浏览器渲染入手 Part 3. 在H5Animato ...

  9. 多视图CAD检测系统乳腺X线摄影基于案例的检测性能优化

    多视图CAD检测系统乳腺X线摄影基于案例的检测性能优化 介绍 通常乳腺肿块的检测基于双视图乳腺摄影,医生在阅片是会将所有可用视图的信息组合在一起,他们比较MLO和CC视图,寻找不对称性并评估相对于先前 ...

最新文章

  1. python BeautifulSoup的简单使用
  2. linux 文件系统 启动,linux kernel文件系统启动部分
  3. 成功解决AttributeError: 'collections.defaultdict' object has no attribute 'iteritems'
  4. 给其他账户访问mysql的权限,将postgresql数据库的权限授予其他用户
  5. Android Nine-patch
  6. 令牌桶 限速_Go 限流器实战系列(2) Token Bucket 令牌桶
  7. 系统无法分配所需内存_Innodb内存管理解析
  8. 编程语言基础 c语言同步教案,C语言程序设计基实验教案.doc
  9. 雪花飞舞的java程序_【图片】请问大神帮我看看一段代码,老是提示空指针异常【java吧】_百度贴吧...
  10. MySQL8.0卸载教程
  11. 用html做工资查询登陆页面,薪资筛选页面.html
  12. AS/400开发经验点滴(六)如何制作下拉菜单
  13. sufficient statistic
  14. 计算机电源管理器怎么打开,联想电源管理软件打不开怎么办
  15. CSDN博客专家认证通过暨我的CSDN成长之路!
  16. 访问Windows 11恢复环境的5种简单方法
  17. Python读取MEIC文件(.nc格式及.asc格式)
  18. 学习Unity3D之探照灯效果和相机跟随。
  19. java连接access数据库的三种方式以及远程连接
  20. 教你如何搜索pois(兴趣点),制作可视化作品

热门文章

  1. article与section的区别
  2. 如何将Python文件转换为EXE
  3. 自己动手做Arduino玩具(一)
  4. WEB自动化测试面试题及答案大全
  5. 【强化学习论文合集】二十八.2020神经信息处理系统大会论文(NIPS2020)
  6. python _ 统计红楼梦人员姓名出现次数
  7. 上海证券交易所PBU升位历史
  8. 360儿童卫士2智能手表
  9. VirtualLab教程特辑
  10. 淘宝UWP--自定义图片缓存