如何开发基于Netty的HTTP/HTTPS应用程序

目录
  • 一、通过 SSL/TLS 保护应用程序
  • 二、HTTP 编解码器
  • 三、聚合 HTTP 消息
  • 四、HTTP 压缩
  • 五、HTTPS
  • 六、WebSocket

一、通过 SSL/TLS 保护应用程序

SSL 和 TLS 安全协议层叠在其他协议之上,用以实现数据安全。为了支持 SSL/TLS,Java 提供了 javax.net.ssl 包,它的 SSLContext 和 SSLEngine 类使得实现解密和加密变得相当简单。Netty 通过一个名为 SsLHandler 的 ChannelHandler 实现了这个 API,其中 SSLHandler 在内部使用 SSLEngine 来完成实际工作

Netty 还提供了基于 OpenSSL 工具包的 SSLEngine 实现,比 JDK 提供的 SSLEngine 具有更好的性能。如果 OpenSSL 可用,可以将 Netty 应用程序配置为默认使用 OpenSSLEngine。如果不可用,Netty 将会退回到 JDK 实现

下述代码展示了如何使用 ChannelInitializer 来将 SslHandler 添加到 ChannelPipeline 中

public class SslChannelInitializer extends ChannelInitializer<Channel> {

    private final SslContext context;
    private final boolean startTls;

    public SslChannelInitializer(SslContext context, boolean startTls) {
        this.context = context;
        this.startTls = startTls;
    }

    @Override
    protected void initChannel(Channel ch) throws Exception {
        SSLEngine engine = context.newEngine(ch.alloc());
        ch.pipeline().addFirst("ssl", new SslHandler(engine, startTls));
    }
}

大多数情况下,Sslhandler 将是 ChannelPipeline 中的第一个 ChannelHandler,这确保了只有在所有其他的 ChannelHandler 将它们的逻辑应用到数据之后,才会进行加密

SSLHandler 具有一些有用的方法,如表所示,例如,在握手阶段,两个节点将相互验证并且商定一种加密方式,你可以通过配置 SslHandler 来修改它的行为,或者在 SSL/TLS 握手一旦完成之后提供通知,握手阶段之后,所有的数据都将会被加密

方法名称 描述
setHandshakeTimeout(long, TimeUnit)
setHandshakeTimeoutMillis(long)
getHandshakeTimeoutMillis()
设置和获取超时时间,超时之后,握手 ChannelFuture 将会被通知失败
setCloseNotifyTimeout(long, TimeUnit)
setCloseNotifyTimeoutMillis(long)
getCloseNotifyTimeoutMillis()
设置和获取超时时间,超时之后,将会触发一个关闭通知并关闭连接,这也会导致通知该 ChannelFuture 失败
handshakeFuture() 返回一个在握手完成后将会得到通知的 ChannelFuture,如果握手先前已经执行过,则返回一个包含了先前握手结果的 ChannelFuture
close()
close(ChannelPipeline)
close(ChannelHandlerContext, ChannelPromise)
发送 close_notify 以请求关闭并销毁底层的 SslEngine

二、HTTP 编解码器

HTTP 是基于请求/响应模式的,客户端向服务器发送一个 HTTP 请求,然后服务器将会返回一个 HTTP 响应,Netty 提供了多种多种编码器和解码器以简化对这个协议的使用

下图分别展示了生产和消费 HTTP 请求和 HTTP 响应的方法

如图所示,一个 HTTP 请求/响应可能由多个数据部分组成,并且总以一个 LastHttpContent 部分作为结束

下表概要地介绍了处理和生成这些消息的 HTTP 解码器和编码器

名称 描述
HttpRequestEncoder 将 HTTPRequest、HttpContent 和 LastHttpContent 消息编码为字节
HttpResponseEncoder 将 HTTPResponse、HttpContent 和 LastHttpContent 消息编码为字节
HttpRequestDecoder 将字节编码为 HTTPRequest、HttpContent 和 LastHttpContent 消息
HttpResponseDecoder 将字节编码为 HTTPResponse、HttpContent 和 LastHttpContent 消息

下述代码中的 HttpPipelineInitializer 类展示了将 HTTP 支持添加到你的应用程序是多么简单 —— 只需要将正确的 ChannelHandler 添加到 ChannelPipeline 中

public class HttpPipelineInitializer extends ChannelInitializer<Channel> {

