日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 前端技术 > javascript >内容正文

javascript

Netty+SpringBoot+FastDFS+Html5实现聊天App详解(一)

發(fā)布時(shí)間:2024/4/17 javascript 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Netty+SpringBoot+FastDFS+Html5实现聊天App详解(一) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Netty學(xué)習(xí)

Netty+SpringBoot+FastDFS+Html5實(shí)現(xiàn)聊天App,項(xiàng)目介紹:https://segmentfault.com/a/11...

Netty+SpringBoot+FastDFS+Html5實(shí)現(xiàn)聊天App,項(xiàng)目github鏈接:https://github.com/ShimmerPig...

本章練習(xí)完整代碼鏈接:https://github.com/ShimmerPig...

IO編程與NIO編程

傳統(tǒng)IO編程性能分析

IO編程模型在客戶端較少的情況下運(yùn)行良好,但是對(duì)于客戶端比較多的業(yè)務(wù)來(lái)說(shuō),單機(jī)服務(wù)端可能需要支撐成千上萬(wàn)的連接,IO模型可能就不太合適了。這是因?yàn)樵趥鹘y(tǒng)的IO模型中,每個(gè)連接創(chuàng)建成功之后都需要一個(gè)線程來(lái)維護(hù),每個(gè)線程包含一個(gè)while死循環(huán),那么1w個(gè)連接對(duì)應(yīng)1w個(gè)線程,繼而1w個(gè)while死循環(huán),這就帶來(lái)如下幾個(gè)問(wèn)題:

1.線程資源受限:線程是操作系統(tǒng)中非常寶貴的資源,同一時(shí)刻有大量的線程處于阻塞狀態(tài)是非常嚴(yán)重的資源浪費(fèi),操作系統(tǒng)耗不起。

2.線程切換效率低下:單機(jī)cpu核數(shù)固定,線程爆炸之后操作系統(tǒng)頻繁進(jìn)行線程切換,應(yīng)用性能急劇下降。

3.除了以上兩個(gè)問(wèn)題,IO編程中,我們看到數(shù)據(jù)讀寫是以字節(jié)流為單位,效率不高。

為了解決這三個(gè)問(wèn)題,JDK在1.4之后提出了NIO。下面簡(jiǎn)單描述一下NIO是如何解決以上三個(gè)問(wèn)題的。

線程資源受限

NIO編程模型中,新來(lái)一個(gè)連接不再創(chuàng)建一個(gè)新的線程,而是可以把這條連接直接綁定到某個(gè)固定的線程,然后這條連接所有的讀寫都由這個(gè)線程來(lái)負(fù)責(zé)。
這個(gè)過(guò)程的實(shí)現(xiàn)歸功于NIO模型中selector的作用,一條連接來(lái)了之后,現(xiàn)在不創(chuàng)建一個(gè)while死循環(huán)去監(jiān)聽(tīng)是否有數(shù)據(jù)可讀了,而是直接把這條連接注冊(cè)到selector上,然后,通過(guò)檢查這個(gè)selector,就可以批量監(jiān)測(cè)出有數(shù)據(jù)可讀的連接,進(jìn)而讀取數(shù)據(jù)。

線程切換效率低下

由于NIO模型中線程數(shù)量大大降低,線程切換效率因此也大幅度提高。

IO讀寫以字節(jié)為單位

NIO解決這個(gè)問(wèn)題的方式是數(shù)據(jù)讀寫不再以字節(jié)為單位,而是以字節(jié)塊為單位。IO模型中,每次都是從操作系統(tǒng)底層一個(gè)字節(jié)一個(gè)字節(jié)地讀取數(shù)據(jù),而NIO維護(hù)一個(gè)緩沖區(qū),每次可以從這個(gè)緩沖區(qū)里面讀取一塊的數(shù)據(jù)。




hello netty

完整代碼鏈接:https://github.com/ShimmerPig...

首先定義一對(duì)線程組——主線程bossGroup與從線程workerGroup。
bossGroup——用于接受客戶端的連接,但是不做任何處理,跟老板一樣,不做事。
workerGroup——bossGroup會(huì)將任務(wù)丟給他,讓workerGroup去處理。

