如今, Netty已用于Internet上的各种应用程序中,以处理数千(即使不是数百万)的聊天对话,包括Minecraft , Twitter和许多其他应用程序在内的多人游戏。 但是,它并没有在开发企业应用程序的企业程序员的头脑中走得很远。
我相信Netty可以引入一种新的功能,这是其他解决方案无法比拟的,因为它具有完全双向的文本和二进制非HTTP数据传输功能,并且比传统的“每线程线程数”支持更多的并发客户端”服务器。
您可能知道Netty在WebSockets方面的能力,但是您知道它可以像传统的Web服务器一样出色地工作吗? 由于其非常周到的设计,通过在其管道中添加适当的处理程序,Netty几乎可以处理任何流量。 它还可以同时处理多种类型,例如同时在同一端口上处理WebSocket和HTTP。 通过将这些结合在一起,程序员可以免于处理诸如CORS(跨源资源共享)之类的麻烦,当浏览器尝试向未从其下载的服务器发出请求时,这些麻烦可以抬头。
净值的力量
为了显示其转换企业应用程序的功能,我整理了一个代码示例,显示了Web的传统示例之一,该示例检索股价。
其他应用程序必须发出AJAX请求,轮询,具有刷新按钮等以更新价格。 WebSockets消除了任何这些需求。 创建持续开放的双向连接后,客户端和服务器都可以在需要时彼此对话,而无需任何协商。 因此,客户端让服务器知道任何用户何时更改标准,并且只要相关数据基于该标准发生变化,服务器就会更新客户端。
- 您可以在此处找到功能齐全的代码。
我为客户端设置了一些基于JSON的协议,以使服务器知道用户的决定。 要将新符号添加到服务器正在监视客户端的列表中,只需要一个简单的调用即可。 这是一个例子:
doSend('{"command":"add", "tickerSymbol":"GOOG"}');
这会将符号添加到列表中。 服务器的下一次更新将在其数据中包括新符号的当前股价(来自Yahoo Finance的REST API)。 删除项目同样容易:
doSend('{"command":"remove", "tickerSymbol":"GOOG"}');
使用这两个命令,客户端可以控制服务器正在为每个用户监视的符号列表。 在Netty处理程序的服务器端,程序员要考虑多个用户的唯一工作是确保为每个新连接创建一个新的处理程序,并且在不共享数据的地方不使用静态成员。 。 除非另有说明,否则Netty假定处理程序不可共享。
让我们看看如何为Netty管道定义处理程序。 这来自StockTickerServer类:
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast("encoder", new HttpResponseEncoder());
p.addLast("decoder", new HttpRequestDecoder());
p.addLast("aggregator", new HttpObjectAggregator(65536));
p.addLast("handler", new StockTickerServerHandler());
}
});
这里的顺序非常重要,因为管道中的每个处理程序都有机会处理(或不处理)数据并将其传递给下一个处理程序。 股票行情处理程序位于底部,因为它是将数据发送回客户端的程序,因此位于管道的末端。 通过创建处理程序的新实例,每个新连接都将获得每个处理程序自己的实例。 如果处理程序是无状态的并且是线程安全的,则可以在适用的情况下使用单例代替以节省内存。 我使用的处理程序中没有一个是可共享的,因此这里没有显示示例。
Netty作为Web服务器
使用一些技巧来使Netty同时处理HTTP和WebSocket通信。
1. StockTickerServerHandler扩展了SimpleChannelInboundHandler <Object>
这告诉Netty,我们希望所有流量都到达此处理程序。 否则,如果只想处理HTTP通信,则可以使用SimpleChannelInboundHandler <FullHttpRequest>;如果只想处理WebSocket通信,则可以使用SimpleChannelInboundHandler <WebSocketFrame>。
2. channelRead0(通道读取为零)方法
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof FullHttpRequest) {
this.handleHttpRequest(ctx, (FullHttpRequest)msg);
} else if (msg instanceof WebSocketFrame) {
this.handleWebSocketFrame(ctx, (WebSocketFrame)msg);
}
}
这使我们能够根据每种协议处理HTTP和WebSocket通信。 handleHttpRequest提供HTML,图像,CSS,JavaScript和所有其他正常的网络流量,handleWebSocketFrame指出了如何处理从客户端发送的自定义消息。
3.哑剧类型
Netty没有内置对处理mime类型的支持,因为WebSocket调用本质上并不需要它们。
我添加了Apache的mime类型文件的稍作修改的版本,并静态加载了它。 我正在负载上进行同步,因为如果需要,Netty可以在池的开头创建很多处理程序,并且构造函数可以同时由多个处理程序执行。 由于该字段是静态的,因此在映射变为非null之前,可以对其进行多次加载。 同步静态锁(不是类的当前实例)可以防止这种情况的发生。
其他详情
handleWebSocketFrame方法处理WebSocket协议定义的不同“已知”帧类型。 收到全文框后,我会将其传递给我创建的接口的实现者,以指定如何处理业务逻辑。
该代码存在于StockTickerMessageHandler中。 它创建一个后台线程来检索股票报价并将其发送给客户端,并处理客户端发送的命令。
那里有一些凌乱的代码,用于处理Yahoo发送的Gzip压缩数据并解析服务返回的JSON,以及一些使用java.util.concurrent类(例如Executor,AtomicBoolean,AtomicReference和CopyOnWriteArrayList)的代码来保持后台线程和Netty处理程序彼此踩踏,因为它们共享有关通道和当前符号列表的详细信息。
我还使用Gson将传入的JSON转换为POJO,以便更轻松地对其进行处理。 除此之外,这只是本例的业务目的。
关于身份验证的注意事项
我没有时间向此示例添加身份验证。 如果这样做的话,我会使用Shiro ,它是一种功能强大的身份验证/授权/密码框架,可与普通应用程序和Web应用程序一起使用。 还缺少HTTPS支持,因为这是检查股票价格的公共应用程序。 有添加HTTPS(和WSS)的例子在这里 。
使用JavaScript WebSockets很难(如果不是不可能的话)的一件事是发送身份验证数据以及升级请求(即调用新的WebSocket(uri))。 因此,通常像通常的网站一样首先发送HTTPS POST并设置auth cookie令牌。 这样,当发送升级请求时,cookie会自动发送。 使用身份验证时,请记住使用HTTPS和WSS而不是HTTP和WS来保护数据。 身份验证到位后,就变成了在必要时检查经过身份验证的用户的问题,注意某些流量应始终通过(HTML,图像等)。
结论
Netty已成为构建新应用程序的一种高性能,改变游戏规则的方法。 通过利用WebSockets提供的功能,当今的企业应用程序可以比现在更具交互性。 我希望您喜欢这个进入Netty的小冒险,并请原谅糟糕的浏览器客户端,我只是没有时间为此示例制作一个不错的Backbone.js客户端应用程序。
谢谢!
翻译自: https://www.javacodegeeks.com/2015/03/netty-a-different-kind-of-websocket-server.html