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 調度

相關文章
相關標籤/搜索