NIO同步阻塞与同步非阻塞

BIO与NIO

IO(BIO)和NIO区别:其本质就是阻塞和非阻塞的区别

阻塞概念:应用程序在获取网络数据的时候,如果网络传输数据很慢,就会一直等待,直到传输完毕为止。

非阻塞概念:应用程序直接可以获取已经准备就绪好的数据,无需等待。

IO为同步阻塞形式,NIO为同步非阻塞形式,NIO并没有实现异步,在JDK1.7后升级NIO库包,支持异步非阻塞

同学模型NIO2.0(AIO)

BIO:同步阻塞式IO,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。 
NIO:同步非阻塞式IO,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。 
AIO(NIO.2):异步非阻塞式IO,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。

同步时,应用程序会直接参与IO读写操作,并且我们的应用程序会直接阻塞到某一个方法上,直到数据准备就绪:

或者采用轮训的策略实时检查数据的就绪状态,如果就绪则获取数据.

异步时,则所有的IO读写操作交给操作系统,与我们的应用程序没有直接关系,我们程序不需要关系IO读写,当操作

系统完成了IO读写操作时,会给我们应用程序发送通知,我们的应用程序直接拿走数据极即可。

伪异步

由于BIO一个客户端需要一个线程去处理,因此我们进行优化,后端使用线程池来处理多个客户端的请求接入,形成客户端个数M:线程池最大的线程数N的比例关系,其中M可以远远大于N,通过线程池可以灵活的调配线程资源,设置线程的最大值,防止由于海量并发接入导致线程耗尽。

原理:

当有新的客户端接入时,将客户端的Socket封装成一个Task(该Task任务实现了java的Runnable接口)投递到后端的线程池中进行处理,由于线程池可以设置消息队列的大小以及线程池的最大值,因此,它的资源占用是可控的,无论多少个客户端的并发访问,都不会导致资源的耗尽或宕机。

使用多线程支持多个请求

服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善

//tcp服务器端...class TcpServer {public static void main(String[] args) throws IOException {System.out.println("socket tcp服务器端启动....");ServerSocket serverSocket = new ServerSocket(8080);// 等待客户端请求try {while (true) {Socket accept = serverSocket.accept();new Thread(new Runnable() {@Overridepublic void run() {try {InputStream inputStream = accept.getInputStream();// 转换成string类型byte[] buf = new byte[1024];int len = inputStream.read(buf);String str = new String(buf, 0, len);System.out.println("服务器接受客户端内容:" + str);} catch (Exception e) {// TODO: handle exception}}}).start();}} catch (Exception e) {e.printStackTrace();} finally {serverSocket.close();}}}public class TcpClient {public static void main(String[] args) throws UnknownHostException, IOException {System.out.println("socket tcp 客户端启动....");Socket socket = new Socket("127.0.0.1", 8080);OutputStream outputStream = socket.getOutputStream();outputStream.write("我是蚂蚁课堂".getBytes());socket.close();}}

使用线程池管理线程

//tcp服务器端...class TcpServer {public static void main(String[] args) throws IOException {ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();System.out.println("socket tcp服务器端启动....");ServerSocket serverSocket = new ServerSocket(8080);// 等待客户端请求try {while (true) {Socket accept = serverSocket.accept();//使用线程newCachedThreadPool.execute(new Runnable() {@Overridepublic void run() {try {InputStream inputStream = accept.getInputStream();// 转换成string类型byte[] buf = new byte[1024];int len = inputStream.read(buf);String str = new String(buf, 0, len);System.out.println("服务器接受客户端内容:" + str);} catch (Exception e) {// TODO: handle exception}}});}} catch (Exception e) {e.printStackTrace();} finally {serverSocket.close();}}}public class TcpClient {public static void main(String[] args) throws UnknownHostException, IOException {System.out.println("socket tcp 客户端启动....");Socket socket = new Socket("127.0.0.1", 8080);OutputStream outputStream = socket.getOutputStream();outputStream.write("我是蚂蚁课堂".getBytes());socket.close();}}

IO模型关系

什么是阻塞

阻塞概念:应用程序在获取网络数据的时候,如果网络传输很慢,那么程序就一直等着,直接到传输完毕。

什么是非阻塞

应用程序直接可以获取已经准备好的数据,无需等待.

IO为同步阻塞形式,NIO为同步非阻塞形式。NIO没有实现异步,在JDK1.7之后,升级了NIO库包

,支持异步费阻塞通讯模型NIO2.0(AIO)

Netty快速入门