    private final boolean client;

    public HttpPipelineInitializer(boolean client) {
        this.client = client;
    }

    @Override
    protected void initChannel(Channel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        if (client) {
            // 如果是客户端,则添加 HttpResponseDecoder 处理来自服务器的响应
            pipeline.addLast("decoder", new HttpResponseDecoder());
            // 如果是客户端,则添加 HttpRequestEncoder 向服务器发送请求
            pipeline.addLast("encoder", new HttpRequestEncoder());
        } else {
            // 如果是服务端,则添加 HttpRequestDecoder 处理来自客户端的请求
            pipeline.addLast("decoder", new HttpRequestDecoder());
            // 如果是客户端,则添加 HttpResponseEncoder 向客户端发送响应
            pipeline.addLast("encoder", new HttpResponseEncoder());
        }
    }
}

三、聚合 HTTP 消息

在 ChannelInitializer 将 ChannelHandler 安装到 ChannelPipeline 中之后,你就可以处理不同类型的 HTTPObject 消息了。但由于 HTTP 请求和响应可能由许多部分组成,因此你需要聚合它们以形成完整的消息。Netty 提供了一个聚合器,它可以将多个消息部分合并为 FullHttpRequest 或者 FullHttpResponse 消息

由于消息分段需要被缓冲,直到可以转发下一个完整的消息给下一个 ChannelInboundHandler,所以这个操作有轻微的开销,其所带来的好处就是你可以不必关心消息碎片了

引入这种自动聚合机制只不过是向 ChannelPipeline 中添加另外一个 ChannelHandler 罢了,下述代码展示了如何做到这一点:

public class HttpAggregatorInitializer extends ChannelInitializer<Channel> {

    private final boolean isClient;

    public HttpAggregatorInitializer(boolean isClient) {
        this.isClient = isClient;
    }

    @Override
    protected void initChannel(Channel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        if (isClient) {
            // 如果是客户端,则添加 HttpClientCodec
            pipeline.addLast("codec", new HttpClientCodec());
        } else {
            // 如果是服务器,则添加 HttpServerCodec
            pipeline.addLast("codec", new HttpServerCodec());
        }
        // 将最大的消息大小为 512KB 的 HTTPObjectAggregator 添加到 ChannelPipeline
        pipeline.addLast("aggregator", new HttpObjectAggregator(512 * 1024));
    }
}

四、HTTP 压缩

当使用 HTTP 时,建议开启压缩功能以尽可能多地减小传输数据的大小。虽然压缩会带来一些消耗,但通常来说它都是一个好主意,尤其是对于文本数据而言

Netty 为压缩和解压都提供了 ChannelHandler 实现,它们同时支持 gzip 和 deflate 编码

客户端可以通过提供以下头部信息来指示服务器它所支持的压缩格式

GET /encrypted-area HTTP/1.1

Host: www.example.com

Accept-Encoding: gzip, deflate

然而,需要注意的是,服务器没有义务压缩它所发送的数据

public class HttpCompressionInitializer extends ChannelInitializer<Channel> {

    private final boolean isClient;

    public HttpCompressionInitializer(boolean isClient) {
        this.isClient = isClient;
    }

    @Override
    protected void initChannel(Channel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        if (isClient) {
            // 如果是客户端,则添加 HTTPClientCodec
            pipeline.addLast("codec", new HttpClientCodec());
            // 如果是客户端,则添加 HttpContentDecompressor 以处理来自服务器的压缩内容
            pipeline.addLast("decompressor", new HttpContentDecompressor());
        } else {
            // 如果是服务端,则添加 HttpServerCodec
            pipeline.addLast("codec", new HttpServerCodec());
            // 如果是服务器,则添加 HttpContentDecompressor 来压缩数据
            pipeline.addLast("decompressor", new HttpContentDecompressor());
        }
    }
}

五、HTTPS

启用 HTTPS 只需要将 SslHandler 添加到 ChannelPipeline 的 ChannelHandler 组合中

public class HttpsCodecInitializer extends ChannelInitializer<Channel> {

    private final SslContext context;
    private final boolean isClient;

    public HttpsCodecInitializer(SslContext context, boolean isClient) {
        this.context = context;
        this.isClient = isClient;
    }

