原文網址:https://plumbr.eu/blog/gc-impact-on-throughput-and-latency
有一類問題是每一個 Java application 都會遇到的,那就是 GC。當 GC 正常運作時,它是一個美妙的發明;當它沒有運作、或是 GC 用出乎意料的方式運作,那你的朋友就會翻臉變成仇人。
這篇文章是關於 GC 造成的暫停時間。或著更精確地說:為什麼你要在意這些暫停時間?
原文網址:https://plumbr.eu/blog/gc-impact-on-throughput-and-latency
有一類問題是每一個 Java application 都會遇到的,那就是 GC。當 GC 正常運作時,它是一個美妙的發明;當它沒有運作、或是 GC 用出乎意料的方式運作,那你的朋友就會翻臉變成仇人。
這篇文章是關於 GC 造成的暫停時間。或著更精確地說:為什麼你要在意這些暫停時間?
原文網址:https://plumbr.eu/blog/how-to-estimate-memory-consumption
這個故事得從十年前開始說起,那時我第一次接觸到一個尖頭老闆的問題:「我們的產品要佈署的時候,需要買多好的 server?」在產品發表之後,我們花了九個月的時間打造了一個金光閃閃的新系統,顯然公司已經承諾要提供整個解決方案,包含硬體的部份。
囧… 我麻煩大了。我只有短短幾年的經驗,回答這問題跟丟骰子沒啥兩樣。雖然我看起來整個就是缺乏信心,不過我仍然得生出一個答案。在 google 了四個小時後,眼花撩亂的腦袋裡仍然是相同的問題:
「如何估算所需的運算能力?」
原文網址:http://googlecloudplatform.blogspot.tw/2014/02/app-engine-190-now-available.html
今天我們宣佈 App Engine 1.9.0 發布,包含 Modules API general availability(GA)、新版 Java MapReduce library、以及加強 PHP runtime。
AutoBean 是目前打算拿來在 GWT 中處理 JSON 的工具。 不過在講正事之前,先扯兩段雜談 [毆飛]
要不是要弄 web API,其實也不會想碰 JSON, GWT RPC 好好的幹麼弄什麼 JSON [遠目]。 不過 server side 要處理 JSON 其實有 GSON, 正常 encode / decode 真的都還蠻簡單的, 簡單到不知道能介紹什麼 XD。 是說 LaPass(ptt.cc)因為有一個我覺得有點詭異的需求, 結果挖出了 TypeAdapterFactory 的用法, 看起來真的很乾淨很炫(炫到都快看不懂了 [遮臉]), 只能說好 library 如當是也。
理所當然的,會想看看有沒有 GWT 版的 GSON, 結果看到 bGwtGson 這玩意差點噴出來。 因為他的作法是用 GWT RPC 把東西丟到 server side, 這樣 server side 就有 GSON 可以用了… 揪咪…
我都不知道該說牛逼還是坑爹,這世界果然很廣大阿 [握拳]
喔對,順帶一提,GSON 在 AppEngine 上也可以使用。
我搞不太懂 AutoBean 在 GWT 當中扮演什麼樣的角色? 彷彿還蠻多人在用的(因為大家都炸同樣的問題… Orz), 但是官方指南似乎沒有這個東西(JavaDoc 當然還是有), 教學文件只有出現在 google code 的 wiki 上, 所以這是裏技嗎?
我比較怕這是即將被拋棄的裏技 Orz
畢竟要在 GWT 裡頭處理 JSON 並不是太麻煩。 官方指南建議的 JSNI / Overlay Types 寫起來堪稱簡單直覺, 尤其是跟 AutoBean 相比的話 [死]。 再不然 GWT-Jackson 好像也是種選擇?
剛好兩者的風格我都不愛 [淚目]
最大的哏在於 AutoBean 並沒有辦法直接處理 List<T> 這種東西,
這個 bug 在 2011.10 被提出之後,
2012.11 最後一個 comment 之後就無聲無息了,
目前最新的 GWT 2.5.1 還是有同樣的問題 Zzzz,只能靠 workaround。
後頭會詳述這些事情 [死]。
好了,終於要進入正題了。 [握拳]
GWT 已經內建 AutoBean,所以不用另外掛 jar 檔,
但是要在 gwt.xml 當中補上:
<inherits name="com.google.web.bindery.autobean.AutoBean"/>
如果你要把下面這個 JSON 字串轉成 Foo 的 instance
{
"uid":"cde6c847-d072-4d33-82bd-93fa4710dc9b",
"limit":50,
"deleted":true,
"update":1388157386000
}
首先… Foo 得是個 bean 的 interface,定義一堆 getter/setter,
名稱得跟 JSON 裡頭的一致:
interface Foo {
public void setUid(String uid);
public void setLimit(int limit);
public void setDeleted(boolean deleted);
public void setUpdate(Date update);
public String getUid();
public int getLimit();
public boolean isDeleted();
public Date getUpdate();
}
轉換的時候需要先建立一個 factory,通常會這樣寫
interface MyFactory extends AutoBeanFactory {
AutoBean<Foo> foo();
}
MyFactory factory = GWT.create(MyFactory.class);
然後… 終於可以 decode 了:
String foo = "{" +
"\"uid\":\"cde6c847-d072-4d33-82bd-93fa4710dc9b\"," +
"\"limit\":50," +
"\"deleted\":true," +
"\"update\":1388157380000" +
"}";
Foo instance = AutoBeanCodex.decode(factory, Foo.class, foo).as();
encode 的話就是:
String fooJson = AutoBeanCodex.encode(
AutoBeanUtils.getAutoBean(instance)
).getPayload();
如果願意在 MyFactory 裡頭加一個 method:
interface MyFactory extends AutoBeanFactory {
AutoBean<Foo> foo();
//下面這個是新增的
AutoBean<Foo> genFooBean(Foo foo);
}
那不用 AutoBeanUtils.getAutoBean() 而是
String fooJson = AutoBeanCodex.encode(
factory.genFooBean(instance)
).getPayload();
有沒有比較快樂就見仁見智,不過後面會需要用到後面這招,
或著這麼說比較實在:
「請忘記 AutoBeanUtils 吧」。
如果到這邊你還能忍受 AutoBean, 那先講幾個我已經炸到,但可以理解的哏, 主要是跟 GSON 的差異。
Gson.toJson() 遇到 false / null 值不會省略該 field,
但是 AutoBean 會。
也就是說,如果 foo.setDeleted(false);,
那麼 AutoBeanCodex.encode() 出來的字串不會看到 deleted。
當然,這其實不妨礙正常運作。 GSON 的作法可能有些人還會覺得怪?
日期(java.util.Date)的處理。
Gson.toJson() 會用 Date.toString() 作值(反之亦然),
但是 AutoBean 則是用 Date.getTime()(反之亦然)。
只能說還是統統用 long 表示日期就算了
(然後在 JSON 當中最好還把這數字當成字串,
免得像 32bit 的 PHP 還給你耍花招 [怨念ing])。
至於 Joda 要解決的議題… 遇到再說 XD
(有遇到會再補上來 Orz)
List 的炸點如果你永遠不會 decode / encode 一個 array 或是 List,
那恭喜你,除了寫法稍稍扭曲一點之外,
AutoBean 是可以接受、也算好用的(應該啦…)。
實際上… 別鬧了,怎麼可能不處理 List?
於是 AutoBean 就成了茶几──上頭擺滿了悲劇。
以 GSON 吐出來的東西來看,一個 List<Foo> 的 instance 會長這樣
(喔對,我把 update 的型態改成 long 了 XD):
[
{"uid":"cde6c847-d072-4d33-82bd-93fa4710dc9b",
"limit":50,"deleted":false,"update":"1388157380000"},
{"uid":"a391dedf-1f81-4380-a712-59eac4d9aea3",
"limit":50,"deleted":false,"update":"1388157380000"}
]
想依樣畫葫蘆比照辦理時…
等等,AutoBeanCodex.decode() 的第二個參數要給什麼?
然後於是有人弄出了這個 workaround。
首先,要建一個 interface 來代表 List<Foo> 這玩意:
interface FooList {
public void setList(List<Foo> list);
public List<Foo> getList();
}
factory 的 interface 則是:
interface MyFactory extends AutoBeanFactory {
//下面這個暫時用不到
AutoBean<Foo> genFooBean(Foo foo);
AutoBean<FooList> fooList();
}
最後,要對拿到的 JSON 字串動手腳,變成這樣:
FooList fooList = AutoBeanCodex.decode(
factory, FooList.class, "{\"list\":" + foo + "}"
).as();
List<Foo> instance = fooList.getList();
簡單地說,就是 Java 的部份你要讓他有個 class 為依歸, 但是光這樣還不夠,因為 AutoBean 不知道要從何處理起, 所以 JSON 的部份你也要偽造一下……
等等,還沒完,好戲壓箱底、好酒沈甕底, encode 的部份那才叫經典。
要把一個 List<Foo> 轉成 JSON,這到底是有多難?
不難,如果把剛剛 AutoBeanCode.decode() 出來的 fooList 再次轉成 JSON,
那麼只要 factory 加上
interface MyFactory extends AutoBeanFactory {
//下面這個暫時用不到
AutoBean<Foo> genFooBean(Foo foo);
AutoBean<FooList> fooList();
//下面這個是新增的
AutoBean<FooList> genFooListBean(FooList instance);
}
立馬就轉,沒有問題!(也完全沒意義 ==”)
AutoBeanCodex.encode(
factory.genFooListBean(fooList)
).getPayload();
如果是把既有的 List<Foo> instance 轉換,
依照上面的邏輯,得先實做那個毫無意義的 FooList:
FooList fooList = new FooList() {
List<Foo> list = new ArrayList<Foo>();
@Override
public void setList(List<Foo> list) {
this.list = list;
}
@Override
public List<Foo> getList() {
return list;
}
};
//FooImpl 就容許我跳過,反正就是 implements Foo 的東東
fooList.getList().add(new FooImpl());
AutoBeanCodex.encode(
factory.genFooListBean(fooList)
).getPayload();
執行上面這段程式碼,你就會發現 AutoBeanCodex.encode()
快樂的炸了 NPE,而且完全搞不懂發生了什麼事情。
這是個已知的 bug(Issue 6904),
雖然不知道會不會有人去解…… Orz。
而世界還真的是很廣大,有人也找出了 workaround,
解法就是你不能直接丟 FooImpl 的 instance,
得用 MyFactory.genFooBean() 產生出 AutoBean<Foo>,
再藉由它(as())取得 Foo 的 instance 才可以……
寫的我自己都亂了,看 code 比較實在:
interface MyFactory extends AutoBeanFactory {
AutoBean<Foo> genFooBean(Foo foo);
AutoBean<FooList> fooList();
AutoBean<FooList> genFooListBean(FooList instance);
}
fooList.getList().add(
//原本是 new FooImpl()
factory.genFooBean(new FooImpl()).as()
);
AutoBeanCodex.encode(
factory.genFooListBean(fooList)
).getPayload();
套最近流行的句型: 「如果這不叫脫褲子放屁,我還真他媽的不知道什麼才叫脫褲子放屁」
喔對,無論 genFooBean() 還是 genFooListBean()
都不能用官方文件用的 AutoBeanUtils.getAutoBean()。
如果拿他替換 genFooBean(),一樣噴 NPE;
如果拿它替話 genFooListBean(),不會噴 NPE,
而是轉換出來的 JSON 字串會是 null。
WTF
拿 GSON 跟 AutoBean 相比是很有趣的。 一個是完美到不需要瞭解內部到底發生什麼事情, 一個則是太糟糕了,所以根本不想瞭解。
短時間之內,我可能還是不會放棄 AutoBean,
除非 GWT 2.6 就遺棄 AutoBean,
或是找到更好的 tool(而不是 GWT-Jackson 那種 style)。
都花了這麼多時間了,就看看能被炸到走到什麼程度。
寫到後來,都不知道到底是在介紹推廣還是在吐槽。 只能說,嗯… 我對 GWT 真的很有愛…… [遠目]
原始報告請到 http://vaadin.com/gwt/report-2013 下載(要輸入 EMail 帳號)。 除了序言之外,其餘都只翻譯數據跟(個人認定的)重點,非逐句翻譯。
在 2012 年第一次作 GWT 狀況調查 時,收到的回覆數量淹沒了我們。 我們把調查報告取名為《The Furture of GWT》(譯名:GWT 調查報告), 因為我們想知道「GWT 衰老中」這個謠言到底有多少可信度。 在超過 1300 個回應裡,我們、以及全世界都看到了 GWT 活蹦亂跳的樣子。 許多大型企業用 GWT 來建構大型的 application, 對 GWT 投注的資源也在增加。
在《GWT 調查報告 2013 版》中, 我們想要知道 GWT 未來該如何發展?(第六節) 開發團隊在用什麼 Java 跟 GWT 的版本?以瞭解 哪些地方需要支援?(第四節) 你們需要哪些 extension、在找哪些 framework?(第五節) 為了取得背景以及人口統計學的資料, 我們也問了關於你的團隊(第一節)、 你在寫的 application(第二節)、 以及你如何開發 application(第三節)。 有了這些資訊整合成這份報告, 我們覺得這是最綜合性的調查,可以引領 GWT 很長一段路。
在 2013 年,有 12 月的研討會(gwtcreate.com), 有令人興奮的 GWT 3.0 的計畫、 還有許多大公司投入 GWt 作為他們未來的技術; 我們知道 GWT 跨出了不只一大步。 2013 年 GWT 委員會也決定了未來的 release 步調: 每年一個大改版以及一個小改版。
這份調查是由 Vaadin、 Ray Cromwell(Google 代表、現任委員會主席)、 Daniel Kurka(mGWT 與 GWT-phonegap 的 Google 開發者)、 Artur Signell(Vaadin 代表)、 Bhaskar Janakiraman(Google)、 Colin Alworth(Sencha)、 Christian Goudreau(Arcbees)、 Konstantin Solomatov(Jetbrains)、 Mike Brock(RedHat)、 Stephen Haberman(Bizo)、 Joonas Lehtinen(Vaadin)以及 Thomas Broyer。 你可以在這份報告中看到他們的意見與反應。
這份調查報告在 GWT.create 2013 發表, 友超過 600 個熱心的 GWT 開發者出席這個年度最大的 GWT 活動。 我們同樣感謝超過 1400 位受訪者所花的時間與坦白, 期待看到你們的意見!
GWT 開發團隊的平均人數是 12.5 人,而中位數則是 8 人。這跟去年的調查相同。 我們也問了過去 12 個月團隊大小的變化。
(兩張團隊大小的統計分佈圖)
以八人團隊為例,任務分配為:
20 是一個魔術數字。調查大型 app 的畫面數量應該很有趣。
Christian:少於 5~7 個畫面的 app 實在太小了,為什麼會用 GWT 呢? 這是個有趣的問題。
對照去年的數據,可以代表 project 的規模越來越大。
Ray 與 Bhaskar:要改善 compile 時間有點難、需要一點時間來克服。 不過我們已經開始嘗試 incremental / modular compilation system。
Ray:看到有人用 GWT 寫遊戲實在很棒。 GWT 可以讓你在 Web 與 Android 之間共用程式碼, 藉由 PlayN 這種 library 也可以拓展到 iOS 上。
(註:下列數據分別為 希望 2013 年有支援 / 希望 2014 年有支援)
Joonas:IE6/7 終於快死光了,所以 GWT 3.0 可以不用鳥它們了。 不過看起來 2014 年還是要很痛苦地去支援 IE8/9, 因為需求量看起來還是蠻大的。
Ray:對於改進 GWT 核心而言,IE6/7/8 是可悲的毒藥, 有很多 API 沒辦法增強就是因為老一輩的 browser 沒辦法處理。 網頁正轉型為 mobile 跟 HTML5, 要整合過時的 browser 會讓這個轉型過程變得很困難。
Thomas:看到 IE6/7/8 加起來還不少,讓我很沮喪。
(註:下列數據分別為 2012 年 / 2013 年)
Christian:我不懂為什麼這年頭還會有人想要 Java 刻 UI? 每個語言都有對應的 UI 描述方式。 ActionScript 有 MXML、 JavaScript 有 HTML、 .NET 有 XAML。
Daniel:看到這麼多人用 Java 刻 UI? 明明 UiBinder 就是比較好的方式。我們需要大力推廣 UiBinder。
Ray:GWT-RPC 在小型 app、快速開發方面實在超有力的, 但是在 compile 時間以及 interoperability 上會花很多時間。 JAXRS 是 GWT 3.0 考慮的方法之一,可能會提供更好的中間層。
Daniel:在決定 GWT 3.0 以後的 RPC 系統時, 我們應該要記得「很多人喜歡 GWT RPC 的簡單好用」這件事。
Christian:我個人最愛 IDEA 的 GWT plugin,是目前最成熟的。
Ray:我是 IDEA 的愛好者,它把寫 JSNI/JavaScript 程式碼這檔事做的很好。
Thomas:看到 Super DevMode 成長真好。 未來要讓所有 browser 支援 DevMode 不太可能。 Super DevMode 的比例成長應該有部份會歸應於 mobile 平台。
Joonas:我們真的應該讓 Super DevMode 變成預設值。 用它來執行程式會比較接近實際的運算效能。 我覺得 DevMode 的優勢在於設定比較方便。
Thomas:看到有 3% 的人用 thunk 版真的超驚訝的。 我們計畫推出 nightly build,明年這個比率可能會增加。
Ray:我可以發布一個解法,只讓 client 的碼用 Java8。 但是 Java6 無所不在,希望多一點人可以願意去試試看。 Java8 的 lambdas 超好的, 讓 async callback 還有 collection 操作變得很愉快。
Joonas:Vaadin 7 已經支援 Java 8 了, 我希望我們可以在 GWT 3 當中用到 Lambdas 的優點。
Daniel:80% 的人會想要在 GWT 中用 Java8,這相當好。 我們必須找出一個好方法讓 server side 必須用 Java6 的人也能運作。
Ray:GWT 3.0 會有一個新的 JS interop system, 銜接向 Polymar 或是 Angular 就不用寫 JSNI 的程式碼而變得更容易。 如此一來,想要用什麼 framework 就去用吧。
回收的問卷中有 87% 的人使用了外掛,其中:
Thomas:GWT 真的夠好嗎?我不確定…… 到底是大家沒有被 bug 炸到,還是他們懶得回報 bug?
每人兩票:
每人兩票:
Daniel:GWT 2.6 就已經有一些關於 compile 時間的改善。
Thomas:GWT 3.0 的重點會在改善 compile 時間以及 DevMode 的 refresh 時間上。