在Java中使用协程 - 协程实践
本文最后更新于:2022年12月19日 晚上
1、前言
协程在2022年已经不是什么新鲜的东西,甚至可以说有一点烂大街了,但是最近在工作中真正地用上了协程,并且带来了很大的性能提升,这里做一个简单的分享。
其实在这段时间的工作中都在优化这个服务的性能(一个日常单机1000+QPS的应用),应该算并发比较高的服务了,虽然平均rt等指标看上去都很正常,但是P95 P99等都不那么好看,甚至有一点糟糕,一些服务的兜底率也偏高。
期间尝试过优化代码、调整JVM参数控制GC,虽然有一定的提升,但是P95 P99始终是一把悬着的利剑,有时候服务的最大响应时间也会彪上1s。
这个时候我们来分析一下这个服务:高并发、IO型,并没有太多的计算,那么这个服务的线程切换一定非常频繁,因为它不仅需要处理上游的请求,还要同时向下游发起调用,所以当时我想到了开启协程试一下,正巧公司JDK使用的都是支持协程的dragonwell,同时之前其他应用也有开启的前例,并没有出现任何问题。
2、Java如何使用协程
Java原生并不支持协程,但是阿里提供了支持协程的JDK:
阿里巴巴有着最丰富的Java应用场景,覆盖电商,金融,物流等众多领域,世界上最大的Java用户之一。 作为OpenJDK的下游, Alibaba Dragonwell是阿里巴巴内部OpenJDK定制版AJDK的开源版本, AJDK为在线电商,金融,物流做了结合业务场景的优化,运行在超大规模的,100,000+ 服务器的阿里巴巴数据中心。Alibaba Dragonwell是OpenJDK的下游(friendly fork),使用了和OpenJDK一样的licensing。阿里会更紧密地和OpenJDK等开源社区协作, 贡献更多的patches, 促进Java技术的持续发展。
dragonwell只支持linux和windows系统,所以使用macos的各位暂时不能在本机上进行调试(实际上应该也不需要)
这里就不赘述如何安装JDK了,dgragonwell有很多其他功能,我们这里主要介绍Wsip这个功能:
Wisp2 是在JVM层面实现的有栈对称式协程。相较于Kotlin、Kilim等字节码方案来说,Wisp在JDK阻塞接口上插入了调度支持,因此可以让现有Java应用无需改动地获得协程所带来的性能提升。
简单来说就是Wsip可以在无侵入的情况下实现Java的协程。
3、开启协程并调试
对于Java应用来说,并不是所有线程都适合开启协程的,像Netty、一些内核线程等都不太适合开启协程,我们可以通过设置白名单的方式将我们的一些业务线程转换为协程。
使用如下启动参数:
1 |
|
这样,我们可以让你的业务线程池、Dubbo业务线程池开启协程,实践中可以根据需要调整参数。
到这里其实我们就成功开启了协程,那么问题来了,怎么知道协程是否成功开启了呢?
答案是使用jstack,jstack等工具已经支持了协程。
服务运行后jstack pid > o.txt,我们用vi/vim搜索你自己的线程名,就可以得到如下结果:
1 |
|
- active表示协程被调度的次数
- steal表示work stealing发生的次数
- preempt 表示抢占的次数
如果下面的堆栈出现了Wsip的字样,那说明协程已经成功开启啦
4、性能
过程固然重要,但是如果性能不好那也只是徒劳。
Wsip官方提供的性能测试中:
1 |
|
1 |
|
可以看到对于线程切换频繁的应用,协程带来的提升是巨大的。
在实践中也印证了这个理论,在开启协程前一个上游应用的调用监控中:P95 P99大约都在250-500ms来回横跳(抖动),服务的尾请求非常的不稳定,开启协程后基本都稳定在200-250ms,几乎没有抖动,应用自己的监控,接口最大响应时间也几乎没有以前的抖动,可以说性能提升巨大。
5、其他
其实本篇文章基本没有什么技术含量,算是一个小小的日记,对于很多Java开发者来说,有的可能接触过其他语言,如Go的协程,但是大多数人实际上是没有在生产环境中实际使用过协程的。如果你也正在为一些性能问题所困扰,或许可以试试这款JDK,网络上很多人因为阿里的原因对这个JDK存在偏见,但是凡事都得真正使用一下,才能知道它到底几斤几两。
当然,协程也不是什么银弹,之前也有其他服务开启过协程,并没有带来什么收益,需要具体应用具体分析。
参考:
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!