Netty入門教程2——動手搭建HttpServer
認識Http請求
在動手寫Netty框架之前,我們先要了解http請求的組成,如下圖:
HTTP request component partsHTTP Request 第一部分是包含的頭資訊- HttpContent 裡面包含的是資料,可以後續有多個 HttpContent 部分
- LastHttpContent 標記是 HTTP request 的結束,同時可能包含頭的尾部資訊
- 完整的 HTTP request,由1,2,3組成
- HTTP response 第一部分是包含的頭資訊
- HttpContent 裡面包含的是資料,可以後續有多個 HttpContent 部分
- LastHttpContent 標記是 HTTP response 的結束,同時可能包含頭的尾部資訊
- 完整的 HTTP response,由1,2,3組成
從request的介紹我們可以看出來,一次http請求並不是通過一次對話完成的,他中間可能有很次的連線。通過上一章我們隊netty的瞭解,每一次對話都會建立一個channel,並且一個ChannelInboundHandler一般是不會同時去處理多個Channel的。
如何在一個Channel裡面處理一次完整的Http請求?這就要用到我們上圖提到的FullHttpRequest,我們只需要在使用netty處理channel的時候,只處理訊息是FullHttpRequest的Channel,這樣我們就能在一個ChannelHandler中處理一個完整的Http請求了。
開始動手
搭建一個Netty伺服器,我們只需要兩個類——一個是啟動類,負責啟動(BootStrap)和main方法,一個是ChannelHandler,負責具體的業務邏輯,我們先從啟動類說起。
package com.dz.netty.http;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
/**
* Created by RoyDeng on 17/7/20.
*/
public class HttpServer {
private final int port;
public HttpServer(int port) {
this.port = port;
}
public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.err.println(
"Usage: " + HttpServer.class.getSimpleName() +
" <port>");
return;
}
int port = Integer.parseInt(args[0]);
new HttpServer(port).start();
}
public void start() throws Exception {
ServerBootstrap b = new ServerBootstrap();
NioEventLoopGroup group = new NioEventLoopGroup();
b.group(group)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch)
throws Exception {
System.out.println("initChannel ch:" + ch);
ch.pipeline()
.addLast("decoder", new HttpRequestDecoder()) // 1
.addLast("encoder", new HttpResponseEncoder()) // 2
.addLast("aggregator", new HttpObjectAggregator(512 * 1024)) // 3
.addLast("handler", new HttpHandler()); // 4
}
})
.option(ChannelOption.SO_BACKLOG, 128) // determining the number of connections queued
.childOption(ChannelOption.SO_KEEPALIVE, Boolean.TRUE);
b.bind(port).sync();
}
}
這個類同上一章中出現的Netty簡易封裝伺服器程式碼類似,不一樣的是這裡使用了多個ChannelHandler,在這裡一一介紹:
- HttpRequestDecoder,用於解碼request
- HttpResponseEncoder,用於編碼response
- aggregator,訊息聚合器(重要)。為什麼能有FullHttpRequest這個東西,就是因為有他,HttpObjectAggregator,如果沒有他,就不會有那個訊息是FullHttpRequest的那段Channel,同樣也不會有FullHttpResponse。
如果我們將z'h
HttpObjectAggregator(512 * 1024)的引數含義是訊息合併的資料大小,如此代表聚合的訊息內容長度不超過512kb。 - 新增我們自己的處理介面
完成啟動類之後,接下來就是我們的業務處理類HttpHandler了,先上程式碼:
package com.dz.netty.http;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.AsciiString;
/**
* Created by RoyDeng on 17/7/20.
*/
public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> { // 1
private AsciiString contentType = HttpHeaderValues.TEXT_PLAIN;
@Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {
System.out.println("class:" + msg.getClass().getName());
DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
HttpResponseStatus.OK,
Unpooled.wrappedBuffer("test".getBytes())); // 2
HttpHeaders heads = response.headers();
heads.add(HttpHeaderNames.CONTENT_TYPE, contentType + "; charset=UTF-8");
heads.add(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()); // 3
heads.add(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
ctx.write(response);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelReadComplete");
super.channelReadComplete(ctx);
ctx.flush(); // 4
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("exceptionCaught");
if(null != cause) cause.printStackTrace();
if(null != ctx) ctx.close();
}
}
該段程式碼需要注意的地方如註釋所示,有以下四點:
- Handler需要宣告泛型為<FullHttpRequest>,宣告之後,只有msg為FullHttpRequest的訊息才能進來。
由於泛型的過濾比較簡單,我們就不改程式碼來驗證了,但是在這裡我們可以利用泛型的特性另外做個小測試,將泛型去掉,並且將HttpServer中.addLast("aggregator", new HttpObjectAggregator(512 * 1024)) // 3
這一行程式碼註釋掉,然後觀察註釋前後的log。
註釋前:
initChannel ch:[id: 0xcb9d8e9e, L:/0:0:0:0:0:0:0:1:8888 - R:/0:0:0:0:0:0:0:1:58855]
class:io.netty.handler.codec.http.HttpObjectAggregator$AggregatedFullHttpRequest
channelReadComplete
註釋後:
initChannel ch:[id: 0xc5415409, L:/0:0:0:0:0:0:0:1:8888 - R:/0:0:0:0:0:0:0:1:58567]
class:io.netty.handler.codec.http.DefaultHttpRequest
class:io.netty.handler.codec.http.LastHttpContent$1
channelReadComplete
channelReadComplete
從中可以看出,如果沒有aggregator,那麼一個http請求就會通過多個Channel被處理,這對我們的業務開發是不方便的,而aggregator的作用就在於此。
- 生成response,這裡使用的FullHttpResponse,同FullHttpRequest類似,通過這個我們就不用將response拆分成多個channel返回給請求端了。
- 新增header描述length。這一步是很重要的一步,如果沒有這一步,你會發現用postman發出請求之後就一直在重新整理,因為http請求方不知道返回的資料到底有多長。
- channel讀取完成之後需要輸出緩衝流。如果沒有這一步,你會發現postman同樣會一直在重新整理。
構建HTTPS服務
首先,構建HTTPS服務需要證書,那麼什麼是SSL證書呢?
SSL 證書就是遵守 SSL協議,由受信任的數字證書頒發機構CA,在驗證伺服器身份後頒發,具有伺服器身份驗證和資料傳輸加密功能。
也就是說,HTTPS相比於HTTP服務,能夠防止網路劫持,同時具備一定的安全加密作用。
一般來說,證書可以在阿里雲、騰訊雲這種雲服務上申請。申請下來之後,證書只能用於指定的域名和伺服器上。
netty有提供SSL加密的工具包,只需要通過新增SslHandler,就能快速搭建。基於上面的程式碼,我們重新定義一個ChannelInitializer。
public class SSLChannelInitializer extends ChannelInitializer<SocketChannel> {
private final SslContext sslContext;
public SSLChannelInitializer() {
String keyStoreFilePath = "/root/.ssl/test.pkcs12";
String keyStorePassword = "[email protected]";
try {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(new FileInputStream(keyStoreFilePath), keyStorePassword.toCharArray());
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, keyStorePassword.toCharArray());
sslContext = SslContextBuilder.forServer(keyManagerFactory).build();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
protected void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
SSLEngine sslEngine = sslContext.newEngine(ch.alloc());
pipeline
.addLast(new SslHandler(sslEngine))
.addLast("decoder", new HttpRequestDecoder())
.addLast("encoder", new HttpResponseEncoder())
.addLast("aggregator", new HttpObjectAggregator(512 * 1024))
.addLast("handler", new HttpHandler());
;
}
}
相關推薦
Netty入門教程2——動手搭建HttpServer
認識Http請求在動手寫Netty框架之前,我們先要了解http請求的組成,如下圖:HTTP request component partsHTTP Request 第一部分是包含的頭資訊HttpContent 裡面包含的是資料,可以後續有多個 HttpContent 部分LastHttpContent 標記
Unix/Linux環境C程式設計入門教程 2 CentOS環境搭建
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
[雪峰磁針石部落格]python 3.7極速入門教程2 Hello與變數
Hello 命令列方式 $ python Python 3.7.0 (default, Jun 28 2018, 13:15:42) [GCC 7.2.0] :: Anaconda, Inc. on linux Type "help", "copyright", "credits" or "license
組合語言入門教程(2)----暫存器篇
目錄 文章目錄 目錄 摘要 1.通用暫存器 2.幾條彙編指令 3.實體地址的方法 4.CS和IP 摘要 本節主要記錄自己學習組合語言的過程。主要參考組合語言這本教材。
ASP.NET Core 入門教程 2、使用ASP.NET Core MVC框架構建Web應用
一、前言 1、本文主要內容 使用dotnet cli建立基於解決方案(sln+csproj)的專案 使用Visual Studio Code開發基於解決方案(sln+csproj)的專案 Visual Studio Code Solution外掛( vscode-solution-explorer)基礎使用
React入門教程2---元素渲染
React 元素渲染 元素是構成 React 應用的最小單位,它用於描述螢幕上輸出的內容。 const element = <h1>Hello, world!</h1>; 與瀏覽器的 DOM 元素不同,React 當中的元素事實上是普通的物件,R
Android直播入門實踐:動手搭建一套簡單的直播系統
轉載自:http://www.52im.net/thread-1154-1-1.html 1、前言 實時視訊直播是這兩年非常火的技術形態,已經滲透到教育、線上互娛等各種業務場景中。但要搭建一套實時視訊直播系統,並非易事,當然相關的直播技術理論在論壇的其它文章裡已經寫的非常詳細,本文不再展開。
Netty入門教程——認識Netty
Netty 什麼是Netty? Netty 是一個利用 Java 的高階網路的能力,隱藏其背後的複雜性而提供一個易於使用的 API 的客戶端/伺服器框架。 Netty 是一個廣泛使用的 Java 網路程式設計框架(Netty 在 2011 年獲得了Duke's Ch
Python爬蟲入門教程 2-100 妹子圖網站爬取
字典 註意 while import 我們 分鐘 基礎 便是 訪問 前言 從今天開始就要擼起袖子,直接寫Python爬蟲了,學習語言最好的辦法就是有目的的進行,所以,接下來我將用10+篇的博客,寫爬圖片這一件事情。希望可以做好。 為了寫好爬蟲,我們需要準備一個火狐瀏覽器,還
生產環境的開源容器管理平臺--Rancher入門教程 2 Host
Rancher是一個用於部署和管理生產環境的容器的開源平臺,它與Kubernetes/Mesos/Docker Swarm進行整合 使得在任何硬體環境上容器化應用變得觸手可及.在這個系列的教程中我們將會學習如何使用rancher, 本文主要用於介紹如何在Rancher的Cattle環境
RabbitMQ workQueues for Java【入門教程 2】
入門教程1 我們學到了 P——>佇列——>C 這種單一的模式。一個生產者對應一個消費者。那麼在實際中可能存在一個生產者對應多個消費者,如在車間裡面的生產線,一個流水線生產的部件可能供應對應多個工人小費。那麼就引入了今天所討論的知識。 工作佇列 我們通過H
Spark2.x 快速入門教程 2
Spark SQL之 Dataframe/Dataset 一、實驗介紹 1.1 實驗內容 從 Spark 2.0 始支援了SQL 2003 準語法。當我們使用某種程式語言開發的 Spark 作業來執行 SQL 時,返回的結果是 Dataframe/Dataset 型
Netty入門教程
Netty入門教程——認識Netty 什麼是Netty? Netty 是一個利用 Java 的高階網路的能力,隱藏其背後的複雜性而提供一個易於使用的 API 的客戶端/伺服器框架。 Netty 是一個廣泛使用的 Java 網路程式設計框架(Netty 在 2011 年獲得了D
p5.js入門教程(2) 小球動畫
意見: 1、CSDN太難用了,稽核太難受,發表了看不了,不方便文章預覽修改格式。 QQ空間待稽核的話自己還是可以看的,求改進! 2、文字編輯也很難用! 一、運動的小球 本節將用p5.js做一個在螢幕上運動的小球。 思路是用變數記錄小球的位置,然後在draw()函式裡對其做
PyQt4入門教程(2)_PyQt4的第一個程式
注:文中譯者的話將用方括號【】標出。 這一部分我們將學習PyQt中一些基本的函式。 一個簡單的例子 這是一個能夠顯示出一個視窗的簡單例子。目前為止我們已經可以對這個窗口乾很多事情了,比如說改變它的尺寸,最大化,最小化……幹這些事情本來需要寫很多程式
EXTJS入門教程及其框架搭建
EXTJS是一個相容AJAX的前臺WEB UI的框架,在普通的HTML檔案的 BODY 元素中無須寫任何HTML程式碼,就能產生相應的表格等元素。 首先是為每一個頁面定義一個類,再以EXTJS的規範格式增加所需的元素,可以使用所見所得的工具:extbuilder 來操作,這個類將以XXXXX.js的
超強、超詳細Redis資料庫入門教程(2)
【本教程目錄】 1.redis是什麼 2.redis的作者何許人也 3.誰在使用redis 4.學會安裝redis 5.學會啟動redis 6.使用redis客戶端 7.redis資料結構 – 簡介 8.redis資料結構 – strings 9.redis資料結構 – l
Netty入門系列(2) --使用Netty解決粘包和拆包問題
前言 上一篇我們介紹瞭如果使用Netty來開發一個簡單的服務端和客戶端,接下來我們來討論如何使用解碼器來解決TCP的粘包和拆包問題 TCP為什麼會粘包/拆包 我們知道,TCP是以一種流的方式來進行網路轉播的,當tcp三次握手簡歷通訊後,客戶端服務端之間就建立了一種通訊管道,我們可以想象成自來水管道,流出來的水
Vue.js 2.0 入門教程(一) 搭建開發環境
最近,前段開發框架層出不窮,響應式和模組化開發框架一下變得炙手可熱起來。在這樣的大浪潮下,我也開始進入vue的學習行列中。 這裡有觀眾會問,為什麼選擇vue呢? 小編作為一個剛入門前端的小白,vue 相比於angular2 ,react,入門簡單容易上手,簡單也是效
【原創 Hadoop&Spark 動手實踐 5】Spark 基礎入門,集群搭建以及Spark Shell
min util man 操作 because tro txt library all Spark 基礎入門,集群搭建以及Spark Shell 主要借助Spark基礎的PPT,再加上實際的動手操作來加強概念的理解和實踐。