做了多年web開發,http真是熟悉的陌生人(經常在用,但是從頭到尾理清楚真的是很有難度的),其實http概述和netty中的http應用真有必要說下,一起聊聊netty實現tomcat的功能,請求服務的功能。
(一)Http協議概述
1.什麽是Http協議
HTTP是一個屬於【應用層】的麵向對象的協議,由於其簡捷、快速的方式,適用於分布式超媒體信息係統。
2.HTTP協議的主要特點
支持客戶/服務器模式。
簡單快速
客戶向服務器請求服務時,隻需傳送請求方法和路徑。請求方法常用的有GET、HEAD、POST。每種方法規定了客戶與
服務器聯係的類型不同。由於HTTP協議簡單,使得HTTP服務器的程序規模小,因而通信速度很快。
靈活
HTTP允許傳輸任意類型的數據對象。正在傳輸的類型由Content-Type加以標記。
無連接
無連接的含義是限製每次連接隻處理一個請求。服務器處理完客戶的請求,並收到客戶的應答後,即斷開連接。采用這種方式可以節省傳輸時間。
無狀態
HTTP協議是無狀態協議。無狀態是指協議對於事務處理沒有記憶能力。缺少狀態意味著如果後續處理需要前麵的信息,則它必須重傳,這樣可能導致每次連接傳送的數據量增大。另一方麵,在服務器不需要先前信息時它的應答就較快。從HTTP協議來講是無狀態的,其實在應用的時候,很多情況通過回話的方式還是有狀態的。
(二)Http協議交互過程
協議交互本質是指協議兩端(客戶端、服務端),互聯網上沒有協議是混亂的,正如如果現實中沒有法律也會打亂一樣。
1.傳輸數據
傳輸數據一般基於TCP/IP 實現,體現到開發語言上就是我們所熟悉的Socket 編程。
2.交換數據
交換數據本質是指,兩端(客戶端、服務端)能各自識別對方所發送的數據。那麽這就需要製定一套【報文編碼】格式,雙方以該格式編碼數據發送給對方。
Http 對應的Request 與Response報文
注: 我們可以通過抓包工具(fiddler)可以直接看到該報文格式。
報文約定好以後兩端都需要對其進行解碼和編碼操作
3.Http協議內容組成
請求方法
方法描述GET請求指定的頁麵信息,並返回實體主體。HEAD類似於get請求,隻不過返回的響應中沒有具體的內容,用於獲取報頭POST向指定資源提交數據進行處理請求(例如提交表單或者上傳文件)。數據被包含在請求體中。POST請求可能會導致新的資源的建立和/或已有資源的修改。PUT從客戶端向服務器傳送的數據取代指定的文檔的內容。DELETE請求服務器刪除指定的頁麵。CONNECTHTTP/1.1協議中預留給能夠將連接改為管道方式的代理服務器。OPTIONS允許客戶端查看服務器的性能。TRACE回顯服務器收到的請求,主要用於測試或診斷。
部分請求頭
請求頭說明Host接受請求的服務器地址,可以是IP:端口號,也可以是域名User-Agent發送請求的應用程序名稱Connection指定與連接相關的屬性,如Connection:Keep-AliveAccept-Charset通知服務端可以發送的編碼格式Accept-Encoding通知服務端可以發送的數據壓縮格式Accept-Language通知服務端可以發送的語言
部分響應頭
響應頭說明Server服務器應用程序軟件的名稱和版本Content-Type響應正文的類型(是圖片還是二進製字符串)Content-Length實體報頭域用於指明實體正文的長度,以字節方式存儲的十進製數字來表示響應正文長度Content-Charset響應正文使用的編碼Content-Encoding響應正文使用的數據壓縮格式Content-Language響應正文使用的語言
部分響應狀態
狀態碼說明200響應成功302跳轉,跳轉地址通過響應頭中的Location屬性指定(JSP中Forward和Redirect之間的區別)400客戶端請求有語法錯誤,不能被服務器識別403服務器接收到請求,但是拒絕提供服務(認證失敗)404請求資源不存在500服務器內部錯誤
(二)基於Netty 實現Http協議過程分析
源碼:https://github.com/limingios/netFuture/tree/master/源碼/『互聯網架構』軟件架構-io與nio線程模型reactor模型(上)(53)/nio
Http協議分為三部分:
1.遠程數據傳輸
2.報文編解碼
3.業務處理
但如果是開發基於Http普通應用,完全沒有必要重複造輪子,,我們隻需實現業務即可。現比較成熟的中間件有:Tomcat、Jetty、Jboos。這些中間有個缺點是較重,如果需要輕量實現可采用:netty 或JDK自還http 實現JDK Http源碼參見:com.sun.net.httpserver.HttpServer
netty 實現http
源碼:nio/http中。
1.初始ServerBootstrap
2.通過ChannelInitializer 初始 pipeline
3.基於SimpleChannelInboundHandler HttpServer處理類
import⠩o.netty.bootstrap.ServerBootstrap;import⠩o.netty.buffer.Unpooled;import⠩o.netty.channel.*;import⠩o.netty.channel.nio.NioEventLoopGroup;import⠩o.netty.channel.socket.ServerSocketChannel;import⠩o.netty.channel.socket.nio.NioServerSocketChannel;import⠩o.netty.channel.socket.nio.NioSocketChannel;import⠩o.netty.handler.codec.http.*;import⠩o.netty.util.concurrent.Future;import⠩o.netty.util.concurrent.GenericFutureListener;import⠪ava.net.ContentHandler;import⠪ava.util.concurrent.ThreadFactory;/**⠪⠃reated⠢y⠩dig8.com⠪/public⠣lass⠈ttpSimpleServer⠻⠯/open⠥動服務⠰ublic⠶oid⠯penServer()⠻⠓erverBootstrap⠢ootstrap⠽⠮ew⠓erverBootstrap();⠢ootstrap.channel(NioServerSocketChannel.class);⠅ventLoopGroup⠢oss⠽⠮ew⠎ioEventLoopGroup(1);⠅ventLoopGroup⠷ork⠽⠮ew⠎ioEventLoopGroup(8);⠢ootstrap.childHandler(new⠃hannelInitializer<NioSocketChannel>()⠻⠀Override⠰rotected⠶oid⠩nitChannel(NioSocketChannel⠣h)⠴hrows⠅xception⠻⠣h.pipeline().addLast("http-decoder",⠮ew⠈ttpRequestDecoder());⠣h.pipeline().addLast("http-aggregator",⠮ew⠈ttpObjectAggregator(65536));⠣h.pipeline().addLast("http-encoder",⠮ew⠈ttpResponseEncoder());⠣h.pipeline().addLast("http-server",⠮ew⠈ttpServerHandler());⠽⠽);⠢ootstrap.group(boss,⠷ork);⠴ry⠻⠃hannelFuture⠦uture⠽⠢ootstrap.bind(8080).sync();⠓ystem.out.println("服務啟動:8080");⠦uture.channel().closeFuture().sync();⠽⠣atch⠨InterruptedException⠥)⠻⠥.printStackTrace();⠽⠦inally⠻⠢oss.shutdownGracefully();⠷ork.shutdownGracefully();⠽⠽⠰rivate⠳tatic⠣lass⠈ttpServerHandler⠥xtends⠓impleChannelInboundHandler⠻⠀Override⠰rotected⠶oid⠣hannelRead0(ChannelHandlerContext⠣tx,⠏bject⠭sg)⠴hrows⠅xception⠻⠆ullHttpResponse⠲esponse⠽⠮ew⠄efaultFullHttpResponse(HttpVersion.HTTP_1_1,⠈ttpResponseStatus.OK);⠲esponse.headers().set(HttpHeaderNames.CONTENT_TYPE,⠢text/html;charset=UTF-8");⠓tring⠨tml⠽⠢<!DOCTYPE⠨tml>n"⠫⠢<html>n"⠫⠢<head>n"⠫⠢⠼meta⠣harset="UTF-8">n"⠫⠢⠼title>hello⠩dig8.com</title>n"⠫⠢</head>n"⠫⠢<body>n"⠫⠢hello⠩dig8.comn"⠫⠢</body>n"⠫⠢</html>";⠲esponse.content().writeBytes(html.getBytes("UTF-8"));⠣tx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);⠽⠽⠰ublic⠳tatic⠶oid⠭ain(String[]⠡rgs)⠻⠈ttpSimpleServer⠳impleServer⠽⠮ew⠈ttpSimpleServer();⠳impleServer.openServer();⠽}
實現過程分析
建立連接讀取消息流
解碼Request
業務處理
編碼Response
返回消息關閉連接
Channel 與 ChannelPipeline
1.Channel:
a. ServerSocketChannel
b. SocketChannel
2.pipeline:一個pipeline 當中包含了多個ChandlerHandler,而且是有順序的
3.ChandlerHandler
a. HttpRequestDecode:解碼請求
b. HttpResponseEncode :編碼返回結果
在 Netty 中每個 Channel 都有且僅有一個 ChannelPipeline 與之對應, 它們的組成關係如下:
一個 Channel 包含了一個 ChannelPipeline, 而 ChannelPipeline 中又維護了一個由 ChannelHandlerContext 組成的雙向鏈表. 這個鏈表的頭是 HeadContext, 鏈表的尾是 TailContext,並且每個 ChannelHandlerContext 中又關聯著一個 ChannelHandler
bootstrap.childHandler(new⠃hannelInitializer<NioSocketChannel>()⠻@Overrideprotected⠶oid⠩nitChannel(NioSocketChannel⠣h)⠴hrows⠅xception⠻ch.pipeline().addLast("http-decoder",⠮ew⠈ttpRequestDecoder());ch.pipeline().addLast("http-aggregator",⠮ew⠈ttpObjectAggregator(65536));ch.pipeline().addLast("http-encoder",⠮ew⠈ttpResponseEncoder());ch.pipeline().addLast("http-server",⠮ew⠈ttpServerHandler());}});
HttpRequest 在netty 當中的表示結構
HttpResponse在netty 當中的結構
PS:說了下http協議和如何通過netty完成http服務

相关文章
发表评论
评论列表