    @Override
    protected void initChannel(Channel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        SSLEngine engine = context.newEngine(ch.alloc());
        pipeline.addLast("ssl", new SslHandler(engine));
        if (isClient) {
            pipeline.addLast("codec", new HttpClientCodec());
        } else {
            pipeline.addLast("codec", new HttpServerCodec());
        }
    }
}

六、WebSocket

WebSocket 解决了一个长期存在的问题:既然底层协议(HTTP)是一个请求/响应模式的交互序列,那么如何实时地发布信息呢?AJAX一定程度上解决了这个问题,但数据流仍然是由客户端所发送的请求驱动的

WebSocket 提供了在单个 TCP 连接上提供双向的通信,它为网页和远程服务器之间的双向通信提供了一种替代 HTTP 轮询的方案

要想向你的应用程序添加对于 WebSocket 的支持,你需要将适当的客户端或者服务器 WebSocketChannelHandler 添加到 ChannelPipeline 中。这个类将处理由 WebSocket 定义的称为帧的特殊消息类型,如表所示,WebSocketFrame 可以被归类为数据帧或者控制帧

名称 描述
BinaryWebSocketFrame 数据帧:二进制数据
TextWebSocketFrame 数据帧:文本数据
ContinuationWebSocketFrame 数据帧:属于上一个 BinaryWebSocketFrame 或者 TextWebSocketFrame 的文本或者二进制的数据
CloseWebSocketFrame 控制帧:一个 CLOSE 请求,关闭的状态码以及关闭的原因
PingWebSocketFrame 控制帧:请求一个 PongWebSocketFrame
PongWebSocketFrame 控制帧:对 PingWebSocketFrame 请求的响应

因为 Netty 主要是一种服务器端技术,所以我们重点创建 WebSocket 服务器。下述代码展示了使用 WebSocketChannelHandler 的简单示例,这个类会处理协议升级握手,以及三种控制帧 —— Close、Ping 和 Pong,Text 和 Binary 数据帧将会被传递给下一个 ChannelHandler 进行处理

public class WebSocketServerInitializer extends ChannelInitializer<Channel> {

    @Override
    protected void initChannel(Channel ch) throws Exception {
        ch.pipeline().addLast(
                new HttpServerCodec(),
                new HttpObjectAggregator(65536),
                // 如果被请求的端点是 /websocket,则处理该升级握手
                new WebSocketServerProtocolHandler("/websocket"),
                // TextFrameHandler 处理 TextWebSocketFrame
                new TextFrameHandler(),
                // BinaryFrameHandler 处理 BinaryWebSocketFrame
                new BinaryFrameHandler(),
                // ContinuationFrameHandler 处理 Continuation WebSocketFrame
                new ContinuationFrameHandler());
    }

    public static final class TextFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

        @Override
        protected void messageReceived(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
            // do something
        }
    }

    public static final class BinaryFrameHandler extends SimpleChannelInboundHandler<BinaryWebSocketFrame> {

        @Override
        protected void messageReceived(ChannelHandlerContext ctx, BinaryWebSocketFrame msg) throws Exception {
            // do something
        }
    }

    public static final class ContinuationFrameHandler extends SimpleChannelInboundHandler<ContinuationWebSocketFrame> {

        @Override
        protected void messageReceived(ChannelHandlerContext ctx, ContinuationWebSocketFrame msg) throws Exception {
            // do something
        }
    }
}

以上就是如何开发基于Netty的HTTP/HTTPS应用程序的详细内容,更多关于Netty HTTP/HTTPS的资料请关注我们其它相关文章!

(0)

