-
1 # 北大青鳥優越IT學院
-
2 # 暴富2021
其實沒什麼最佳不最佳,這個看你們公司業務,我覺得Java挺好的,企業級,發展這麼多年一直很平穩,社群也很強大,作為Javaer,我愛Java
-
3 # BitMan
golang連基本的OO特性都支援不全(沒繼承),而且還沒有泛型…其中的痛苦,誰用誰知道(可能是因為我一直做java開發)…
java成熟而穩重,各類框架和庫全面而充分,效能均衡而穩定,大小公司都在用,技術成熟,工作機會多…未來5-10年必然仍舊是java的天下!
golang會在特定領域,比如微服務上有所建樹(話說回來,隨著Spring的發展,java在微服務上也不差);但是,打敗java的不會是golang…
千萬別說go還在發展和演進,好像java不發展了一樣…
-
4 # 開源之道
我認為Java還會是企業級服務軟體以及大資料方向的首選,生態很完善和強大,不光是阿里美團這樣大廠的支援和佈道,對小公司也可以依靠Java完善的生態,快速搭建起自己的系統推向市場。 儘管oracle一直在作死,還是有很多大廠基於openjdk做支援,Java不會死,還會越來越好。
所以我的觀點是go是一門值得學習和關注的語言,但是當你面臨技術選型時,如果很糾結,選擇Java一般不會錯,可能不會太好,但總不會太差。而且從就業面上來說,Java目前也會更好一些。
-
5 # 東北猿在學習
這個問題的看待不能只從技術層面,技術的誕生服務於人,不論什麼技術最後都是服務與人的,go在一定程度上確實優異於java,但是它不一定是最佳選擇,在開發商選用技術語言通常要考慮多方面,人工成本,生態環境,穩定性,乙方接受程度,人才繼承性,總不能開發完的系統以後招個人維護都難,綜合各個因素來說,go的最佳選擇性就會降低很多。
-
6 # 你看我獨角獸嗎
作為開發人員好幾年,我可以使用多種語言和框架來做自己喜歡的事情。其中包括Basic,C,C ++,FORTRAN,PHP,Javascript,最近也包括Golang和Python。
在開始使用雲伺服器計算後,我被Golang吸引了。簡單的協程可幫助到我們以最少的工作量和高併發性編寫高度可擴充套件的後端。這使得在單頁面Web應用程式和混合移動應用程式去編寫API更容易。
和Java比較?好吧,我不是特別喜歡Java,雖然它很健壯,因為它複雜的語法才能實現比較簡單的目標。如果您在開始使用Java之前就已經學習過Python,那麼您完全有可能因為它的複雜性而放棄了。
Java在構建企業級軟體應用程式方面的強大功能尚無定論,但當您檢視替代方案時,你就會覺得Java的複雜性就太大了。
儘管React-Native等混合框架越來越流行,Java仍然是Android和後端開發人員的最愛。許多公司已經使用Java構建了複雜的應用程式,尤其是在銀行業或者現在的阿里。但是,由於Golang的簡單性和直接編譯成機器語言的能力,它更勝過Java一籌。
當Golang被編譯成二進位制檔案並在不依賴目標系統的情況下進行分發時,Java使用Java虛擬機器(JVM)。Java與底層硬體進行良好互動以實現效能的能力是其成功的主要因素,但是Golang的直接二進位制編譯優勢使其成為編寫高效能指令碼的有力競爭者。
與Python之類的解釋型語言相比,Java仍然更快。但是對於伺服器端計算呢?Golang勝了!
與Java相比,Go的編譯速度更快,並且佔用的記憶體更少。考慮到Java的統治地位,這可能不是一個主要因素,但是Golang一直在穩步採用Java來構建可擴充套件的後端體系結構。
所以如果你想選擇一門又快又容易寫的語言,Golang真的很不錯。
-
7 # 慎談奧秘
阿里妹導讀:隨著大量新生的非同步框架和支援協程的語言(如Go)的出現,在很多場景下作業系統的執行緒排程成為了效能的瓶頸,Java也因此被質疑是否不再適應最新的雲場景了。4年前,阿里JVM團隊開始自研Wisp2,將Go語言的協程能力帶入到Java世界。既享受Java的豐富生態,又獲得非同步程式的效能,Wisp2讓Java平臺歷久彌新。
Java平臺一直以生態的繁榮著稱,大量的類庫、框架幫助開發者們快速搭建應用。而其中大部分Java框架類庫都是基於執行緒池以及阻塞機制來服務併發的,主要原因包括:
1. Java語言在核心類庫中提供了強大的併發能力,多執行緒應用可以獲得不俗的效能;
2. Java EE的一些標準都是執行緒級阻塞的(比如JDBC);
3. 基於阻塞模式可以快速地開發應用。
但如今,大量新生的非同步框架和支援協程的語言(如Go)的出現,在很多場景下作業系統的執行緒排程成為了效能的瓶頸。Java也因此被質疑是否不再適應最新的雲場景了。
4年前,阿里開始自研Wisp2。它主要是用在IO密集的伺服器場景,大部分公司的線上服務都是這樣的場景 (離線應用都是偏向於計算,則不適用)。它在功能屬性上對標Goroutine的Java協程,在產品形態、效能、穩定性上都達到了一個比較理想的情況。到現在,已經有上百個應用,數萬個容器上線了Wisp1/2。Wisp協程完全相容多執行緒阻塞的程式碼寫法,僅需增加JVM引數來開啟協程,阿里巴巴的核心電商應用已經在協程模型上經過兩個雙十一的考驗,既享受到了Java的豐富生態,又獲得了非同步程式的效能。
Wisp2主打的是效能和對現有程式碼的相容性,簡而言之,現有的基於多執行緒的IO密集的Java應用只需要加上Wisp2的JVM引數就可以獲得非同步的效能提升。
作為例子,以下是訊息中介軟體代理(簡稱mq)和drds只新增引數不改程式碼的壓測比較:
可以看到上下文切換以及sys CPU顯著降低,RT減少、QPS分別提升11.45%,18.13%。
Quick Start
由於Wisp2完全相容現有的Java程式碼,因此使用起來十分簡單,有多簡單?
如果你的應用是“標準”的線上應用(使用/home/admin/$APP_NAME/setenv.sh配置引數),那麼在admin使用者下輸入如下命令就可以開啟Wisp2了:
curl https://gosling.alibaba-inc.com/sh/enable-wisp2.sh | 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表示時間片搶佔次數。
下圖是DRDS在ecs上壓測時的top -H,可以看出來應用的數百個執行緒被8個Carrier執行緒託管,均勻地跑在CPU核數個執行緒上面。下方一些名為java的執行緒是gc執行緒。
過多執行緒的開銷
誤區1: 進核心引發上下文切換
我們看一段測試程式:
pipe(a);
while (1) {
write(a[1], a, 1);
read(a[0], a, 1);
n += 2;
}
執行這段程式時上下文切換非常低,實際上上面的IO系統呼叫都是不會阻塞的,因此核心不需要掛起執行緒,也不需要切換上下文,實際發生的是使用者/核心態的模式切換。
上面的程式在神龍伺服器測得每個pipe操作耗時約334ns,速度很快。
誤區2: 上下文切換的開銷很大
本質上來說無論是使用者態還是核心態的上下文切換都是很輕量的,甚至有一些硬體指令來支援,比如pusha可以幫助我們儲存通用暫存器。同一個程序的執行緒共享頁表,因此上下文切換的開銷一般只有:
儲存各種暫存器
切換sp(call指令會自動將pc壓棧)
可以在數十條指令內完成。
開銷
既然近核心以及上下文切換都不慢,那麼多執行緒的開銷究竟在哪?
我們不妨看一個阻塞的系統呼叫futex的熱點分佈:
可以看到上面的熱點中有大量涉及排程的開銷。我們來看過程:
呼叫系統呼叫(可能需要阻塞);
系統呼叫確實需要阻塞,kernel需要決定下一個被執行的執行緒(排程);
執行上下切換。
綜上,希望透過執行緒模型來提升web server效能的原則是:
活躍執行緒數約等於CPU個數
每個執行緒不太需要阻塞
文章後續將緊緊圍繞這兩個主題。
為了滿足上述兩個條件,使用eventloop+非同步callback的方式是一個極佳的選擇。
非同步與協程的關係
為了保持簡潔,我們以一個非同步伺服器上的Netty寫操作為例子(寫操作也存在阻塞的可能):
private void writeQuery(Channel ch) {
ch.write(Unpooled.wrappedBuffer("query".getBytes())).sync();
logger.info("write finish");
}
這裡的sync()會阻塞執行緒。不滿足期望。由於netty本身是一個非同步框架,我們引入回撥:
private void writeQuery(Channel ch) {
ch.write(Unpooled.wrappedBuffer("query".getBytes()))
.addListener(f -> {
logger.info("write finish");
});
}
注意這裡非同步的write呼叫後,writeQuery會返回。因此假如邏輯上要求在write後執行的程式碼,必須出現在回撥裡,write是函式的最後一行。這裡是最簡單的情形,如果函式有其他呼叫者,那麼就需要用CPS變換。
需要不斷的提取程式的"下半部分",即continuation,似乎對我們造成一些心智負擔了。這裡我們引入kotlin協程幫助我們簡化程式:
suspend fun Channel.aWrite(msg: Any): Int =
suspendCoroutine { cont ->
write(msg).addListener { cont.resume(0) }
}
suspend fun writeQuery(ch: Channel) {
ch.aWrite(Unpooled.wrappedBuffer("query".toByteArray()))
logger.info("write finish")
}
這裡引入了一個魔法suspendCoroutine,我們可以獲得當前Continuation的引用,並執行一段程式碼,最後掛起當前協程。Continuation代表了當前計算的延續,透過Continuation.resume()我們可以恢復執行上下文。因此只需在寫操作完成時回撥cont.resume(0),我們又回到了suspendCoroutine處的執行狀態(包括caller writeQuery),程式繼續執行,程式碼返回,執行log。從writeQuery看我們用同步的寫法完成了非同步操作。當協程被suspendCoroutine切換走後,執行緒可以繼續排程其他可以執行的協程來執行,因此不會真正阻塞,我們因此獲得了效能提升。
從這裡看,只需要我們有一個機制來儲存/恢復執行上下文,並且在阻塞庫函數里採用非阻塞+回撥的方式讓出/恢復協程,就可以使得以同步形式編寫的程式達到和非同步同樣的效果了。
理論上只要有一個庫包裝了所有JDK阻塞方法,我們就可以暢快地編寫非同步程式了。改寫的阻塞庫函式本身需要足夠地通用流行,才能被大部分程式使用起來。據我所知,vert.x的kotlin支援已經做了這樣的封裝。
雖然vert.x很流行,但是無法兼顧遺留程式碼以及程式碼中的鎖阻塞等邏輯。因此不能算是最通用的選擇。實際上Java程式有一個繞不過的庫——JDK。Wisp就是在JDK裡所有的阻塞調用出進行了非阻塞+事件恢復協程的方式支援了協程排程,在為使用者帶來最大便利的同時,兼顧了現有程式碼的相容性。
上述方式支援了,每個執行緒不太需要阻塞,Wisp在Thread.start()處,將執行緒轉成成了協程,來達到了另一目的: 活躍執行緒數約等於CPU個數。因此只需要使用Wisp協程,所有現有的Java多執行緒程式碼都可以獲得非同步的效能。
手工非同步/Wisp效能比較
對於基於傳統的程式設計模型的應用,考慮到邏輯清晰性、異常處理的便利性、現有庫的相容性,改造成非同步成本巨大。使用Wisp相較於非同步程式設計優勢明顯。
下面我們在只考慮效能的新應用的前提下分析技術的選擇。
基於現有元件寫新應用
如果要新寫一個應用我們通常會依賴JDBC、Dubbo、Jedis這樣的常用協議/元件,假如庫的內部使用了阻塞形式,並且沒有暴露回撥介面,那麼我們就沒法基於這些庫來寫非同步應用了(除非包裝執行緒池,但是本末倒置了)。下面假設我們依賴的所有庫都有回撥支援,比如dubbo。
1)假設我們使用Netty接受請求,我們稱之為入口eventLoop,收到請求可以在Netty的handler裡處理,也可以為了io的實時性使用業務執行緒池。
2)假設請求處理期間需要呼叫dubbo,因為dubbo不是我們寫的,因此內部有自己的Netty Eventloop,於是我們向dubbo內部的Netty eventLoop處理IO,等待後端響應後回撥。
3)dubbo eventLoop收到響應後在eventloop或者callback執行緒池呼叫callback。
4)後續邏輯可以在callback執行緒池或者原業務執行緒池繼續處理。
5)為了完成對客戶端的響應最終總是要由入口的eventloop來寫回響應。
我們可以看到由於這種封裝導致的eventLoop的割裂,即便完全使用回撥的形式,我們處理請求時多多少少要在多個eventLoop/執行緒池之間傳遞,而每個執行緒又都沒法跑到一個較滿的程度,導致頻繁地進入os排程。與上述的每個執行緒不太需要阻塞原則相違背。因此雖然減少了執行緒數,節約了記憶體,但是我們得到的效能收益變得很有限。
完全從零開始開發
對於一個功能有限的新應用(比如nginx只支援http和mail協議)來說我們可以不依賴現有的元件來重新寫應用。比如我們可以基於Netty寫一個數據庫代理伺服器,與客戶端的連線以及與真正後端資料庫的連線共享同一個eventloop。
這樣精確控制執行緒模型的應用通常可以獲得很好的效能,通常效能是可以高於透過非非同步程式轉協程的,原因如下:
執行緒控制更加精確:舉個例子,比如我們可以控制代理的客戶端和後端連線都繫結在同一個netty執行緒,所有的操作都可以threadLocal化
沒有協程的runtime和排程開銷(1%左右)
但是使用協程依舊有一個優勢:對於jdk中無處不在的synchronized塊,wisp可以正確地切換排程。
適應的Workload
基於上述的背景,我們已經知道Wisp或者其他各種協程是適用於IO密集Java程式設計的。否則執行緒沒有任何切換,只需要盡情地在CPU上跑,OS也不需要過多的干預,這是比較偏向於離線或者科學計算的場景。
線上應用通常需要訪問RPC、DB、cache、訊息,並且是阻塞的,十分適合使用Wisp來提升效能。
最早的Wisp1也是對這些場景進行了深度定製,比如hsf接受的請求處理是會自動用協程取代執行緒池,將IO執行緒數量設定成1個後使用epoll_wait(1ms)來代替selector.wakeup(),等等。因此我們經常受到的一個挑戰是Wisp是否只適合阿里內部的workload?
對於Wisp1是這樣的,接入的應用的引數以及Wisp的實現做了深度的適配。
對於Wisp2,會將所有執行緒轉換成協程,已經無需任何適配了。
為了證明這一點,我們使用了web領域最權威的techempower benchmak集來驗證,我們選擇了com.sun.net.httpserver、Servlet等常見的阻塞型的測試(效能不是最好,但是最貼近普通使用者,同時具備一定的提升空間)來驗證Wisp2在常見開源元件下的效能,可以看到在高壓力下qps/RT會有10%~20%的最佳化。
Project Loom
Project Loom作為OpenJDK上的標準協程實現很值得關注,作為java開發者我們是否應該擁抱Loom呢?
我們首先對Wisp和Loom這裡進行一些比較:
1)Loom使用序列化的方式儲存上下文,更省記憶體,但是切換效率低。
2)Wisp採用獨立棧的方式,這點和go類似。協程切換隻需切換暫存器,效率高但是耗記憶體。
3)Loom不支援ObectMonitor,Wisp支援。
synchronized/Object.wait()將佔用執行緒,無法充分利用CPU。
還可能產生死鎖,以Wisp的經驗來說是一定會產生死鎖(Wisp也是後來陸續支援ObectMonitor的)。
4)Wisp支援在棧上有native函式時切換(反射等等),Loom不支援。
對dubbo這樣的框架不友好,棧底下幾乎都帶有反射。
同時Wisp目前完全相容Loom的Fiber API,假如我們的使用者基於Fiber API來程式設計,我們可以保證程式碼的行為在Loom和Wisp上表現完全一致。
FAQ
協程也有排程,為什麼開銷小?
我們一直強調了協程適用於IO密集的場景,這就意味了通常任務執行一小段時間就會阻塞等待IO,隨後進行排程。這種情況下只要系統的CPU沒有完全打滿,使用簡單的先進先出排程策略基本都能保證一個比較公平的排程。同時,我們使用了完全無鎖的排程實現,使得排程開銷相對核心大大減少。
Wisp2為什麼不使用ForkJoinPool來排程協程?
ForkJoinPool本身十分優秀,但是不太適合Wisp2的場景。
為了便於理解,我們可以將一次協程喚醒看到做一個Executor.execute()操作,ForkJoinPool雖然支援任務竊取,但是execute()操作是隨機或者本執行緒佇列操作(取決於是否非同步模式)的,這將導致協程在哪個執行緒被喚醒的行為也很隨機。
在Wisp底層,一次steal的代價是有點大的,因此我們需要一個affinity,讓協程儘量保持繫結在固定執行緒,只有執行緒忙的情況下才發生workstealing。我們實現了自己的workStealingPool來支援這個特性。從排程開銷/延遲等各項指標來看,基本能和ForkJoinPool打平。
還有一個方面是為了支援類似go的M和P機制,我們需要將被協程阻塞的執行緒踢出排程器,這些功能都不適宜改在ForkJoinPool裡。
如何看待Reactive程式設計?
Reactive程式設計模型已經被業界廣泛接受,是一種重要的技術方向;同時Java程式碼裡的阻塞也很難完全避免。我們認為協程可以作為一種底層worker機制來支援Reactive程式設計,即保留了Reactive程式設計模型,也不用太擔心使用者程式碼的阻塞導致了整個系統阻塞。
這裡是Ron Pressler最近的一次演講,作為Quasar和Loom的作者,他的觀點鮮明地指出了回撥模型會給目前的程式設計帶來很多挑戰 。
Wisp經歷了4年的研發,我將其分為幾個階段:
1)Wisp1,不支援objectMonitor、並行類載入,可以跑一些簡單應用;
2)Wisp1,支援了objectMonitor,上線電商核心,不支援workStealing,導致只能將一些短任務轉為協程(否則workload不均勻),netty執行緒依舊是執行緒,需要一些複雜且trick的配置;
3)Wisp2,支援了workStealing,因此可以將所有執行緒轉成協程,上述netty問題也不再存在了。
目前主要的限制是什麼?
目前主要的限制是不能有阻塞的JNI呼叫,wisp是透過在JDK中插入hook來實現阻塞前排程的,如果是使用者自定義的JNI則沒有機會hook。
最常見的場景就是使用了Netty的EpollEventLoop:
1)螞蟻的bolt元件預設開啟了這個特點,可以透過-Dbolt.netty.epoll.switch=false 來關閉,對效能的影響不大。
2)也可以使用-Dio.netty.noUnsafe=true , 其他unsafe功能可能會受影響。
3)(推薦) 對於netty 4.1.25以上,支援了透過-Dio.netty.transport.noNative=true 來僅關閉jni epoll,參見358249e5
———————————————— 河南新華
-
8 # 淺析架構
Java不僅僅是Java語言了,而是一個以Java為中心的生態圈,在這個生態圈裡面可以滿足你需要的各種資源,也是這麼多年發展起來的。
而Go語言就算說的有多麼強大,但是要發展起來還是需要一段時間的,近期來看Java還是很好的選擇,也比較成熟。不過對於有些新的應用,並且也是go適合的場景,嘗試一下go也未嘗不可。
回覆列表
如果從薪資出發,Go程式設計師的平均薪資是能高出Java一節的。
為什麼呢?
第一:Java 不管是大小廠都在用,低階Java 數不勝數,工資也入門級的,這些金字塔低端的人群拉低了 Java 的平均薪資。
第二,Go 主要是大廠在用,小廠不敢冒險跟一種新技術(除非有強力 CTO 坐鎮),而且 Go 基本上沒有新手可言, Go 的使用者絕大部分集中在多年後端經驗的老手,大部分由 Python、C++、Java 轉過來的,因此平均薪資極高,能跟 Scala、Erlang 媲美的高薪一族(注意這倆高薪也是跟 Golang 一個情況,多年 java、c++轉的)。
因此就薪資而言,一定是 Golang 高。所以對於你而言,Java還是不是最佳選擇關鍵在於你是不是能轉Go的老手。除非你水平極高,不然一個在校學生估計被前輩秒成渣, Java、php 起碼崗位多,能養活菜鳥,Scala、Erlang、Golang 這種高薪語言不養菜鳥的。