//主線程 EventLoopGroup bossGroup = new NioEventLoopGroup(); //從線程 EventLoopGroup workerGroup = new NioEventLoopGroup();

定義服務(wù)端的啟動(dòng)類serverBootstrap,需要設(shè)置主從線程,NIO的雙向通道,與子處理器(用于處理workerGroup),這里的子處理器后面我們會(huì)手動(dòng)創(chuàng)建。

// netty服務(wù)器的創(chuàng)建, ServerBootstrap 是一個(gè)啟動(dòng)類ServerBootstrap serverBootstrap = new ServerBootstrap();serverBootstrap.group(bossGroup, workerGroup) // 設(shè)置主從線程組.channel(NioServerSocketChannel.class) // 設(shè)置nio的雙向通道.childHandler(new HelloServerInitializer()); // 子處理器,用于處理workerGroup

啟動(dòng)服務(wù)端,綁定8088端口,同時(shí)設(shè)置啟動(dòng)的方式為同步的,這樣我們的Netty就會(huì)一直等待,直到該端口啟動(dòng)完畢。

ChannelFuture channelFuture = serverBootstrap.bind(8088).sync();

監(jiān)聽(tīng)關(guān)閉的通道channel,設(shè)置為同步方式。

channelFuture.channel().closeFuture().sync();

將兩個(gè)線程優(yōu)雅地關(guān)閉。

bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully();

創(chuàng)建管道channel的子處理器HelloServerInitializer,用于處理workerGroup。
HelloServerInitializer里面只重寫了initChannel方法,是一個(gè)初始化器,channel注冊(cè)后,會(huì)執(zhí)行里面相應(yīng)的初始化方法。
在initChannel方法中通過(guò)SocketChannel獲得對(duì)應(yīng)的管道,通過(guò)該管道添加相關(guān)助手類handler。
HttpServerCodec是由netty自己提供的助手類,可以理解為攔截器,當(dāng)請(qǐng)求到服務(wù)端,我們需要做解碼,響應(yīng)到客戶端做編碼。
添加自定義的助手類customHandler,返回"hello netty~"

ChannelPipeline pipeline = channel.pipeline(); pipeline.addLast("HttpServerCodec", new HttpServerCodec()); pipeline.addLast("customHandler", new CustomHandler());

創(chuàng)建自定義的助手類CustomHandler繼承SimpleChannelInboundHandler,返回hello netty~
重寫channelRead0方法,首先通過(guò)傳入的上下文對(duì)象ChannelHandlerContext獲取channel,若消息類型為http請(qǐng)求,則構(gòu)建一個(gè)內(nèi)容為"hello netty~"的http響應(yīng),通過(guò)上下文對(duì)象的writeAndFlush方法將響應(yīng)刷到客戶端。

if (msg instanceof HttpRequest) {// 顯示客戶端的遠(yuǎn)程地址System.out.println(channel.remoteAddress());// 定義發(fā)送的數(shù)據(jù)消息ByteBuf content = Unpooled.copiedBuffer("Hello netty~", CharsetUtil.UTF_8);// 構(gòu)建一個(gè)http responseFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);// 為響應(yīng)增加數(shù)據(jù)類型和長(zhǎng)度response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());// 把響應(yīng)刷到客戶端ctx.writeAndFlush(response); }

訪問(wèn)8088端口,返回"hello netty~"




netty聊天小練習(xí)

完整代碼鏈接:https://github.com/ShimmerPig...

服務(wù)器

定義主從線程與服務(wù)端的啟動(dòng)類

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();}}}

創(chuàng)建channel的子處理器WSServerInitialzer
加入相關(guān)的助手類handler