什么是Netty

Netty 是一个基于 JAVA NIO 类库的异步通信框架,它的架构特点是:异步非阻塞、基于事件驱动、高性能、高可靠性和高可定制性。

Netty应用场景

1.分布式开源框架中dubbo、Zookeeper,RocketMQ底层rpc通讯使用就是netty。

2.游戏开发中,底层使用netty通讯。

为什么选择netty

在本小节,我们总结下为什么不建议开发者直接使用JDK的NIO类库进行开发的原因:

1)      NIO的类库和API繁杂,使用麻烦,你需要熟练掌握Selector、ServerSocketChannel、SocketChannel、ByteBuffer等;

2)      需要具备其它的额外技能做铺垫,例如熟悉Java多线程编程,因为NIO编程涉及到Reactor模式,你必须对多线程和网路编程非常熟悉,才能编写出高质量的NIO程序;

3)      可靠性能力补齐,工作量和难度都非常大。例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常码流的处理等等,NIO编程的特点是功能开发相对容易,但是可靠性能力补齐工作量和难度都非常大;

4)      JDK NIO的BUG,例如臭名昭著的epoll bug,它会导致Selector空轮询,最终导致CPU 100%。官方声称在JDK1.6版本的update18修复了该问题,但是直到JDK1.7版本该问题仍旧存在,只不过该bug发生概率降低了一些而已,它并没有被根本解决。

Netty通信实践

Netty服务器端​

