官网:https://netty.io/
1、Netty 由JBOSS提供的一个开源框架,现为github上的独立项目
2、Netty是一个异步的、基于事件驱动的网络应用框架,用于快速开发高性能、高可靠性的网络IO程序
3、Netty主要是针对TCP/IP协议下,面向客户端的高并发应用,或者peer-to-peer场景下的大量数据的持续传输的应用。
4、Netty本质是NIO框架,适用于服务器通讯相关的多种应用场景
1、互联网行业
1)互联网行业∶在分布式系统中,主节点之间需要远程服务调用,高性能的RPC框架比不可少的一部分,Netty作为异步高性能的通信框架,往往作为基础通信组件这些RPC框架所使用。
2)典型应用:阿里的分布式服务框架Dubbo的RPC框架使用Dubbo协议进行节点通信,Dubbo协议默认使用Netty作为基础通信组件,用于实现各个进程节点之间的内部通信。
2、游戏行业
1)无论手游服务端还是大型的网络游戏
2)Netty 作为高性能的基础通信组件,提供了TCP/UDP和HTTP协议栈,方便定制和开发私有协议栈
3)地图服务器之间可以方便的通过Netty进行高性能通信
3、大数据领域
1)经典的Hadoop的高性能通信和序列化组件(AVRO实现数据文件共享)的RPC框架,默认采用Netty进行跨节点的通信。
2)Netty Service基于Netty框架第二次封装实现。
4、其他开源项目使用到netty
https://netty.io/wiki/related-projects.html
1、Netty Inaction:实战性比较强
2、Netty权威指南:理论原理性较强
1、阻塞和非阻塞
1)线程访问资源,该资源是否准备就绪的一种处理方式
同步阻塞IO,Block lO
同步非阻塞IO,New lO (Non-block lO)
异步非阻塞的IO(比较懒,等待别人通知)
所有的IO操作都由同一个NIO线程处理的
由一组NIO线程处理IO操作
单线程就像是饭店门口的接待员,后面的线程池就像是饭店里面的服务员。。。
1)构建一对主从线程池
2)定义服务器启动类
3)为服务器设置channel
4)设置处理从线程池的助手类初始化起
5)监听启动和关闭服务器
首先,创建maven项目,导入netty依赖
<dependency>
<groupId>io.nettygroupId>
<artifactId>netty-allartifactId>
<version>4.1.49.Finalversion>
dependency>
创建服务器类:实现客户端发送请求,服务器给予响应:
public class HelloNettyServer {
public static void main(String[ ] args) throws Exception {
//创建一组线程池组
//主线程池:用于接受客户端的请求链接,不做任何处理
EventLoopGroup group1 = new NioEventLoopGroup();
//从线程池:主线程池会把任务交给它,让其做任务
EventLoopGroup group2 = new NioEventLoopGroup();
try {
//创建服务器启动类
serverBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(group1,group2)//设置主从线程组
.channel(NioServerSocketChannel.class)//设置nio双向通道
.childHandler(null);//添加子处理器,用于处理从线程池的任务
//启动服务,并且设置端口号,同时启动方式为同步
ChannelFuture channelFuture = serverBootstrap.bind(8888).sync();//监听关闭的channel,设置为同步方式
channelFuture.channel().closeFuture().sync();
}finally{
group1.shutdownGracefully();
group2.shutdownGracefully();
}
}
设置channel初始化器:
每一个channel由多个handler共同组成的管道(pipeline) ;
初始化器,channel注册之后,会执行里边的响应的初始化方法:
public class HelloNettyServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
//通过SocketChannel去获得对应的管道
ChannelPipeline pipeline = channel.pipeline();
/**
* 通过管道添加handler
* HttpServerCodec:是由netty自己提供的助手类,可以理解为拦截器
* 当请求到服务器,我们需要解码,响应到客户端做编码
*/
pipeline.addLast("HttpServerCodec",new HttpServerCodec());
//添加自定义助手类,给客户端浏览器渲染hello netty~
pipeline.addLast("CustomHandler",new CustomHandler());
}
}
serverBootstrap.group(group1,group2)//设置主从线程组
.channel(NioServerSocketChannel.class)//设置nio双向通道
.childHandler(HelloNettyServerInitializer);//添加子处理器,用于处理从线程池的任务
自定义助手类:
public class CustomHandler extends SimpleChannelInboundHandler<Http0bject> {
@Override
protected void channelRead0(ChannelHandlerContext ctx,HttpObject msg) throws Exception {
//获取channel
Channel channel = ctx.channel();
if(msg instanceof HttpRequest){ //判断一下是否是HTTP请求
//在控制台打印远程地址
System.out.println(channel.remoteAddress());
//定义向客户端发送的数据内容
ByteBuf content = Unpooled.copiedBuffer("hello netty~",CharsetUtil.UTF_8);
//构建http response
FullHttpResponse response= new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,HttpResponseStatus.OK,content);
//为响应增加数据类型和长度
response.headers().set(HttpHeaderNames.CONTENT_TYPE,"text/plain");
response.headers().set(HttpHeaderNames.CONTENT_LENGTH,content.readableBytes());
//把响应渲染到html客户端页面上
ctx.writeAndFlush(response);
}
}
//生命周期======================================================================================
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("channel注册");
}
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception
System.out.println( "channel移除");
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channel活跃");
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out. println( "channel不活跃");
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx,0bject evt) throws Exception {
System.out.println("用户事件触发");
}
@Override
public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
System.out.println("channel可写更改");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx,Throwable cause) throws Exception {
System.out.println("捕获到异常");
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
System.out.println("助手类添加");
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
System.out.println("助手类移除");
}
}
1、Ajax轮询
2、long pull
3、websocket (目前主要)
- 3.1 持久化的协议
- 3.2 主动实时反馈服务端信息
写一个WebSocketServer:
public class webSocketServer{
public static void main(String[] args) throws Exception {
//创建主从线程池
EventLoopGroup mainGroup = new NioEventLoopGroup();
EventLoopGroup subGroup = new NioEventLoopGroup(;
try {
//创建服务器类
ServerBootstrap server = new ServerBootstrap();
server.group(mainGroup,subGroup)
.channel(NioserverSocketChannel.class)
.childHandler(new WSServerInitialzer());
ChannelFuture future = server.bind(8088).sync();
future.channel().closeFuture().sync();
}finally {
mainGroup.shutdownGracefully();
subGroup.shutdownGracefully();
}
}
public class WSServerInitialzer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
//获取管道(pipeline)
ChannelPipeline pipeline = channel.pipeline() ;
// websocket基于http协议,所需要的http编解码器
pipeline.addLast(new HttpServerCodec());
//在http上有一些数据流产生,有大有小,我们对其进行处理,既然如此,我们需要使用netty对下数据流写提供支持,这个类叫ChunkedWriteHandler
pipeline.addLast(new ChunkedWriteHandler());
//对httpMessage进行聚合处理,聚合成request或response
pipeline.addLast(new HttpObjectAggregator(1024*64));
/**
*本handler会帮你处理一些繁重复杂的事情
*会帮你处理握手动作: handshaking (close、ping、pong) ping+pong =心跳
*对于websocket来讲,都是以frams进行传输的,不同的数据类型对应的fnams也不同
*/
pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
//自定义的handler
pipeline.addLast(new ChatHandler());
}
}
用于处理消息的handler:
由于它的传输数据的载体是frame,这个frame 在netty中,是用于为websocket专门处理文本对象的,frame是消息的载体,此类叫:TextWebsocketFrame
public class ChatHandler extends SimpleChannelInboundHandler<TextWebSocketFrame>{
//用于记录和管理所有客户端的channel
private static ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
@Override
protected void channelRead0(ChannelHandlerContext ctx,TextWebSocketFrame msg) throws Exception {
//获取客户端所传输的消息
String content = msg.text();
System.out.println("接收到的数据:"+content);
//将数据刷新到客户端上
clients.writeAndFlush(
new TextWebSocketFrame(
"[服务器在:]"+ LocalDateTime.now()
+"接收到消息,消息内容为: "+content));
}
@Override
public void hanglerAdded(ChannelHandlerContext ctx) throws Exception {
clients.add(ctx.channel());
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
//clients.remove(ctx.channel());
System.out.println("客户端断开,channel对应的长Id为:"+ctx.channel().id().asLongText());
System.out.println("客户端断开,channel对应的短Id为:"+ctx.channel().id().asShortText());
}
}
1) var socket = new WebSocket("ws://ip:[port]");
2) 生命周期: onopen()
(只触发一次),onmessage()
只要二者建立连接,服务端向客户端推送消息,就会触发此函数,onerror()
,onclose()
。
3) 主动方法:Socket.send()
, Socket.close()
;
DOCTYPE html><html>
<head>
<meta charset="utf-8">
<title>Netty实时通信title>
head>
<body>
发送消息:<input type="text" id="msgcontent"/>
<input type="button" value="发送消息" onclick=“CHAT.chat()”/>
<hr />
接收消息:
<div id="receiveMsg">div>
<script type="text/javascript">
window.CHAT = {
socket: null,
init:function(){
//判断浏览器是否支持websocket
if(window.webSocket){
//创建websocket对象
CHAT.socket = new webSocket("ws://192.168.0.101:8088/ws");
CHAT.socket.onopen = function(){
console.log("链接建立成功");
},
CHAT.socket.close=function(){
console.log("链接关闭");
},
CHAT.socket.onerror = function(){
console.log("发生异常"");
},
CHAT.socket.onmessage = function(e){
console.log("接受消息:"+e.data);
var receiveMsg = document.getElementById("receiveMsg");
var html= receiveMsg.innerHTML;//获取本对象原有的内容
//嵌入新的内容
receiveMsg.innerHTML= html + "
" + e.data;
},
}else{
console.log("您的浏览器不支持websocket协议");
},
chat:function(){
//获取发送消息框中所输入内容
var msgContent = document.getElementById("msgContent").value;
//将客户输入的消息进行发送
CHAT.socket.send(msgContent);
};
CHAT.init( );
script>
body>
html>
启动后台WebSocket服务:
【参考】https://www.bilibili.com/video/BV1Zf4y137sY?p=23