public class WSServerInitialzer extends ChannelInitializer<SocketChannel> {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();// websocket 基于http協(xié)議,所以要有http編解碼器pipeline.addLast(new HttpServerCodec());// 對(duì)寫大數(shù)據(jù)流的支持 pipeline.addLast(new ChunkedWriteHandler());// 對(duì)httpMessage進(jìn)行聚合,聚合成FullHttpRequest或FullHttpResponse// 幾乎在netty中的編程,都會(huì)使用到此hanlerpipeline.addLast(new HttpObjectAggregator(1024*64));// ====================== 以上是用于支持http協(xié)議 ======================// ====================== 以下是支持httpWebsocket ======================/*** websocket 服務(wù)器處理的協(xié)議,用于指定給客戶端連接訪問(wèn)的路由 : /ws* 本handler會(huì)幫你處理一些繁重的復(fù)雜的事* 會(huì)幫你處理握手動(dòng)作: handshaking(close, ping, pong) ping + pong = 心跳* 對(duì)于websocket來(lái)講,都是以frames進(jìn)行傳輸?shù)?#xff0c;不同的數(shù)據(jù)類型對(duì)應(yīng)的frames也不同*/pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));// 自定義的handlerpipeline.addLast(new ChatHandler());}}

創(chuàng)建自定義的助手類ChatHandler,用于處理消息。
TextWebSocketFrame:在netty中,是用于為websocket專門處理文本的對(duì)象,frame是消息的載體。
創(chuàng)建管道組ChannelGroup,用于管理所有客戶端的管道channel。

private static ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

重寫channelRead0方法,通過(guò)傳入的TextWebSocketFrame獲取客戶端傳入的內(nèi)容。通過(guò)循環(huán)的方法對(duì)ChannelGroup中所有的channel進(jìn)行回復(fù)。

@Override protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {// 獲取客戶端傳輸過(guò)來(lái)的消息String content = msg.text();System.out.println("接受到的數(shù)據(jù):" + content);// for (Channel channel: clients) { // channel.writeAndFlush( // new TextWebSocketFrame( // "[服務(wù)器在]" + LocalDateTime.now() // + "接受到消息, 消息為:" + content)); // }// 下面這個(gè)方法,和上面的for循環(huán),一致clients.writeAndFlush(new TextWebSocketFrame("[服務(wù)器在]" + LocalDateTime.now() + "接受到消息, 消息為:" + content));}

重寫handlerAdded方法,當(dāng)客戶端連接服務(wù)端之后(打開(kāi)連接),獲取客戶端的channle,并且放到ChannelGroup中去進(jìn)行管理。

@Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception {clients.add(ctx.channel()); }

重寫handlerRemoved方法,當(dāng)觸發(fā)handlerRemoved,ChannelGroup會(huì)自動(dòng)移除對(duì)應(yīng)客戶端的channel。

@Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {// 當(dāng)觸發(fā)handlerRemoved,ChannelGroup會(huì)自動(dòng)移除對(duì)應(yīng)客戶端的channel// clients.remove(ctx.channel());System.out.println("客戶端斷開(kāi),channle對(duì)應(yīng)的長(zhǎng)id為:" + ctx.channel().id().asLongText());System.out.println("客戶端斷開(kāi),channle對(duì)應(yīng)的短id為:" + ctx.channel().id().asShortText()); }

客戶端

<!DOCTYPE html> <html><head><meta charset="utf-8" /><title></title></head><body><div>發(fā)送消息:</div><input type="text" id="msgContent"/><input type="button" value="點(diǎn)我發(fā)送" onclick="CHAT.chat()"/><div>接受消息:</div><div id="receiveMsg" style="background-color: gainsboro;"></div><script type="application/javascript">window.CHAT = {socket: null,init: function() {if (window.WebSocket) {CHAT.socket = new WebSocket("ws://192.168.1.4:8088/ws");CHAT.socket.onopen = function() {console.log("連接建立成功...");},CHAT.socket.onclose = function() {console.log("連接關(guān)閉...");},CHAT.socket.onerror = function() {console.log("發(fā)生錯(cuò)誤...");},CHAT.socket.onmessage = function(e) {console.log("接受到消息:" + e.data);var receiveMsg = document.getElementById("receiveMsg");var html = receiveMsg.innerHTML;receiveMsg.innerHTML = html + "<br/>" + e.data;}} else {alert("瀏覽器不支持websocket協(xié)議...");}},chat: function() {var msg = document.getElementById("msgContent");CHAT.socket.send(msg.value);}};CHAT.init();</script></body> </html>




測(cè)試

總結(jié)

以上是生活随笔為你收集整理的Netty+SpringBoot+FastDFS+Html5实现聊天App详解(一)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。