package com.xiaofeng.netty.server;import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;/*** @author xiaofeng* @version V1.0* @title: NettyServer* @package: com.xiaofeng.netty.server* @description: netty server* @date 2019/12/17 11:24*/
public class NettyServer {private static class SingletionNettyServer {static final NettyServer instance = new NettyServer();}public static NettyServer getInstance() {return SingletionNettyServer.instance;}private EventLoopGroup mainGroup;private EventLoopGroup subGroup;private ServerBootstrap server;private ChannelFuture future;public NettyServer() {//主线程组,用于接受客户端的连接,但是不做任何处理,跟老板一样,不做事mainGroup = new NioEventLoopGroup();//从线程组, 老板线程组会把任务丢给他,让手下线程组去做任务subGroup = new NioEventLoopGroup();//server启动类server = new ServerBootstrap();//建立联系server.group(mainGroup, subGroup).channel(NioServerSocketChannel.class).childHandler(new NettyServerInitialzer());}public void start() {this.future = server.bind(9999);System.err.println("netty server 启动完毕...");}public static void main(String[] args) {NettyServer.getInstance().start();}
}
package com.xiaofeng.netty.server;import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;/*** @author xiaofeng* @version V1.0* @title: NettyServerInitialzer* @package: com.xiaofeng.netty.server* @description: 服务端初始化器* @date 2019/12/17 11:30*/
public class NettyServerInitialzer extends ChannelInitializer<SocketChannel> {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();pipeline.addLast("stringD", new StringDecoder());pipeline.addLast("stringC", new StringEncoder());pipeline.addLast("http", new HttpClientCodec());// 自定义的handlerpipeline.addLast(new ServerChatHandler());}}
package com.xiaofeng.netty.server;import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;/*** @author xiaofeng* @version V1.0* @title: ServerChatHandler* @package: com.xiaofeng.netty.server* @description: 处理消息的handler* @date 2019/12/17 11:38*/
public class ServerChatHandler extends SimpleChannelInboundHandler<String> {// 用于记录和管理所有客户端的channlepublic static ChannelGroup users =new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);@Overrideprotected void channelRead0(ChannelHandlerContext ctx, String s)throws Exception {Channel channel = ctx.channel();for (Channel ch : users) {if (ch == channel) {System.out.println("收到来自客户端[" + channel.id().asShortText() + "]的消息:" + s);ctx.writeAndFlush("[你说]:" + s + "\n");} else {ctx.writeAndFlush("[" + channel.remoteAddress() + "]" + s + "\n");}}}/*** 当客户端连接服务端之后(打开连接)* 获取客户端的channle,并且放到ChannelGroup中去进行管理*/@Overridepublic void handlerAdded(ChannelHandlerContext ctx) throws Exception {String channelId = ctx.channel().id().asShortText();System.out.println("客户端加入,channelId为:" + channelId);users.add(ctx.channel());}@Overridepublic void handlerRemoved(ChannelHandlerContext ctx) throws Exception {String channelId = ctx.channel().id().asShortText();System.out.println("客户端被移除,channelId为:" + channelId);// 当触发handlerRemoved,ChannelGroup会自动移除对应客户端的channelusers.remove(ctx.channel());}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();// 发生异常之后关闭连接(关闭channel),随后从ChannelGroup中移除ctx.channel().close();users.remove(ctx.channel());}
}

Netty客户端

package com.xiaofeng.netty.client;import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;/*** @author xiaofeng* @version V1.0* @title: NettyClient* @package: com.xiaofeng.netty.client* @description: netty client* @date 2019/12/17 11:14*/
public class NettyClient {private static class SingletionNettyClient {static final NettyClient instance = new NettyClient();}public static NettyClient getInstance() {return SingletionNettyClient.instance;}private EventLoopGroup eventGroup;private Bootstrap client;public NettyClient() {eventGroup = new NioEventLoopGroup();//创建客户端启动类client = new Bootstrap();client.group(eventGroup).channel(NioSocketChannel.class).handler(new NettyClientInitialzer());}public void start() throws InterruptedException, IOException {Channel channel = client.connect("127.0.0.1", 9999).sync().channel();while (true) {BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));String input = reader.readLine();if (input != null) {if ("quit".equals(input)) {System.exit(1);}channel.writeAndFlush(input);}}}public static void main(String[] args) throws InterruptedException, IOException {NettyClient.getInstance().start();}
}
package com.xiaofeng.netty.client;import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;/*** @author xiaofeng* @version V1.0* @title: NettyClientInitialzer* @package: com.xiaofeng.netty.client* @description: 客户端初始化器* @date 2019/12/17 11:29*/
public class NettyClientInitialzer extends ChannelInitializer<Channel> {@Overrideprotected void initChannel(Channel channel) throws Exception {//获取管道ChannelPipeline pipeline = channel.pipeline();//添加编解码器pipeline.addLast("stringD", new StringDecoder());pipeline.addLast("stringC", new StringEncoder());pipeline.addLast("http", new HttpClientCodec());// 自定义的handlerpipeline.addLast(new ClientChatHandler());}
}
package com.xiaofeng.netty.client;import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;/*** @author xiaofeng* @version V1.0* @title: ClientChatHandler* @package: com.xiaofeng.netty.client* @description: 处理消息的handler* @date 2019/12/17 11:38*/
public class ClientChatHandler extends SimpleChannelInboundHandler<String> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, String s)throws Exception {System.out.println(s);}}

TCP粘包、拆包问题解决方案

什么是粘包/拆包

一个完整的业务可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这个就是TCP的拆包和封包问题。

下面可以看一张图,是客户端向服务端发送包:

1. 第一种情况,Data1和Data2都分开发送到了Server端,没有产生粘包和拆包的情况。

2. 第二种情况,Data1和Data2数据粘在了一起,打成了一个大的包发送到Server端,这个情况就是粘包。

3. 第三种情况,Data2被分离成Data2_1和Data2_2,并且Data2_1在Data1之前到达了服务端,这种情况就产生了拆包。

由于网络的复杂性,可能数据会被分离成N多个复杂的拆包/粘包的情况,所以在做TCP服务器的时候就需要首先解决拆包/

解决办法

1.消息定长,报文大小固定长度,不够空格补全,发送和接收方遵循相同的约定,这样即使粘包了通过接收方编程实现获取定长报文也能区分。


sc.pipeline().addLast(new FixedLengthFrameDecoder(10));

2.包尾添加特殊分隔符,例如每条报文结束都添加回车换行符(例如FTP协议)或者指定特殊字符作为报文分隔符,接收方通过特殊分隔符切分报文区分。

ByteBuf buf = Unpooled.copiedBuffer("_mayi".getBytes());sc.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, buf));

将消息分为消息头和消息体,消息头中包含表示信息的总长度(或者消息体长度)的字段

