原文地址:http://adolgarev.blogspot.com/2013/12/lets-do-our-own-full-blown-http-server.html

Sometimes servlets just doesn't fit you, sometimes you need to support some protocols except HTTP, sometimes you need something really fast. Allow me to show you the Netty that can suit for these needs.

Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. (http://netty.io/)

Netty has everything one needs for HTTP, thus web server on Netty is like a low hanging fruit.
First of all you need to understand what pipeline is, see Interface ChannelPipeline. Pipeline is like processing line where various ChannelHandlers convert input bytes into output. Pipeline corresponds one to one to connection, thus ChannelHandlers in our case will convert HTTP Request into HTTP Response, handlers will be responsible for such auxiliary things like parsing incoming packets and assembling outcoming and also call business logic to handle requests and produce responses.
Full source is available.

There are 2 types of handlers.

  1. ChannelInboundHandlers that process incoming data.

  2. ChannelOutboundHandlers that produce data to be sent.

See How an event flows in a pipeline.
In our case we'll create following inbound handlers

--(ByteBuf)--> HttpRequestDecoder --(HttpObject)--> HttpObjectAggregator --(FullHttpRequest)--> RequestDecoder* --(Request*)--> FormPayloadDecoder* --(FullDecodedRequest*)--> DefaultHandler* --(Response*)--> DefaultExceptionHandler*

In braces you see messages being passed between handlers. With * marked our custom classes, all others are from Netty.

  • HttpRequestDecoder is responsible for low level parsing and converts raw bytes into HttpObject that correspond to HTTP request header and body chunks.

  • HttpObjectAggregator composes from these objects one single FullHttpRequest per HTTP request. It can be configured with maxContentLength to prevent too big requests.

  • RequestDecoder simply wraps FullHttpRequest into custom Request and adds auxiliary parameters namely orderNumber. HTTP client can send multiple requests in one connection and it expects responses in the same order. We process those requests in parallel and for instance second one can be processed before first. Thus we need to assembly responses in the right order before sending them out. For this purpose we assign index number to each incoming Request in our pipeline.

Example. Imagine to process request we just sleep specified amount of seconds.

telnet localhost 9999
Trying ::1...
Connected to localhost.
Escape character is '^]'.
GET /?duration=5 HTTP/1.1 <-- sleep for 5 seconds
User-Agent: curl/7.33.0
Host: localhost:9999
Accept: */*GET /?duration=3 HTTP/1.1 <-- sleep for 3 seconds
User-Agent: curl/7.33.0
Host: localhost:9999
Accept: */*HTTP/1.1 200 OK <-- despite second request processed first we get both responses in the right order after 5 seconds
Content-Type: application/json
Content-Length: 18
Set-Cookie: JSESSIOINID=e9ma1foeretnh19u4demqta7tr
Connection: keep-alive"Slept for 5 secs"HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 18
Set-Cookie: JSESSIOINID=8ti5pbfc0dmd4r09i6or005r6b
Connection: keep-alive"Slept for 3 secs"
  • FormPayloadDecoder implements incoming parameters parsing. It uses QueryStringDecoder and HttpPostRequestDecoder from netty to parse application/x-www-form-urlencoded and multipart/form-data encoded GET and POST data. It produces FullDecodedRequest that differs from Request by containing Values - simple key-value mapping for parameter names and values. You can easily replace FormPayloadDecoder with any other handler that converts Request into FullDecodedRequest, for instance, parse JSON encoded data in POST body.

Sidenote. 'Why we cannot just update Request object like Request.setValues() and pass it further?' you say. There is a good reason to keep messages being passed immutable in multithreaded application (even though a connection is bound to particular thread in netty). Even more, type gives you an information about what you actually have. FullDecodedRequest clearly says that parameters were parsed, in case we make Request object mutable and set there Values one cannot say at any given point in time whether Request already contains Values or not. DefaultHandler in its turn requires FullDecodedRequest as input, in other words it says give me fully parsed request object with parameters. In case of mutable Request this restriction is not clear.

  • DefaultHandler uses a separate set of threads to handle business logic. It uses netty EventExecutorGroup that is like ExecutorService in Java SE. EventExecutorGroup provides methods to submit tasks and returns futures to observe (in contrast to Java SE Future one can add listeners to netty Future). Provider below is a callable that given input parameters produces some output object.

protected void channelRead0(final ChannelHandlerContext ctx,final FullDecodedRequest decodedRequest) throws Exception {Callable<? extends Object> callable = new Provider(decodedRequest.getPath(), decodedRequest.getValues());final Future<? extends Object> future = executor.submit(callable);future.addListener(new GenericFutureListener<Future<Object>>() {@Overridepublic void operationComplete(Future<Object> future)throws Exception {if (future.isSuccess()) {ctx.writeAndFlush(new Response(decodedRequest.getRequest(),future.get()));} else {ctx.fireExceptionCaught(future.cause());}}});
}
  • DefaultExceptionHandler is a catch all handler that receives all exceptions from any handler above, makes pretty output from java stack trace and closes connection.

Now, given a result object, one need to produce output in HTTP way. For this purpose following outbound handlers are used.

--(Response*)--> JacksonJsonResponseEncoder* --(FullEncodedResponse*)--> ResponseEncoder* --(FullHttpResponse)--> HttpResponseEncoder --(ByteBuf)-->
  • Response contains result and reference to request. JacksonJsonResponseEncoder as you may guess uses Jackson library to convert response to JSON, produces FullEncodedResponse that contains FullHttpResponse with appropriate Content-Type and Content-Length set. You can replace JacksonJsonResponseEncoder with any other way to encode response, for instance, to xml or depending to Accept-Encoding request header.

Sidenote about the naming. If you have some interface lets say example.Showable don't call it example.IShowable or example.ShowableI (find out by yourself why). Also do not call implementations of this interface like example.ShowableImpl or example.impl.Showable. Concrete implementation of the interface must be specific in something that differs it from all other implementations (if there is only one implementation you are trying to trick yourself that you are loosely coupled). Reflect its specifics in its name like example.ShowableWhiteBoard, example.ShowablePixelBitmap.

  • ResponseEncoder evicts FullHttpResponse from FullEncodedResponse and handles such aspects as cookies, session ids, keep-alive, etc. Also it assembles responses in the right order.

  • HttpResponseEncoder is a netty thing that obviously produces raw bytes from FullHttpResponse. Pay attention at ReferenceCountUtil.release(httpRequest). Netty uses pool of buffers and reference counting to omit problems with GC. See Reference counted objects.

All those handlers are composed together into pipeline by DefaultServerInitializer. Feel free to browse the source code. Also take a look at various handlers from the box that you may find useful like HttpContentCompressor or SslHandler.

转载于:https://www.cnblogs.com/davidwang456/p/5124998.html

Let's do our own full blown HTTP server with Netty--转载相关推荐

  1. 在github上托管Maven存储库

    本文翻译自:Hosting a Maven repository on github I have a fork of a small open sourced library that I'm wo ...

  2. redhat linux 关闭服务,Redhat用户使用chkconfig关闭不必要服务

    Redhat用户使用chkconfig关闭不必要服务 发布时间:2008-01-01 21:28:45来源:红联作者:qdtantao 在Redhat Linux上你可以使用chkconfig方便的停 ...

  3. MySQL中定义fk语句_MySQL基础篇/第3篇:MySQL基本操作语句.md · qwqoo/MySQL-Review - Gitee.com...

    ### 第3篇:MySQL基本操作语句 - MySQL基础操作 #### 排序检索数据 - 之前的数据没有进行排序,其是按照默认在数据表中的数据返回的 - SELECT语句的ORDER BY 子句进行 ...

  4. 大学可以学前端开发_所有开发人员在大学中应该学习的东西

    大学可以学前端开发 忘记"代码行" (Forget About "Lines of Code") Source 资源 As a developer, you'l ...

  5. react引入多个图片_重新引入React:v16之后的每个React更新都已揭开神秘面纱。

    react引入多个图片 In this article (and accompanying book), unlike any you may have come across before, I w ...

  6. 学习sql注入:猜测数据库_对于SQL的热爱:为什么要学习它以及它将如何帮助您...

    学习sql注入:猜测数据库 I recently read a great article by the esteemed @craigkerstiens describing why he feel ...

  7. 可视化编码_Modulz简介:可视编码的下一步

    可视化编码 by Colm Tuite 通过Colm Tuite Modulz简介:可视编码的下一步 (Introducing Modulz: The Next Step in Visual Codi ...

  8. esl8266开发之旅_从ESL老师到越南软件开发人员的旅程

    esl8266开发之旅 by alberto montalesi 通过阿尔贝托·蒙塔莱西 从ESL老师到越南软件开发人员的旅程 (My Journey from an ESL Teacher to S ...

  9. travis-ci自动部署_如何使用Travis CI部署(几乎)零恐惧的Cloud Foundry应用

    travis-ci自动部署 by Robin Bobbitt 罗宾·波比(Robin Bobbitt) 如何使用Travis CI部署(几乎)零恐惧的Cloud Foundry应用 (How to d ...

最新文章

  1. CentOS 7 安装jdk
  2. CSS选择器详解(一)常用选择器
  3. String转Double
  4. [css] 在页面中的应该使用奇数还是偶数的字体?为什么呢?
  5. 双向链表逆置c语言,【C++】实现双向链表的所有操作,包括逆置双链表(三种方法)...
  6. 阿里巴巴下一代云分析型数据库AnalyticDB入选Forrester Wave™ 云数仓评估报告 解读
  7. java 删除图形界面_Java图形化界面报错?
  8. AES加密,解决了同步问题,和随机密钥和固定密钥,多端通信加密不一致解决办法...
  9. Javascript 清空input type=file 的值方法
  10. [原创] Ubuntu 安装vim与中文帮助文档
  11. 《Think Python》第15章学习笔记
  12. SSM框架使用拦截器和过滤器实现登录的拦截
  13. 658. 一元二次方程公式
  14. 数据结构 队列的结构特点及基本操作
  15. 大智慧行情服务器文件夹,大智慧行情分析系统炒股软件常用三大菜单操作
  16. 干货!深度学习模型的水印和验证
  17. 如何快速搭建一个简单图像搜索引擎
  18. Android基础面试题
  19. vue组件挂载与html加载区别,vue中的挂载是什么意思?
  20. 【C++FunCode】基于Funcode使用C++语言编写小游戏(小鲨鱼历险记)

热门文章

  1. 安卓代码拉下来编译后怎么运行_支付宝秒开是因为用了方舟编译器?官方回应...
  2. php mysql 性能测试工具_高性能MySQL–MySQL基准测试
  3. linux qos 实现机制,linux的qos机制 - cgroup篇 (4)
  4. php 接受数组_PHP接收前端发送的数组
  5. Oracle的三种循环
  6. html tr中可以有br吗,html table tr td br 什么意思 缩写
  7. html js 做的小游戏,用js做一个小游戏平台 (一)
  8. jet nano 车道识别
  9. tf.nn.conv2d 与tf.layers.conv2d的区别
  10. linux 解包与打包