netty性能優(yōu)化

關于netty的學習和介紹处铛,可以去github看官方文檔晾嘶,這里良心推薦《netty實戰(zhàn)》和《netty權威指南》兩本書,前者對于新手更友好距糖,原理和應用都有講到涡真,多讀讀會發(fā)現(xiàn)很多高性能的優(yōu)化點。這里強力推薦netty作者的博客肾筐,干貨真的很多哆料。



1.盡可能的復用EventLoopGroup

這里就要涉及netty的線程模型了。netty實戰(zhàn)的第七章里有很細致的闡釋吗铐。簡單說EventLoopGroup包含了指定數(shù)量(如果沒有指定东亦,默認是cpu核數(shù)的兩倍,可以從源碼中看到)的EvenetLoop唬渗,Eve netLoop和channel的關系是一對多典阵,一個channel被分配給一個EventLoop,它生命周期中都會使用這個EventLoop镊逝,而EventLoop背后就是線程壮啊。見下圖。


因此不需要每次都new出一個EventLoopGroup撑蒜,其本質上是線程分配歹啼,可以復用同一個EventLoopGroup,減少資源的使用和線程的切換座菠。

2. 使用EventLoop的任務調度

在EventLoop的支持線程外使用channel狸眼,用

channel.eventLoop().execute(new Runnable() {
?? @Override
??? public void run() {
??????? channel.writeAndFlush(data)
??? }
});

而不是直接使用channel.writeAndFlush(data);

前者會直接放入channel所對應的EventLoop的執(zhí)行隊列浴滴,而后者會導致線程的切換拓萌。

3. 減少ChannelPipline的調用長度

public class YourHandler extends ChannelInboundHandlerAdapter {
? @Override
? public void channelActive(ChannelHandlerContext ctx) {
??? // BAD (most of the times)
??? ctx.channel().writeAndFlush(msg);
??? // GOOD
??? ctx.writeAndFlush(msg);
?? }
}

前者是將msg從整個ChannelPipline中走一遍,所有的handler都要經(jīng)過升略,而后者是從當前handler一直到pipline的尾部微王,調用更短屡限。

同樣,為了減少pipline的長度炕倘,如果一個handler只需要使用一次囚霸,那么可以在使用過之后,將其從pipline中remove激才。

4. 減少ChannelHandler的創(chuàng)建

如果channelhandler是無狀態(tài)的(即不需要保存任何狀態(tài)參數(shù)),那么使用Sharable注解额嘿,并在bootstrap時只創(chuàng)建一個實例瘸恼,減少GC。否則每次連接都會new出handler對象册养。

@ChannelHandler.Shareable
public class StatelessHandler extends ChannelInboundHandlerAdapter {
??? @Override
??? public void channelActive(ChannelHandlerContext ctx) {}
}

public class MyInitializer extends ChannelInitializer<Channel> {
??? private static final ChannelHandler INSTANCE = new StatelessHandler();
??? @Override
??? public void initChannel(Channel ch) {
??????? ch.pipeline().addLast(INSTANCE);
??? }
}

同時需要注意ByteToMessageDecoder之類的編解碼器是有狀態(tài)的东帅,不能使用Sharable注解。

5. 減少系統(tǒng)調用(Flush)的調用

flush操作是將消息發(fā)送出去球拦,會引起系統(tǒng)調用靠闭,應該盡量減少flush操作,減少系統(tǒng)調用的開銷坎炼。

同時也要減少write的操作愧膀, 因為這樣消息會流過整個ChannelPipline。


6. 使用單鏈接

對于兩個指定的端點可以使用單一的channel谣光,在第一次創(chuàng)建之后保存channel檩淋,然后下次對于同一個IP地址可以復用該channel而不需要重新建立。

你可能需要一個map來保存對于不同ip的channel萄金,但是在初始化時這可能會有一些線程并發(fā)的問題。在這篇微信推文(https://mp.weixin.qq.com/s/JRsbK1Un2av9GKmJ8DK7IQ)中有提到對于這個的解決方案,在螞蟻金服的sofa-bolt項目中有類似情形案腺,不過不太理解另凌。

initialTask = this.connTasks.get(poolKey);
if (null == initialTask) {
??? initialTask = new RunStateRecordedFutureTask<ConnectionPool>(callable);
??? initialTask = this.connTasks.putIfAbsent(poolKey, initialTask);
??? if (null == initialTask) {
??????? initialTask = this.connTasks.get(poolKey);
??????? initialTask.run();
??? }
}

7. 利用netty零拷貝,在IO操作時使用池化的DirectBuffer

在bootstrap配置參數(shù)的時候孙乖,使用.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)來指定一個池化的Allocator浙炼,并且使用ByteBuf buf = allocator.directBuffer();來獲取Bytebuf。