NIO介绍与Netty通信简单入门相关推荐

  1. java netty教程_明哥教学 - Netty简单入门教程

    作为一个正在Java路上摸爬滚打的小菜鸡,之前在项目中也用过Netty,也因为Netty报名阿里的中间件大赛,但终究功力太浅,最终不了了之,最近工作中又遇到了Netty的小姐妹Mina.此时楼主觉得N ...

  2. 【初识Netty使用Netty实现简单的客户端与服务端的通信操作Netty框架中一些重要的类以及方法的解析】

    一.Netty是什么? Netty 由 Trustin Lee(韩国,Line 公司)2004 年开发 本质:网络应用程序框架 实现:异步.事件驱动 特性:高性能.可维护.快速开发 用途:开发服务器和 ...

  3. 分布式锁简单入门以及三种实现方式介绍(滴滴)

    很多小伙伴在学习Java的时候,总是感觉Java多线程在实际的业务中很少使用,以至于不会花太多的时间去学习,技术债不断累积!等到了一定程度的时候对于与Java多线程相关的东西就很难理解,今天需要探讨的 ...

  4. netty实现简单时事通讯_简单的Java实现Netty进行通信

    使用Java搭建一个简单的Netty通信例子 看过dubbo源码的同学应该都清楚,使用dubbo协议的底层通信是使用的netty进行交互,而最近看了dubbo的Netty部分后,自己写了个简单的Net ...

  5. 分布式锁简单入门以及三种实现方式介绍

    分布式锁简单入门以及三种实现方式介绍 2018年01月11日 21:16:28 徐刘根 阅读数:37912 标签: 分布式 分布式锁 高并发 更多 个人分类: 集群分布式 版权声明:本文为博主原创文章 ...

  6. spark学习-72-源代码:Endpoint模型介绍(4)-Spark为何使用Netty通信框架替代Akka

    问题导读: spark 如何在1.6.0之后使用Netty替代了Akka? Spark Network Common怎么实现? BlockTransfer 与 Shuffle 之间的联系? Akka ...

  7. 拜占庭共识Tendermint介绍及简单入门

    1.Tendermint是什么 Tenermint 是一个软件,用于在多台机器安全一致地复制一个应用.所谓安全,指的是即使有多达 1/3 的机器出现任意故障的情况下, Tendermint 仍然能够正 ...

  8. 【Netty】从 BIO、NIO 聊到 Netty

    为了让你更好地了解 Netty 以及它诞生的原因,先从传统的网络编程说起吧! 还是要从 BIO 说起 传统的阻塞式通信流程 早期的 Java 网络相关的 API(java.net包) 使用 Socke ...

  9. 这么说吧,Netty很简单,其实就是个Jar包,是作为通讯组件用的

    极简教程,五分钟快速入门之netty,搭配后面netty实战以及netty源码分析 0 ) 关键词: Netty.NIO.异步.通讯. 1)本质: 一个Jar包,一个NIO框架,是对 socket 网 ...

最新文章

  1. java中获取特定时间段_获取某一时间段特定星期几的所有日期(Java实现)
  2. 将h.264视频流封装成flv格式文件(一.flv格式)
  3. 一位算法工程师从30+场秋招面试中总结出的超强面经——目标检测篇(含答案)
  4. 苹果原壁纸高清_周易壁纸 | 八卦图阵高清壁纸
  5. Typescript学习;Typescript总结;Typescript 的数据类型有哪些?
  6. 设置UITableView Section的背景颜色和字体颜色
  7. 29.Jwt集成(3):token设置过期时间、异常判断
  8. 用Java实现多线程下载
  9. SVPWM调制中非零基础矢量的幅值是2/3Udc还是Udc?
  10. 阿里通信携手联通MWC演示“智选加速” 预演5G垂直应用
  11. 计算机语言元素周期表,多国语言的化学元素周期表
  12. 超全的 100 个 Pandas 函数汇总,建议收藏
  13. js判断两个时间是否超过一年
  14. python实现树莓派开机自动发送IP到指定邮箱
  15. 图像物体分类与检测算法综述
  16. 【我的开源】拿来即用!代码生成器:mybatis-plus-generator自定义模板生成 DTO、VO、Convertor、增删改查方法
  17. Python+Vue计算机毕业设计影评网站系统4i684(源码+程序+LW+部署)
  18. c++糟心问题实验截图
  19. 2017年如何实现1个亿的小目标?
  20. ## 标题 使用eclipse遇到的错误JavaWeb【404,The origin server did not find a current representation for the targ

热门文章

  1. Android开发之NDK下载与NDK更新
  2. java工程师去字节飞书可以,字节跳动飞书Java后端开发暑假实习一面(过了)
  3. 风口上的储能,光伏要飞起来?
  4. 13.10 Scala中使用JSON.toJSONString报错:ambiguous reference to overloaded definition
  5. Medoo 开源项目发布,超轻量级的PHP SQL数据库框架
  6. 解决PHP 中英文字符串截取出现半个字符
  7. 漫谈数据仓库之维度建模
  8. Ajax updatepanel用法
  9. 技术人员职业生涯的红色警报
  10. 华为辞职门事件——再谈工作问题