构建基于Netty 的HTTP/HTTPS 应用程序
HTTP/HTTPS是最常见的协议套件之一,并且随着智能手机的成功,它的应用也日益广泛,因为对于任何公司来说,拥有一个可以被移动设备访问的网站几乎是必须的。这些协议也被用于其他方面。许多组织导出的用于和他们的商业合作伙伴通信的WebService API 一般也是基于HTTP(S)的。
接下来,我们来看看Netty提供的ChannelHandler,你可以用它来处理HTTP 和HTTPS协议,而不必编写自定义的编解码器。
Netty的HTTP解码器、编码器和编解码器
HTTP是基于请求/响应模式的:客户端向服务器发送一个HTTP请求,然后服务器将会返回一个HTTP响应。Netty提供了多种编码器和解码器以简化对这个协议的使用。下面两图分别展示了生产和消费HTTP 请求和HTTP 响应的方法。
HTTP 请求的组成部分
HTTP 响应的组成部分
HTTP 解码器和编码器
HttpRequestEncoder 将HttpRequest、HttpContent 和LastHttpContent 消息编码为字节
HttpResponseEncoder 将HttpResponse、HttpContent 和LastHttpContent 消息编码为字节
HttpRequestDecoder 将字节解码为HttpRequest、HttpContent 和LastHttpContent 消息
HttpResponseDecoder 将字节解码为HttpResponse、HttpContent 和LastHttpContent 消息
添加HTTP 支持
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) {
pipeline.addLast("decoder", new HttpResponseDecoder());// 如果是客户端,则添加HttpResponseDecoder以处理来自服务器的响应
pipeline.addLast("encoder", new HttpRequestEncoder());// 如果是客户端,则添加HttpRequestEncoder以向服务器发送请求
} else {
pipeline.addLast("decoder", new HttpRequestDecoder());// 如果是服务器,则添加HttpRequestDecoder以接收来自客户端的请求
pipeline.addLast("encoder", new HttpResponseEncoder());// 如果是服务器,则添加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) {
pipeline.addLast("codec", new HttpClientCodec());// 如果是客户端,则添加HttpClientCodec
} else {
pipeline.addLast("codec", new HttpServerCodec());// 如果是服务器,则添加HttpServerCodec
}
pipeline.addLast("aggregator", new HttpObjectAggregator(512 * 1024));// 将最大的消息大小为512KB的HttpObjectAggregator添加到ChannelPipeline
}
}
HTTP 压缩
当使用HTTP时,建议开启压缩功能以尽可能多地减小传输数据的大小。虽然压缩会带来一些CPU时钟周期上的开销,但是通常来说它都是一个好主意,特别是对于文本数据来说。 Netty为压缩和解压缩提供了ChannelHandler实现,它们同时支持gzip和deflate编码。
HTTP 请求的头部信息
客户端可以通过提供以下头部信息来指示服务器它所支持的压缩格式:
GET /encrypted-area HTTP/1.1
Host: www.example.com
Accept -Encoding: gzip, deflate
然而,需要注意的是,服务器没有义务压缩它所发送的数据。
自动压缩http消息代码
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) {
pipeline.addLast("codec", new HttpClientCodec());// 如果是客户端,则添加HttpClientCodec
pipeline.addLast("decompressor", new HttpContentDecompressor());// 如果是客户端,则添 加HttpContentDecompressor以处理来自服务器的压缩内容
} else {
pipeline.addLast("codec", new HttpServerCodec());// 如果是服务器,则添加HttpServerCodec
pipeline.addLast("compressor", new HttpContentCompressor());// 如果是服务器,则添加HttpContentCompressor来压缩数据(如果客户端支持它)
}
}
}
版本注意点
如果你正在使用的是JDK 6 或者更早的版本,那么你需要将JZlib(www.jcraft.com/jzlib/)添加到CLASSPATH 中以支持压缩功能。
对于Maven,请添加以下依赖项:
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jzlib</artifactId>
<version>1.1.3</version>
</depe ndency>
Netty中使用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.addFirst("ssl", new SslHandler(engine));// 将SslHandler添加到ChannelPipeline中以使用HTTPS
if (isClient) {
pipeline.addLast("codec", new HttpClientCodec());// 如果是客户端,则添加HttpClientCodec
} else {
pipeline.addLast("codec", new HttpServerCodec());// 如果是服务器,则添加HttpServerCodec
}
}
}
前面的代码是一个很好的例子,说明了Netty的架构方式是如何将代码重用变为杠杆作用的。只需要简单地将一个ChannelHandler添加到ChannelPipeline中,便可以提供一项新功能,甚至像加密这样重要的功能都能提供。
Netty中使用WebSocket
WebSocket解决了一个长期存在的问题:既然底层的协议(HTTP)是一个请求/响应模式的交互序列,那么如何实时地发布信息呢?AJAX提供了一定程度上的改善,但是数据流仍然是由客户端所发送的请求驱动的。还有其他的一些或多或少的取巧方式,但是最终它们仍然属于扩展性受限的变通之法。
WebSocket规范以及它的实现代表了对一种更加有效的解决方案的尝试。简单地说,WebSocket提供了“在一个单个的TCP连接上提供双向的通信……结合WebSocket API……它为网页和远程服务器之间的双向通信提供了一种替代HTTP轮询的方案。”
也就是说,WebSocket在客户端和服务器之间提供了真正的双向数据交换。我们不会深入地描述太多的内部细节,但是我们还是应该提到,尽管最早的实现仅限于文本数据,但是现在已经不是问题了;WebSocket现在可以用于传输任意类型的数据,很像普通的套接字。
要想向你的应用程序中添加对于WebSocket的支持,你需要将适当的客户端或者服务器WebSocketChannelHandler添加到ChannelPipeline中。这个类将处理由WebSocket定义的称为帧的特殊消息类型。
WebSocketFrame类型
BinaryWebSocketFrame 数据帧:二进制数据
TextWebSocketFrame 数据帧:文本数据
ContinuationWebSocketFrame 数据帧:属于上一个BinaryWebSocketFrame或者TextWebSocketFrame的文本的或者二进制数据
CloseWebSocketFrame 控制帧:一个CLOSE请求、关闭的状态码以及关闭的原因
PingWebSocketFrame 控制帧:请求一个PongWebSocketFrame
PongWebSocketFrame 控制帧:对PingWebSocketFrame请求的响应
因为Netty主要是一种服务器端的技术,所以在这里我们重点创建WebSocket服务器。以下代码清单展示了一个使用WebSocketServerProtocolHandler的简单示例,这个类处理协议升级握手,以及3种控制帧——Close、Ping和Pong。Text和Binary数据帧将会被传递给下一个(由你实现的)ChannelHandler进行处理。以下展示Netty在服务器端支持WebSocket
public class WebSocketServerInitializer extends ChannelInitializer<Channel>{
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(
new HttpServerCodec(),
new HttpObjectAggregator(65536), // 为握手提供聚合的HttpRequest
new WebSocketServerProtocolHandler("/websocket"),// 如果被请求的端点是"/websocket",则处理该升级握手
new TextFrameHandler(),// TextFrameHandler处理TextWebSocketFrame
new BinaryFrameHandler(),// BinaryFrameHandler处理BinaryWebSocketFrame
new ContinuationFrameHandler());// ContinuationFrameHandler处理ContinuationWebSocketFrame public static final class TextFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
@Override
public void channelRead0(ChannelHandlerContext ctx,TextWebSocketFrame msg) throws Exception {
// Handle text frame
}
}
public static final class BinaryFrameHandler extends SimpleChannelInboundHandler<BinaryWebSocketFrame> {
@Override
public void channelRead0(ChannelHandlerContext ctx,BinaryWebSocketFrame msg) throws Exception {
// Handle binary frame
}
}
public static final class ContinuationFrameHandler extends SimpleChannelInboundHandler<ContinuationWebSocketFrame> {
@Override
public void channelRead0(ChannelHandlerContext ctx,ContinuationWebSocketFrame msg) throws Exception {
// Handle continuation frame
}
}
}
}
保护WebSocket
要想为WebSocket 添加安全性,只需要将SslHandler作为第一个ChannelHandler添加到ChannelPipeline中。
关于WebSocket的客户端示例,请参考Netty源代码中所包含的例子:https://github.com/netty/netty/tree/4.1/example/src/main/java/io/netty/example/http/websocketx/client。
构建基于Netty 的HTTP/HTTPS 应用程序的更多相关文章
- Netty 框架学习 —— 基于 Netty 的 HTTP/HTTPS 应用程序
通过 SSL/TLS 保护应用程序 SSL 和 TLS 安全协议层叠在其他协议之上,用以实现数据安全.为了支持 SSL/TLS,Java 提供了 javax.net.ssl 包,它的 SSLConte ...
- 基于Netty的IdleStateHandler实现Mqtt心跳
基于Netty的IdleStateHandler实现Mqtt心跳 IdleStateHandler解析 最近研究jetlinks编写的基于Netty的mqtt-client(https://githu ...
- [WCF安全3]使用wsHttpBinding构建基于SSL与UserName授权的WCF应用程序
上一篇文章中介绍了如何使用wsHttpBinding构建UserName授权的WCF应用程序,本文将为您介绍如何使用wsHttpBinding构建基于SSL的UserName安全授权的WCF应用程序. ...
- Netty学习——基于netty实现简单的客户端聊天小程序
Netty学习——基于netty实现简单的客户端聊天小程序 效果图,聊天程序展示 (TCP编程实现) 后端代码: package com.dawa.netty.chatexample; import ...
- 适合新手:从零开发一个IM服务端(基于Netty,有完整源码)
本文由“yuanrw”分享,博客:juejin.im/user/5cefab8451882510eb758606,收录时内容有改动和修订. 0.引言 站长提示:本文适合IM新手阅读,但最好有一定的网络 ...
- 手写一个类SpringBoot的HTTP框架:几十行代码基于Netty搭建一个 HTTP Server
本文已经收录进 : https://github.com/Snailclimb/netty-practical-tutorial (Netty 从入门到实战:手写 HTTP Server+RPC 框架 ...
- 基于Netty打造RPC服务器设计经验谈
自从在园子里,发表了两篇如何基于Netty构建RPC服务器的文章:谈谈如何使用Netty开发实现高性能的RPC服务器.Netty实现高性能RPC服务器优化篇之消息序列化 之后,收到了很多同行.园友们热 ...
- 基于Netty的私有协议栈的开发
基于Netty的私有协议栈的开发 书是人类进步的阶梯,每读一本书都使自己得以提升,以前看书都是看了就看了,当时感觉受益匪浅,时间一长就又还回到书本了!所以说,好记性不如烂笔头,以后每次看完一本书都写一 ...
- ASP.NET Web Api构建基于REST风格的服务实战系列教程
使用ASP.NET Web Api构建基于REST风格的服务实战系列教程[十]——使用CacheCow和ETag缓存资源 系列导航地址http://www.cnblogs.com/fzrain/p/3 ...
随机推荐
- Android网络请求框架之Retrofit实践
网络访问框架经过了从使用最原始的AsyncTask构建简单的网络访问框架(甚至不能称为框架),后来使用开源的android-async-http库,再到使用google发布的volley库,一直不懈的 ...
- RHEL6.4安装nginx
RHEL6.4安装nginx 下载nginx-1.6.1.tar.gz, 解压进入目录: $ yum install pcre-devel $ ./configure --with-http_ssl_ ...
- CRF资料
与最大熵模型相似,条件随机场(Conditional random fields,CRFs)是一种机器学习模型,在自然语言处理的许多领域(如词性标注.中文分词.命名实体识别等)都有比较好的应用效果.条 ...
- prototype.js 和json.js 冲突
1.冲突简述和分析 prototype.js与json.js并不是完全兼容的.主要冲突在于json.js为Object的原型增加了一个toJSONString的方法. 冲突之一:是prototype中 ...
- 深度学习-conv卷积
过滤器(卷积核) 传统的图像过滤器算子有以下几种: blur kernel:减少相邻像素的差异,使图像变平滑. sobel:显示相邻元素在特定方向上的差异. sharpen :强化相邻像素的差异,使图 ...
- java的finalize方法使用
1. finalize的作用 finalize()是Object的protected方法,子类可以覆盖该方法以实现资源清理工作,GC在回收对象之前调用该方法. finalize()与C++中的析构函数 ...
- Linux下转换文件从GBK到UTF-8
iconv -f gbk -t utf- source-file -o target-file
- 用nodejs实现简单爬虫
前言 本喵最近工作中需要使用node,并也想晋升为全栈工程师,所以开始了node学习之旅,在学习过程中, 我会总结一些实用的例子,做成博文和视频教程,以实例形式来理解体会node的用法,所以跟小猫 ...
- JQuery系统梳理
JQuery在前端网页开发中可以说是非常常用了,它所拥有的强大功能足以让我们完成各式各样的效果. 一.JQuery基础语法 1. 使用JQuery必须先导入jquery.x.x.x.js文件: 2. ...
- IdentityServer4实战 - AccessToken 生命周期分析
一.前言 IdentityServer4实战这个系列主要介绍一些在IdentityServer4(后文称:ids4),在实际使用过程中容易出现的问题,以及使用技巧,不定期更新,谢谢大家关注.使用过id ...