1. 程式人生 > >Netty入門教程2——動手搭建HttpServer

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

認識Http請求

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

HTTP request component partsHTTP Request 第一部分是包含的頭資訊
  1. HttpContent 裡面包含的是資料,可以後續有多個 HttpContent 部分
  2. LastHttpContent 標記是 HTTP request 的結束,同時可能包含頭的尾部資訊
  3. 完整的 HTTP request,由1,2,3組成
HTTP response component parts
  1. HTTP response 第一部分是包含的頭資訊
  2. HttpContent 裡面包含的是資料,可以後續有多個 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,負責具體的業務邏輯,我們先從啟動類說起。

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,在這裡一一介紹:

  1. HttpRequestDecoder,用於解碼request
  2. HttpResponseEncoder,用於編碼response
  3. aggregator,訊息聚合器(重要)。為什麼能有FullHttpRequest這個東西,就是因為有他,HttpObjectAggregator,如果沒有他,就不會有那個訊息是FullHttpRequest的那段Channel,同樣也不會有FullHttpResponse。
    如果我們將z'h
    HttpObjectAggregator(512 * 1024)的引數含義是訊息合併的資料大小,如此代表聚合的訊息內容長度不超過512kb。
  4. 新增我們自己的處理介面

完成啟動類之後,接下來就是我們的業務處理類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被處理,這對我們的業務開發是不方便的,而aggregator的作用就在於此。

  1. 生成response,這裡使用的FullHttpResponse,同FullHttpRequest類似,通過這個我們就不用將response拆分成多個channel返回給請求端了。
  2. 新增header描述length。這一步是很重要的一步,如果沒有這一步,你會發現用postman發出請求之後就一直在重新整理,因為http請求方不知道返回的資料到底有多長。
  3. 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,再加上實際的動手操作來加強概念的理解和實踐。