摘要:線程切換效率低下單機核數固定,線程爆炸之后操作系統頻繁進行線程切換,應用性能急劇下降。線程切換效率低下由于模型中線程數量大大降低,線程切換效率因此也大幅度提高。將兩個線程優雅地關閉。創建管道的子處理器,用于處理。
Netty+SpringBoot+FastDFS+Html5實現聊天App,項目介紹:https://segmentfault.com/a/11...
Netty+SpringBoot+FastDFS+Html5實現聊天App,項目github鏈接:https://github.com/ShimmerPig...
本章練習完整代碼鏈接:https://github.com/ShimmerPig...
Netty學習 IO編程與NIO編程 傳統IO編程性能分析IO編程模型在客戶端較少的情況下運行良好,但是對于客戶端比較多的業務來說,單機服務端可能需要支撐成千上萬的連接,IO模型可能就不太合適了。這是因為在傳統的IO模型中,每個連接創建成功之后都需要一個線程來維護,每個線程包含一個while死循環,那么1w個連接對應1w個線程,繼而1w個while死循環,這就帶來如下幾個問題:
1.線程資源受限:線程是操作系統中非常寶貴的資源,同一時刻有大量的線程處于阻塞狀態是非常嚴重的資源浪費,操作系統耗不起。
2.線程切換效率低下:單機cpu核數固定,線程爆炸之后操作系統頻繁進行線程切換,應用性能急劇下降。
3.除了以上兩個問題,IO編程中,我們看到數據讀寫是以字節流為單位,效率不高。
為了解決這三個問題,JDK在1.4之后提出了NIO。下面簡單描述一下NIO是如何解決以上三個問題的。
線程資源受限NIO編程模型中,新來一個連接不再創建一個新的線程,而是可以把這條連接直接綁定到某個固定的線程,然后這條連接所有的讀寫都由這個線程來負責。
這個過程的實現歸功于NIO模型中selector的作用,一條連接來了之后,現在不創建一個while死循環去監聽是否有數據可讀了,而是直接把這條連接注冊到selector上,然后,通過檢查這個selector,就可以批量監測出有數據可讀的連接,進而讀取數據。
由于NIO模型中線程數量大大降低,線程切換效率因此也大幅度提高。
IO讀寫以字節為單位NIO解決這個問題的方式是數據讀寫不再以字節為單位,而是以字節塊為單位。IO模型中,每次都是從操作系統底層一個字節一個字節地讀取數據,而NIO維護一個緩沖區,每次可以從這個緩沖區里面讀取一塊的數據。
完整代碼鏈接:https://github.com/ShimmerPig...
首先定義一對線程組——主線程bossGroup與從線程workerGroup。
bossGroup——用于接受客戶端的連接,但是不做任何處理,跟老板一樣,不做事。
workerGroup——bossGroup會將任務丟給他,讓workerGroup去處理。
//主線程 EventLoopGroup bossGroup = new NioEventLoopGroup(); //從線程 EventLoopGroup workerGroup = new NioEventLoopGroup();
定義服務端的啟動類serverBootstrap,需要設置主從線程,NIO的雙向通道,與子處理器(用于處理workerGroup),這里的子處理器后面我們會手動創建。
// netty服務器的創建, ServerBootstrap 是一個啟動類 ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup, workerGroup) // 設置主從線程組 .channel(NioServerSocketChannel.class) // 設置nio的雙向通道 .childHandler(new HelloServerInitializer()); // 子處理器,用于處理workerGroup
啟動服務端,綁定8088端口,同時設置啟動的方式為同步的,這樣我們的Netty就會一直等待,直到該端口啟動完畢。
ChannelFuture channelFuture = serverBootstrap.bind(8088).sync();
監聽關閉的通道channel,設置為同步方式。
channelFuture.channel().closeFuture().sync();
將兩個線程優雅地關閉。
bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully();
創建管道channel的子處理器HelloServerInitializer,用于處理workerGroup。
HelloServerInitializer里面只重寫了initChannel方法,是一個初始化器,channel注冊后,會執行里面相應的初始化方法。
在initChannel方法中通過SocketChannel獲得對應的管道,通過該管道添加相關助手類handler。
HttpServerCodec是由netty自己提供的助手類,可以理解為攔截器,當請求到服務端,我們需要做解碼,響應到客戶端做編碼。
添加自定義的助手類customHandler,返回"hello netty~"
ChannelPipeline pipeline = channel.pipeline(); pipeline.addLast("HttpServerCodec", new HttpServerCodec()); pipeline.addLast("customHandler", new CustomHandler());
創建自定義的助手類CustomHandler繼承SimpleChannelInboundHandler,返回hello netty~
重寫channelRead0方法,首先通過傳入的上下文對象ChannelHandlerContext獲取channel,若消息類型為http請求,則構建一個內容為"hello netty~"的http響應,通過上下文對象的writeAndFlush方法將響應刷到客戶端。
if (msg instanceof HttpRequest) { // 顯示客戶端的遠程地址 System.out.println(channel.remoteAddress()); // 定義發送的數據消息 ByteBuf content = Unpooled.copiedBuffer("Hello netty~", CharsetUtil.UTF_8); // 構建一個http response FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content); // 為響應增加數據類型和長度 response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain"); response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes()); // 把響應刷到客戶端 ctx.writeAndFlush(response); }
訪問8088端口,返回"hello netty~"
完整代碼鏈接:https://github.com/ShimmerPig...
服務器定義主從線程與服務端的啟動類
public class WSServer { public static void main(String[] args) throws Exception { EventLoopGroup mainGroup = new NioEventLoopGroup(); EventLoopGroup subGroup = new NioEventLoopGroup(); try { ServerBootstrap server = new ServerBootstrap(); server.group(mainGroup, subGroup) .channel(NioServerSocketChannel.class) .childHandler(new WSServerInitialzer()); ChannelFuture future = server.bind(8088).sync(); future.channel().closeFuture().sync(); } finally { mainGroup.shutdownGracefully(); subGroup.shutdownGracefully(); } } }
創建channel的子處理器WSServerInitialzer
加入相關的助手類handler
public class WSServerInitialzer extends ChannelInitializer{ @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); // websocket 基于http協議,所以要有http編解碼器 pipeline.addLast(new HttpServerCodec()); // 對寫大數據流的支持 pipeline.addLast(new ChunkedWriteHandler()); // 對httpMessage進行聚合,聚合成FullHttpRequest或FullHttpResponse // 幾乎在netty中的編程,都會使用到此hanler pipeline.addLast(new HttpObjectAggregator(1024*64)); // ====================== 以上是用于支持http協議 ====================== // ====================== 以下是支持httpWebsocket ====================== /** * websocket 服務器處理的協議,用于指定給客戶端連接訪問的路由 : /ws * 本handler會幫你處理一些繁重的復雜的事 * 會幫你處理握手動作: handshaking(close, ping, pong) ping + pong = 心跳 * 對于websocket來講,都是以frames進行傳輸的,不同的數據類型對應的frames也不同 */ pipeline.addLast(new WebSocketServerProtocolHandler("/ws")); // 自定義的handler pipeline.addLast(new ChatHandler()); } }
創建自定義的助手類ChatHandler,用于處理消息。
TextWebSocketFrame:在netty中,是用于為websocket專門處理文本的對象,frame是消息的載體。
創建管道組ChannelGroup,用于管理所有客戶端的管道channel。
private static ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
重寫channelRead0方法,通過傳入的TextWebSocketFrame獲取客戶端傳入的內容。通過循環的方法對ChannelGroup中所有的channel進行回復。
@Override protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { // 獲取客戶端傳輸過來的消息 String content = msg.text(); System.out.println("接受到的數據:" + content); // for (Channel channel: clients) { // channel.writeAndFlush( // new TextWebSocketFrame( // "[服務器在]" + LocalDateTime.now() // + "接受到消息, 消息為:" + content)); // } // 下面這個方法,和上面的for循環,一致 clients.writeAndFlush( new TextWebSocketFrame( "[服務器在]" + LocalDateTime.now() + "接受到消息, 消息為:" + content)); }
重寫handlerAdded方法,當客戶端連接服務端之后(打開連接),獲取客戶端的channle,并且放到ChannelGroup中去進行管理。
@Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { clients.add(ctx.channel()); }
重寫handlerRemoved方法,當觸發handlerRemoved,ChannelGroup會自動移除對應客戶端的channel。
@Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { // 當觸發handlerRemoved,ChannelGroup會自動移除對應客戶端的channel // clients.remove(ctx.channel()); System.out.println("客戶端斷開,channle對應的長id為:" + ctx.channel().id().asLongText()); System.out.println("客戶端斷開,channle對應的短id為:" + ctx.channel().id().asShortText()); }客戶端
發送消息:接受消息:
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/101650.html
Netty+SpringBoot+FastDFS+Html5實現聊天App,項目介紹。Netty+SpringBoot+FastDFS+Html5實現聊天App,項目github鏈接。本章完整代碼鏈接。 本章內容 (1) 查詢好友列表的接口 (2)通過或忽略好友請求的接口 (3)添加好友功能展示 查詢好友列表的接口 /** * @Description: 查詢我的好友列表 ...
摘要:實現聊天,項目介紹。首先根據搜索的用戶的名稱查找是否存在這個用戶。如果搜索前置條件為成功,則向前端返回搜索用戶的信息。發送添加好友的請求判斷不能為空查詢用戶接受到的朋友申請最終實現效果 Netty+SpringBoot+FastDFS+Html5實現聊天App,項目介紹。Netty+SpringBoot+FastDFS+Html5實現聊天App,項目github鏈接。本章完整代碼鏈接。...
閱讀 2432·2021-11-22 13:53
閱讀 1126·2021-09-22 16:06
閱讀 1370·2021-09-02 15:21
閱讀 1895·2019-08-30 15:55
閱讀 3116·2019-08-29 11:19
閱讀 1911·2019-08-26 13:23
閱讀 931·2019-08-23 18:23
閱讀 1748·2019-08-23 16:06