Netty入門教程2——動手搭建HttpServer

在上一章中我們認識了netty潜支,他有三大優(yōu)點:并發(fā)高遍略,傳輸快,封裝好辅肾。在這一章我們來用Netty搭建一個HttpServer,從實際開發(fā)中了解netty框架的一些特性和概念轮锥。

netty.png

認識Http請求

在動手寫Netty框架之前矫钓,我們先要了解http請求的組成,如下圖:


HTTP request component parts
  1. HTTP Request 第一部分是包含的頭信息
  2. HttpContent 里面包含的是數(shù)據(jù)舍杜,可以后續(xù)有多個 HttpContent 部分
  3. LastHttpContent 標記是 HTTP request 的結束新娜,同時可能包含頭的尾部信息
  4. 完整的 HTTP request,由1既绩,2概龄,3組成
HTTP response component parts
  1. HTTP response 第一部分是包含的頭信息
  2. HttpContent 里面包含的是數(shù)據(jù),可以后續(xù)有多個 HttpContent 部分
  3. LastHttpContent 標記是 HTTP response 的結束饲握,同時可能包含頭的尾部信息
  4. 完整的 HTTP response私杜,由1蚕键,2,3組成

從request的介紹我們可以看出來衰粹,一次http請求并不是通過一次對話完成的锣光,他中間可能有很次的連接。通過上一章我們隊netty的了解寄猩,每一次對話都會建立一個channel嫉晶,并且一個ChannelInboundHandler一般是不會同時去處理多個Channel的
如何在一個Channel里面處理一次完整的Http請求田篇?這就要用到我們上圖提到的FullHttpRequest替废,我們只需要在使用netty處理channel的時候,只處理消息是FullHttpRequest的Channel泊柬,這樣我們就能在一個ChannelHandler中處理一個完整的Http請求了椎镣。

開始動手

搭建一個Netty服務器,我們只需要兩個類——一個是啟動類兽赁,負責啟動(BootStrap)和main方法状答,一個是ChannelHandler,負責具體的業(yè)務邏輯刀崖,我們先從啟動類說起惊科。

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

這個類同上一章中出現(xiàn)的Netty簡易封裝服務器代碼類似,不一樣的是這里使用了多個ChannelHandler亮钦,在這里一一介紹:

  1. HttpRequestDecoder馆截,用于解碼request
  2. HttpResponseEncoder,用于編碼response
  3. aggregator蜂莉,消息聚合器(重要)蜡娶。為什么能有FullHttpRequest這個東西,就是因為有他映穗,HttpObjectAggregator窖张,如果沒有他,就不會有那個消息是FullHttpRequest的那段Channel蚁滋,同樣也不會有FullHttpResponse宿接。
    如果我們將z'h
    HttpObjectAggregator(512 * 1024)的參數(shù)含義是消息合并的數(shù)據(jù)大小,如此代表聚合的消息內容長度不超過512kb辕录。
  4. 添加我們自己的處理接口

完成啟動類之后澄阳,接下來就是我們的業(yè)務處理類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();
    }
}

該段代碼需要注意的地方如注釋所示踏拜,有以下四點:

  1. 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被處理涧黄,這對我們的業(yè)務開發(fā)是不方便的篮昧,而aggregator的作用就在于此。

  1. 生成response笋妥,這里使用的FullHttpResponse懊昨,同F(xiàn)ullHttpRequest類似,通過這個我們就不用將response拆分成多個channel返回給請求端了春宣。
  2. 添加header描述length酵颁。這一步是很重要的一步,如果沒有這一步月帝,你會發(fā)現(xiàn)用postman發(fā)出請求之后就一直在刷新躏惋,因為http請求方不知道返回的數(shù)據(jù)到底有多長。
  3. channel讀取完成之后需要輸出緩沖流嚷辅。如果沒有這一步簿姨,你會發(fā)現(xiàn)postman同樣會一直在刷新。

構建HTTPS服務

? 首先潦蝇,構建HTTPS服務需要證書款熬,那么什么是SSL證書呢?

