關鍵字:使用Netty實現HTTP服務器,使用Netty實現httpserver,Netty Http serverhtml
Netty是一個異步事件驅動的網絡應用程序框架用於快速開發可維護的高性能協議服務器和客戶端。Netty通過精心設計,具備豐富的協議,如FTP,SMTP,HTTP以及各類二進制和基於文本的傳統協議。java
Java程序員在開發web應用的時候,截止2018年大多數公司採用的仍是servlet規範的那一套來開發的,好比springmvc。雖然在2018年Java程序員們能夠選擇使用spring5中的webflux,可是這個轉變沒那麼快。然而,基於servlet那一套的springmvc性能不好,若是你厭煩了,你大可使用netty來實現一個web框架。假設你想使用netty來實現,那麼第一步你得會使用Netty啓動一個HTTP服務器,下面開始吧。git
Netty系列的文章在這裏https://www.cnblogs.com/demingblog/p/9912099.html程序員
本文只是爲了演示如何使用Netty來實現一個HTTP服務器,若是要實現一個完整的,那將是十分複雜的。因此,咱們只實現最基本的,請求-響應。具體來講是這樣的:web
1. 啓動服務 2. 客戶端訪問服務器,如:http://localhost:8081/index 3. 服務器返回 : 你請求的uri爲:/index
netty 的api設計很是好,具備通用性,幾乎就是一個固定模式的感受。server端的啓動和客戶端的啓動代碼十分類似。啓動server的時候指定初始化器,在初始化器中,咱們能夠放一個一個的handler,而具體業務邏輯處理就是放在這一個個的handler中的。寫好的server端代碼以下:redis
import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; import java.net.InetSocketAddress; /** * netty server * 2018/11/1. */ public class HttpServer { int port ; public HttpServer(int port){ this.port = port; } public void start() throws Exception{ ServerBootstrap bootstrap = new ServerBootstrap(); EventLoopGroup boss = new NioEventLoopGroup(); EventLoopGroup work = new NioEventLoopGroup(); bootstrap.group(boss,work) .handler(new LoggingHandler(LogLevel.DEBUG)) .channel(NioServerSocketChannel.class) .childHandler(new HttpServerInitializer()); ChannelFuture f = bootstrap.bind(new InetSocketAddress(port)).sync(); System.out.println(" server start up on port : " + port); f.channel().closeFuture().sync(); } }
server端代碼就這麼多,看起來很長,可是這就是一個樣板代碼,你須要着重留意的就是childHandler(new HttpServerInitializer());
這一行。若是你對netty還不是十分熟悉,那麼你不須要着急把每一行的代碼都看懂。這段代碼翻譯成能夠理解的文字是這樣的:spring
1.bootstrap爲啓動引導器。bootstrap
2.指定了使用兩個時間循環器。EventLoopGroupapi
3.指定使用Nio模式。(NioServerSocketChannel.class)瀏覽器
4.初始化器爲HttpServerInitializer
server啓動代碼就是這麼多,咱們注意看 HttpServerInitializer
作了什麼。
HttpServerInitializer
其實就是一個ChannelInitializer
,在這裏咱們能夠指定咱們的handler。前面咱們說過handler是用來承載咱們具體邏輯實現代碼的地方,咱們須要在ChannelInitializer
中加入咱們的特殊實現。代碼以下:
public class HttpServerInitializer extends ChannelInitializer<SocketChannel>{ @Override protected void initChannel(SocketChannel channel) throws Exception { ChannelPipeline pipeline = channel.pipeline(); pipeline.addLast(new HttpServerCodec());// http 編解碼 pipeline.addLast("httpAggregator",new HttpObjectAggregator(512*1024)); // http 消息聚合器 512*1024爲接收的最大contentlength pipeline.addLast(new HttpRequestHandler());// 請求處理器 } }
上面代碼很簡單,須要解釋的點以下:
1. channel 表明了一個socket. 2. ChannelPipeline 就是一個「羊肉串」,這個「羊肉串」裏邊的每一塊羊肉就是一個 handler. handler分爲兩種,inbound handler,outbound handler 。顧名思義,分別處理 流入,流出。 3. HttpServerCodec 是 http消息的編解碼器。 4. HttpObjectAggregator是Http消息聚合器,Aggregator這個單次就是「聚合,彙集」的意思。http消息在傳輸的過程當中多是一片片的消息片端,因此當服務器接收到的是一片片的時候,就須要HttpObjectAggregator來把它們聚合起來。 5. 接收到請求以後,你要作什麼,準備怎麼作,就在HttpRequestHandler中實現。
上面的展現了 server端啓動的代碼,而後又展現了 server端初始化器的代碼。下面咱們來看看,請求處理的handler的代碼:
public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> { @Override public void channelReadComplete(ChannelHandlerContext ctx) { ctx.flush(); } @Override protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception { //100 Continue if (is100ContinueExpected(req)) { ctx.write(new DefaultFullHttpResponse( HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE)); } // 獲取請求的uri String uri = req.uri(); Map<String,String> resMap = new HashMap<>(); resMap.put("method",req.method().name()); resMap.put("uri",uri); String msg = "<html><head><title>test</title></head><body>你請求uri爲:" + uri+"</body></html>"; // 建立http響應 FullHttpResponse response = new DefaultFullHttpResponse( HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8)); // 設置頭信息 response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=UTF-8"); //response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8"); // 將html write到客戶端 ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); } }
上面代碼的意思,我用註釋標明瞭,邏輯很簡單。不管是FullHttpResponse
,仍是 ctx.writeAndFlush
都是netty的api,看名知意便可。半蒙半猜之下,也能夠看明白這個handler實際上是:1 .獲取請求uri,2. 組裝返回的響應內容,3. 響應對融到客戶端。須要解釋的是100 Continue的問題:
100 Continue含義 HTTP客戶端程序有一個實體的主體部分要發送給服務器,但但願在發送以前查看下服務器是否會接受這個實體,因此在發送實體以前先發送了一個攜帶100 Continue的Expect請求首部的請求。 服務器在收到這樣的請求後,應該用 100 Continue或一條錯誤碼來進行響應。
上面的代碼寫完了,看的再多,不如運行起來跑一把。上面的3個類已經包含了咱們目標中的服務端的完整代碼。咱們只須要在main函數中將其啓動便可,代碼以下:
public class Application { public static void main(String[] args) throws Exception{ HttpServer server = new HttpServer(8081);// 8081爲啓動端口 server.start(); } }
運行這個main函數,在瀏覽器中訪問:http://localhost:8081/index ,http://localhost:8081/text/test 試試看吧。
至此,咱們基於Netty的簡易的Http服務器實現了(若是能夠稱做「HTTP服務器」的話)。 假如咱們想要實現,訪問 /index.html
就返回index.html
頁面,訪問/productList
就返回「商品列表JSON」,那麼咱們還須要作請求路由,還要加入JSON序列化支持,還要根據不一樣的請求類型調整HTTP響應頭。本篇就不作展開了,本篇的目標是爲了試下一個最簡單的Http服務器。源碼下載
使用Netty實現HTTP服務器
Netty實現心跳機制
Netty開發redis客戶端,Netty發送redis命令,netty解析redis消息
Netty系列
spring如何啓動的?這裏結合spring源碼描述了啓動過程
SpringMVC是怎麼工做的,SpringMVC的工做原理
spring 異常處理。結合spring源碼分析400異常處理流程及解決方法
Mybatis Mapper接口是如何找到實現類的-源碼分析