相关推荐

  • 详解Netty编码器和解码器

    目录 一.java的编解码 二.Netty编解码器 2.1 解码器(Decoder) 2.2 代码实现 2.3 编码器(Encoder) 2.4 代码实现 2.5 测试结果 三.编码解码器Codec 3.1 代码实现: 一.java的编解码 1.编码(Encode)称为序列化, 它将对象序列化为字节数组,用于网络传输.数据持久化或者其它 用途. 2.解码(Decode)称为反序列化,它把从网络.磁盘等读取的字节数组还原成原始对象(通常是原 始对象的拷贝),以方便后续的业务逻辑操作. java序列

  • 如何用Netty实现高效的HTTP服务器

    1 概述 HTTP 是基于请求/响应模式的:客户端向服务器发送一个 HTTP 请求,然后服务器将会返回一个 HTTP 响应.Netty 提供了多种编码器和解码器以简化对这个协议的使用.一个HTTP 请求/响应可能由多个数据部分组成,FullHttpRequest 和FullHttpResponse 消息是特殊的子类型,分别代表了完整的请求和响应.所有类型的 HTTP 消息(FullHttpRequest.LastHttpContent 等等)都实现了 HttpObject 接口. (1) Htt

  • JAVA Netty实现聊天室+私聊功能的示例代码

    功能介绍 使用Netty框架实现聊天室功能,服务器可监控客户端上下限状态,消息转发.同时实现了点对点私聊功能.技术点我都在代码中做了备注,这里不再重复写了.希望能给想学习netty的同学一点参考. 服务器代码 服务器入口代码 package nio.test.netty.groupChat; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.chann

  • 分析Netty直接内存原理及应用

    一.通常的内存模型概述 一般地,系统为了保证系统本身的安全性和健壮性,会将内存从逻辑上隔离成内核区域和用户区域,这很容易理解.因为用户行为不可控性太强,暴露得太多,就容易导致各种神奇的用法,超出系统的控制范围.当然,有的语言是支持直接控制内存的,比如C, 你可以用一个指针,访问内存中的几乎任意位置的数据(除了一些硬件地址).而像汇编,则可以访问任意地址.而这些底层的语言,已经离我们越来越远了,它基本上和普通程序员关系不大了. 用户很多时候的编程控制,都是在用户区域进行的,比如我做一些加减乘除,如

  • Netty结合Protobuf进行编解码的方法

    一般在使用netty时,数据传输的时候都会选择对传输的数据进行编解码,编码后的数据变小, 有利于在有限的带宽下传输更多的数据. 由于java本身序列化的缺点较多(无法跨语言,序列化后的码流太大,序列化的性能太低等),业界主流的编解码框架主要有如下三个: Google的Protobuf Facebook的Thrift JBoss的Marshalling 今天我们简单介绍一下Netty结合google的Protobuf框架进行数据的编解码. 1. 什么是Protobuf? Protobuf全称是Go

  • Netty粘包拆包问题解决方案

    TCP黏包拆包 TCP是一个流协议,就是没有界限的一长串二进制数据.TCP作为传输层协议并不不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行数据包的划分,所以在业务上认为是一个完整的包,可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这就是所谓的TCP粘包和拆包问题. 怎么解决? • 消息定长度,传输的数据大小固定长度,例如每段的长度固定为100字节,如果不够空位补空格 • 在数据包尾部添加特殊分隔符,比如下划线,中划线等 • 将消息分为消息头和

  • Java Netty HTTP服务实现过程解析

    超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议. 在后端开发中接触HTTP协议的比较多,目前大部分都是基于Servlet容器实现的Http服务,往往有一些核心子系统对性能的要求非常高,这个时候我们可以考虑采用NIO的网络模型来实现HTTP服务,以此提高性能和吞吐量,Netty除了开发网络应用非常方便,还内置了HTTP相关的编解码器,让用户可以很方便的开发出高性能的HTTP协议的服务,Spring Webflux默认是使用的N

  • 如何开发基于Netty的HTTP/HTTPS应用程序

    目录 一.通过 SSL/TLS 保护应用程序 二.HTTP 编解码器 三.聚合 HTTP 消息 四.HTTP 压缩 五.HTTPS 六.WebSocket 一.通过 SSL/TLS 保护应用程序 SSL 和 TLS 安全协议层叠在其他协议之上,用以实现数据安全.为了支持 SSL/TLS,Java 提供了 javax.net.ssl 包,它的 SSLContext 和 SSLEngine 类使得实现解密和加密变得相当简单.Netty 通过一个名为 SsLHandler 的 ChannelHandl

  • java基于netty NIO的简单聊天室的实现

    一.为何要使用netty开发 由于之前已经用Java中的socket写过一版简单的聊天室,这里就不再对聊天室的具体架构进行细致的介绍了,主要关注于使用netty框架重构后带来的改变.对聊天室不了解的同学可以先看下我的博客(<JAVA简单聊天室的实现>) 本篇博客所使用的netty版本为4.1.36,完整工程已上传到Github(https://github.com/Alexlingl/Chatroom),其中lib文件夹下有相应的netty jar包和source包,自行导入即可. 1.为何要

  • Java基于Netty实现Http server的实战

    目录 HTTP协议基础知识 Netty的http协议栈 基于Netty实现httpserver HTTP协议基础知识 HTTP(超文本传输协议,英文:HyperText Transfer Protocol,缩写:HTTP)是基于TCP/IP协议的应用层的协议,常用于分布式.协作式和超媒体信息系统的应用层协议. http协议的主要特点:(1)支持CS(客户端/服务器)模式.(2)使用简单,指定URL并携带必要的参数即可.(3)灵活.传输非常多类型的数据对象,通过Content-Type指定即可.(

  • 基于Oracle的高性能动态SQL程序开发

    正在看的ORACLE教程是:基于Oracle的高性能动态SQL程序开发. 摘要:对动态SQL的程序开发进行了总结,并结合笔者实际开发经验给出若干开发技巧. 关键词:动态SQL,PL/SQL,高性能 1. 静态SQLSQL与动态SQL Oracle编译PL/SQL程序块分为两个种:其一为前期联编(early binding),即SQL语句在程序编译期间就已经确定,大多数的编译情况属于这种类型:另外一种是后期联编(late binding),即SQL语句只有在运行阶段才能建立,例如当查询条件为用户输

  • Windows下使用Dev-C++开发基于pthread.h的多线程程序实例

    一.下载Windows版本的pthread 目前最新版本是:pthreads-w32-2-9-1-release.zip. 二.解压pthread到指定目录 我选择的目录是:E:\DEV-CPP\Pthread 完成后,该目录会多出三个文件夹:Pre-built.2,pthreads.2,QueueUserAPCEx. 三.配置Dev-C++编译选项 1)点击"工具"→"编译选项"→"目录"→"c++包含文件",浏览到刚才解压

  • Android开发基于Drawable实现圆角矩形的方法

    本文实例讲述了Android开发基于Drawable实现圆角矩形的方法.分享给大家供大家参考,具体如下: 第一步:写个类继承drawable,重写里面的方法,实现的核心代码在draw里 关键技术:BitmapShader public BitmapShader(Bitmap bitmap,Shader.TileMode tileX,Shader.TileMode tileY) 调用这个方法来产生一个画有一个位图的渲染器(Shader). bitmap 在渲染器内使用的位图 tileX The t

  • Android开发基于ScrollView实现的渐变导航栏效果示例

    本文实例讲述了Android开发基于ScrollView实现的渐变导航栏效果.分享给大家供大家参考,具体如下: 前些日子项目要在原来的页面上加入渐变导航栏的功能,查了很多资料,很多资源都是监听到listview的高度来实现渐变导航栏的效果,可是项目里面很多的界面都是使用ScrollView来实现滑动效果. 实在没办法,就自己写了一个test来实现这个效果. 话不多说,马上看一下思路吧,其实渐变导航栏无非就是改变导航栏的透明度也就是可以设定一个高度,根据这个高度,监听ScrollView滑动的距离

  • JavaWeb开发基于ssm的校园服务系统(实例详解)

    利用Javaweb开发的一个校园服务系统,通过发布自己的任务并设置悬赏金额,有些类似于赏金猎人,在这里分享给大家,有需要可以联系我:2186527424: <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-

  • Android 实例开发基于ArcSoft实现人脸识别

    目录 效果图 激活引擎 第一步配置APP_ID和SDK_KEY 第二步:判断是否添加动态链接库(so文件与jar包) 第三步:判断是否申明所有权限 人脸比对 1:N 第一步:初始化本地人脸库 第二步:初始化引擎和相机 第三步:初始化引擎 第四步:活体检测 人脸注册 切换前置.后置摄像头 尾言 效果图 激活引擎 第一步配置APP_ID和SDK_KEY int activeCode = FaceEngine.activeOnline( ChooseFunctionActivity.this, Par

  • SpringCloud微服务开发基于RocketMQ实现分布式事务管理详解

    目录 消息队列实现分布式事务原理 RocketMQ的事务消息 代码实现 基础配置 发送半消息 执行本地事务与回查 Account-Service消费消息 测试 小结 消息队列实现分布式事务原理 首先让我们来看一下基于消息队列实现分布式事务的原理方案. 柔性事务 发送消息的服务有个OUTBOX数据表,在进行INSERT.UPDATE.DELETE 业务操作时也会给OUTBOX数据表INSERT一条消息记录,这样可以保证原子性,因为这是基于本地的ACID事务. OUTBOX表充当临时消息队列,然后我

随机推荐