? SSL 證書就是遵守 SSL協(xié)議攘乒,由受信任的數(shù)字證書頒發(fā)機構CA贤牛,在驗證服務器身份后頒發(fā),具有服務器身份驗證和數(shù)據(jù)傳輸加密功能则酝。

? 也就是說殉簸,HTTPS相比于HTTP服務,能夠防止網(wǎng)絡劫持沽讹,同時具備一定的安全加密作用般卑。

? 一般來說,證書可以在阿里云爽雄、騰訊云這種云服務上申請蝠检。申請下來之后,證書只能用于指定的域名和服務器上挚瘟。

? netty有提供SSL加密的工具包叹谁,只需要通過添加SslHandler饲梭,就能快速搭建⊙骈荩基于上面的代碼憔涉,我們重新定義一個ChannelInitializer。

public class SSLChannelInitializer extends ChannelInitializer<SocketChannel> {

    private final SslContext sslContext;

    public SSLChannelInitializer() {
        String keyStoreFilePath = "/root/.ssl/test.pkcs12";
        String keyStorePassword = "Password@123";

        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做http服務器demo的全部代碼和剖析析苫,希望這篇文章能幫到你兜叨,有問題評論區(qū)溝通。

Netty入門教程3——Decoder和Encoder
Netty入門教程4——如何實現(xiàn)長連接

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末衩侥,一起剝皮案震驚了整個濱河市国旷,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌顿乒,老刑警劉巖议街,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異璧榄,居然都是意外死亡特漩,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門骨杂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來涂身,“玉大人,你說我怎么就攤上這事搓蚪「蚴郏” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵妒潭,是天一觀的道長悴能。 經(jīng)常有香客問我,道長雳灾,這世上最難降的妖魔是什么漠酿? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮谎亩,結果婚禮上炒嘲,老公的妹妹穿的比我還像新娘。我一直安慰自己匈庭,他們只是感情好夫凸,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著阱持,像睡著了一般夭拌。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天啼止,我揣著相機與錄音道逗,去河邊找鬼。 笑死献烦,一個胖子當著我的面吹牛,可吹牛的內容都是我干的卖词。 我是一名探鬼主播巩那,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼此蜈!你這毒婦竟也來了即横?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤裆赵,失蹤者是張志新(化名)和其女友劉穎东囚,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體战授,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡页藻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了植兰。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片份帐。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖楣导,靈堂內的尸體忽然破棺而出废境,到底是詐尸還是另有隱情,我是刑警寧澤筒繁,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布噩凹,位于F島的核電站,受9級特大地震影響毡咏,放射性物質發(fā)生泄漏驮宴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一血当、第九天 我趴在偏房一處隱蔽的房頂上張望幻赚。 院中可真熱鬧,春花似錦臊旭、人聲如沸落恼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽佳谦。三九已至,卻和暖如春滋戳,著一層夾襖步出監(jiān)牢的瞬間钻蔑,已是汗流浹背啥刻。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留咪笑,地道東北人可帽。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像窗怒,于是被迫代替她去往敵國和親映跟。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理扬虚,服務發(fā)現(xiàn)努隙,斷路器,智...
    卡卡羅2017閱讀 134,599評論 18 139
  • Netty是一個高性能事件驅動的異步的非堵塞的IO(NIO)框架辜昵,用于建立TCP等底層的連接荸镊,基于Netty可以建...
    我是解憂鴨鋪鴨閱讀 1,301評論 0 2
  • netty常用API學習 netty簡介 Netty是基于Java NIO的網(wǎng)絡應用框架. Netty是一個NIO...
    花丶小偉閱讀 5,988評論 0 20
  • 第一部分Netty的概念及體系結構 Netty是一款用于創(chuàng)建高性能網(wǎng)絡應用程序的高級框架。在第一部分堪置,我們將深入地...
    全能程序猿閱讀 231,314評論 4 46
  • 一天天過得真快躬存,日子在美食與守望里溜走。充實而悠閑晋柱,緊張又松弛优构,期待與渴望…… 2017年9月14日 晴 星期四 ...
    旅京媽媽閱讀 580評論 1 6