Go语言出现后,Java仍是最佳选择吗?

点击这里,查看异步与协程的关系,手工异步/Wisp性能比较,适应的Workload等更多重要内容java

简介: 阿里妹导读:随着大量新生的异步框架和支持协程的语言(如Go)的出现,在不少场景下操做系统的线程调度成为了性能的瓶颈,Java也所以被质疑是否再也不适应最新的云场景了。4年前,阿里JVM团队开始自研Wisp2,将Go语言的协程能力带入到Java世界。
imageweb

Java平台一直以生态的繁荣著称,大量的类库、框架帮助开发者们快速搭建应用。而其中大部分Java框架类库都是基于线程池以及阻塞机制来服务并发的,主要缘由包括:服务器

Java语言在核心类库中提供了强大的并发能力,多线程应用能够得到不俗的性能;
Java EE的一些标准都是线程级阻塞的(好比JDBC);
基于阻塞模式能够快速地开发应用。
但现在,大量新生的异步框架和支持协程的语言(如Go)的出现,在不少场景下操做系统的线程调度成为了性能的瓶颈。Java也所以被质疑是否再也不适应最新的云场景了。多线程

4年前,阿里开始自研Wisp2。它主要是用在IO密集的服务器场景,大部分公司的在线服务都是这样的场景 (离线应用都是偏向于计算,则不适用)。它在功能属性上对标Goroutine的Java协程,在产品形态、性能、稳定性上都达到了一个比较理想的状况。到如今,已经有上百个应用,数万个容器上线了Wisp1/2。Wisp协程彻底兼容多线程阻塞的代码写法,仅需增长JVM参数来开启协程,阿里巴巴的核心电商应用已经在协程模型上通过两个双十一的考验,既享受到了Java的丰富生态,又得到了异步程序的性能。并发

Wisp2主打的是性能和对现有代码的兼容性,简而言之,现有的基于多线程的IO密集的Java应用只须要加上Wisp2的JVM参数就能够得到异步的性能提高。框架

做为例子,如下是消息中间件代理(简称mq)和drds只添加参数不改代码的压测比较:curl

image

能够看到上下文切换以及sys CPU显著下降,RT减小、QPS分别提高11.45%,18.13%。异步

Quick Start
因为Wisp2彻底兼容现有的Java代码,所以使用起来十分简单,有多简单?oop

若是你的应用是“标准”的在线应用(使用/home/admin/$APP_NAME/setenv.sh配置参数),那么在admin用户下输入以下命令就能够开启Wisp2了:性能

curl https://gosling.alibaba-inc.c... | sh

不然须要手动升级JDK和Java参数:

ajdk 8.7.12_fp2 rpm

sudo yum install ajdk -b current # 也能够经过yum安装最新jdk
java -XX:+UseWisp2 .... # 使用Wisp参数启动Java应用

而后就能够经过jstack验证协程确实被开启了。

Carrier线程是调度协程的线程,下方的- Coroutine [...]表示一个协程,active表示协程被调度的次数,steal表示被work stealing的次数,preempt表示时间片抢占次数。

image

下图是DRDS在ecs上压测时的top -H,能够看出来应用的数百个线程被8个Carrier线程托管,均匀地跑在CPU核数个线程上面。下方一些名为java的线程是gc线程。

image

过多线程的开销
误区1: 进内核引起上下文切换

咱们看一段测试程序:

pipe(a);
while (1) {
  write(a[1], a, 1);
  read(a[0], a, 1);
  n += 2;
}

image
执行这段程序时上下文切换很是低,实际上上面的IO系统调用都是不会阻塞的,所以内核不须要挂起线程,也不须要切换上下文,实际发生的是用户/内核态的模式切换。

上面的程序在神龙服务器测得每一个pipe操做耗时约334ns,速度很快。

误区2: 上下文切换的开销很大

本质上来讲不管是用户态仍是内核态的上下文切换都是很轻量的,甚至有一些硬件指令来支持,好比pusha能够帮助咱们保存通用寄存器。同一个进程的线程共享页表,所以上下文切换的开销通常只有:

保存各类寄存器
切换sp(call指令会自动将pc压栈)
能够在数十条指令内完成。

开销

既然近内核以及上下文切换都不慢,那么多线程的开销究竟在哪?

咱们不妨看一个阻塞的系统调用futex的热点分布:

image

能够看到上面的热点中有大量涉及调度的开销。咱们来看过程:

调用系统调用(可能须要阻塞);
系统调用确实须要阻塞,kernel须要决定下一个被执行的线程(调度);
执行上下切换。
所以,上面2个误区与多线程的开销都有必定因果关系,可是真正的开销来源于线程阻塞唤醒调度。

综上,但愿经过线程模型来提高web server性能的原则是:

活跃线程数约等于CPU个数
每一个线程不太须要阻塞
文章后续将牢牢围绕这两个主题。

为了知足上述两个条件,使用eventloop+异步callback的方式是一个极佳的选择。

关键词:Dubbo Java 应用服务中间件 Go 调度

相关文章
相关标签/搜索