PooledByteBufAllocator唯袄,netty會幫你復用(無需release鼓拧,除非你后面還需要用到同一個bytebuf)而不是每次都重新分配ByteBuf。在IO操作中越妈,分配直接內存而不是JVM的堆空間季俩,就避免了在發(fā)送數(shù)據(jù)時,從JVM到直接內存的拷貝過程梅掠,這也就是zero copy的含義酌住。

8. 一些配置參數(shù)的設置

ServerBootstrap啟動時店归,通常bossGroup只需要設置為1即可,因為ServerSocketChannel在初始化階段酪我,只會注冊到某一個eventLoop上消痛,而這個eventLoop只會有一個線程在運行,所以沒有必要設置為多線程都哭。而 IO 線程秩伞,為了充分利用 CPU,同時考慮減少線上下文切換的開銷欺矫,通常設置為 CPU 核數(shù)的兩倍纱新,這也是 Netty 提供的默認值。

在對于響應時間有高要求的場景穆趴,使用.childOption(ChannelOption.TCP_NODELAY, true)和.option(ChannelOption.TCP_NODELAY, true)來禁用nagle算法脸爱,不等待,立即發(fā)送未妹。


9. 小心的使用并發(fā)編程技巧

千萬不要阻塞EventLoop簿废!包括了Thead.sleep()CountDownLatch和一些耗時的操作等等,盡量使用netty中的各種future络它。如果必須盡量減少重量級的鎖的的使用族檬。

在使用volatile時,

壞的:

private volatile Selector selector;
public void method() {
? selector.select();
? ....
? selector.selectNow();
}

好的:先將volatile變量保存到方法棧中化戳,jdk源碼中大量的使用了這種技巧导梆。

private volatile Selector selector;
public void method() {
? Selector selector = this.selector;
? selector.select();
? ....
? selector.selectNow();
}

使用Atomic*FieldUpdater替換Atomic*。關于這個可以參考http://normanmaurer.me/blog/2013/10/28/Lesser-known-concurrent-classes-Part-1/迂烁。簡單說看尼,如果使用Atomic*,對于每個連接都會創(chuàng)建一個對象盟步,而如果使用Atomic*FieldUpdater則會省去這部分的開銷藏斩,只有一個static final變量。

private static final AtomicLongFieldUpdater<TheDeclaringClass> ATOMIC_UPDATER =
??????? AtomicLongFieldUpdater.newUpdater(TheDeclaringClass.class, "atomic");
private volatile long atomic;
public void yourMethod() {
??? ATOMIC_UPDATER.compareAndSet(this, 0, 1);
}

10. 響應順序的處理

當使用了單鏈接却盘,就有一個必須要解決的問題狰域,將請求和響應順序對應起來。因為所有的操作都是異步的黄橘,TCP是基于字節(jié)流的兆览,所以channel接收到的數(shù)據(jù)無法保證和發(fā)送順序一致。這個的解決方案就是塞关,對于每個請求指定一個id抬探,對于響應也攜帶該id。如果后發(fā)的請求的響應先到,則將其緩存起來(可以使用一個并發(fā)的隊列)小压,然后等待該id之前的所有響應全部接收到线梗,再按序返回。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末怠益,一起剝皮案震驚了整個濱河市仪搔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蜻牢,老刑警劉巖烤咧,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異抢呆,居然都是意外死亡煮嫌,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進店門镀娶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人揪罕,你說我怎么就攤上這事梯码。” “怎么了好啰?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵轩娶,是天一觀的道長。 經(jīng)常有香客問我框往,道長鳄抒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任椰弊,我火速辦了婚禮许溅,結果婚禮上,老公的妹妹穿的比我還像新娘秉版。我一直安慰自己贤重,他們只是感情好,可當我...
    茶點故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布清焕。 她就那樣靜靜地躺著并蝗,像睡著了一般。 火紅的嫁衣襯著肌膚如雪秸妥。 梳的紋絲不亂的頭發(fā)上滚停,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天,我揣著相機與錄音粥惧,去河邊找鬼键畴。 笑死,一個胖子當著我的面吹牛突雪,可吹牛的內容都是我干的镰吵。 我是一名探鬼主播檩禾,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼疤祭!你這毒婦竟也來了盼产?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤勺馆,失蹤者是張志新(化名)和其女友劉穎戏售,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體草穆,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡灌灾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了悲柱。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锋喜。...
    茶點故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖豌鸡,靈堂內的尸體忽然破棺而出嘿般,到底是詐尸還是另有隱情,我是刑警寧澤涯冠,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布炉奴,位于F島的核電站,受9級特大地震影響蛇更,放射性物質發(fā)生泄漏瞻赶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一派任、第九天 我趴在偏房一處隱蔽的房頂上張望砸逊。 院中可真熱鬧,春花似錦掌逛、人聲如沸痹兜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽字旭。三九已至,卻和暖如春崖叫,著一層夾襖步出監(jiān)牢的瞬間遗淳,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工心傀, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留屈暗,地道東北人。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像养叛,于是被迫代替她去往敵國和親种呐。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,492評論 2 348

推薦閱讀更多精彩內容