认识netty+实战

认识netty+实战

官网:https://netty.io/

一、初识netty

1、Netty 由JBOSS提供的一个开源框架,现为github上的独立项目
2、Netty是一个异步的、基于事件驱动的网络应用框架,用于快速开发高性能、高可靠性的网络IO程序
在这里插入图片描述
3、Netty主要是针对TCP/IP协议下,面向客户端的高并发应用,或者peer-to-peer场景下的大量数据的持续传输的应用。
在这里插入图片描述
4、Netty本质是NIO框架,适用于服务器通讯相关的多种应用场景

二、Netty应用场景

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权威指南:理论原理性较强

四、Netty名词介绍

1、阻塞和非阻塞

1)线程访问资源,该资源是否准备就绪的一种处理方式

在这里插入图片描述
2、BIO

同步阻塞IO,Block lO

在这里插入图片描述
3、NIO

同步非阻塞IO,New lO (Non-block lO)

在这里插入图片描述
4、AIO

异步非阻塞的IO(比较懒,等待别人通知)

Netty线程模型(Reactor线程模型)

一、单线程模型

所有的IO操作都由同一个NIO线程处理的

在这里插入图片描述
适用于小型应用场景。。。

二、多线程模型

由一组NIO线程处理IO操作

在这里插入图片描述
单线程就像是饭店门口的接待员,后面的线程池就像是饭店里面的服务员。。。

三、主从线程模型

在这里插入图片描述

Netty服务器搭建

1)构建一对主从线程池
2)定义服务器启动类
3)为服务器设置channel
4)设置处理从线程池的助手类初始化起
5)监听启动和关闭服务器

首先,创建maven项目,导入netty依赖

<!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
<dependency>
	<groupId>io.netty</groupId>
	<artifactId>netty-all</artifactId>
	<version>4.1.49.Final</version>
</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("助手类移除");
	}
}

测试:启动服务端。开启浏览器查看:
在这里插入图片描述
在这里插入图片描述

Netty实时通信原理

一、实时通信

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());
	}
}

二、websocket api(JS)

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 + "<br/>" + 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

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值