皮皮网
皮皮网

【马来西亚原装源码】【基金投资源码】【缔星APP源码】io netty源码

来源:vue源码分析 max 发表时间:2024-12-22 11:50:16

1.一、Netty简介与IO模型
2.netty系列之:在netty中使用native传输协议
3.上为什么没人用t-io?
4.Netty原理-从NIO开始

io netty源码

一、Netty简介与IO模型

       Netty,一个异步事件驱动的网络应用程序框架,专注于高效、可维护的马来西亚原装源码网络应用开发。由官方描述可知,Netty适用于快速构建高性能服务器和客户端,尤其擅长处理并发量极高的场景。

       然而,许多人疑惑:既然有 Tomcat 这样功能丰富的 Servlet 容器支持 NIO(非阻塞 I/O)模式,为何还要采用 Netty?实际上,Netty 的基金投资源码灵活性在于其对协议的自定义能力。尽管 Tomcat 能胜任 HTTP 协议服务,但 Netty 更加强大,可实现任何协议的编码和解码,使其成为网络应用程序框架的理想选择。

       在讨论 IO 模型时,理解“阻塞式 IO”(等待操作完成才返回)、缔星APP源码“非阻塞式 IO”(频繁轮询以检查是否可执行)及“信号驱动 IO”(接收到信号时进行操作,同时保持阻塞状态直至数据复制到用户空间)等不同模型,显得尤为重要。以“阻塞式 IO”为起点,通过“非阻塞式 IO”简化轮询负担,然后“信号驱动 IO”利用信号通知来减少阻塞时间,预约功能实现源码“IO 多路复用”能同时处理多个文件描述符的就绪状态,最后,“异步 IO”实现完全的非阻塞操作。

       同步与异步通信的差异体现在主动等待结果上:同步通信需程序主动阻塞直至结果出现,而异步通信在发出调用后立刻返回,结果由调用者接收。帝国源码宠物模板关于阻塞与非阻塞的区别,则聚焦于程序在等待结果时的状态,阻塞调用意味着在结果返回前,当前线程处于等待状态;而非阻塞调用允许程序在无结果可得时继续执行其他任务。

       综上所述,Netty 作为网络编程的强大工具,结合 IO 模型及通信方式的理解,能够有效地提升开发效率与系统性能,尤其在高度并发和协议自定义的场景下展现其独特优势。

netty系列之:在netty中使用native传输协议

       ç®€ä»‹

       å¯¹äºŽIO来说,除了传统的block IO,使用最多的就是NIO了,通常我们在Netty程序中最常用到的就是NIO,比如NioEventLoopGroup,NioServerSocketChannel等。

       æˆ‘们也知道在IO中有比NIO更快的IO方式,比如kqueue和epoll,但是这两种方式需要native方法的支持,也就是说需要在操作系统层面提供服务。

       å¦‚果我们在支持Kqueue或者epoll的服务器上,netty是否可以提供对这些优秀IO的支持呢?

       ç­”案是肯定的。但是首先kqueue和epoll需要JNI支持,也就是说JAVA程序需要调用本地的native方法。

native传输协议的依赖

       è¦æƒ³ä½¿ç”¨kequeue和epoll这种native的传输方式,我们需要额外添加项目的依赖,如果是linux环境,则可以添加如下的maven依赖环境:

<dependencies><dependency><groupId>io.netty</groupId><artifactId>netty-transport-native-epoll</artifactId><version>${ project.version}</version><classifier>linux-x_</classifier></dependency>...</dependencies>

       å…¶ä¸­version需要匹配你所使用的netty版本号,否则可能出现调用异常的情况。

       classifier表示的是系统架构,它的值可以是linux-x_,也可以是linux-aarch_.

       å¦‚果你使用的mac系统,那么可以这样引入:

<dependencies><dependency><groupId>io.netty</groupId><artifactId>netty-transport-native-kqueue</artifactId><version>${ project.version}</version><classifier>osx-x_</classifier></dependency>...</dependencies>

       netty除了单独的个体包之外,还有一个all in one的netty-all包,如果你使用了这个all in one的包,那么不需要额外添加native的依赖。

       å¦‚æžœnetty提供的系统架构并没有你正在使用的,那么你需要手动进行编译,以下是编译所依赖的程序包, 如果是在RHEL/CentOS/Fedora系统中,则使用:

sudo yum install autoconf automake libtool make tar \ glibc-devel \ libgcc.i glibc-devel.i

       å¦‚果是在Debian/Ubuntu系统中,则使用:

sudo apt-get install autoconf automake libtool make tar \ gcc

       å¦‚果是在MacOS/BSD系统中,则使用:

brew install autoconf automake libtoolnetty本地传输协议的使用

       å®‰è£…好依赖包之后,我们就可以在netty中使用这些native传输协议了。

       native传输协议的使用和NIO的使用基本一致,我们只需要进行下面的替换即可。

       å¦‚果是在liunx系统中,则进行下面的替换:

NioEventLoopGroup → EpollEventLoopGroupNioEventLoop → EpollEventLoopNioServerSocketChannel → EpollServerSocketChannelNioSocketChannel → EpollSocketChannel

       å¦‚果是在mac系统中,则进行下面的替换:

NioEventLoopGroup → KQueueEventLoopGroupNioEventLoop → KQueueEventLoopNioServerSocketChannel → KQueueServerSocketChannelNioSocketChannel → KQueueSocketChannel

       è¿™é‡Œè¿˜æ˜¯ä½¿ç”¨æˆ‘们熟悉的聊天服务为例,首先看下基于Kqueue的netty服务器端应该怎么写:

EventLoopGroup bossGroup = new KQueueEventLoopGroup(1);EventLoopGroup workerGroup = new KQueueEventLoopGroup();try { ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup) .channel(KQueueServerSocketChannel.class) .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new NativeChatServerInitializer());Channel channel = b.bind(PORT).sync().channel();log.info("server channel:{ }", channel);channel.closeFuture().sync();

       å’ŒNIO一样,在服务器端我们需要使用KQueueEventLoopGroup创建两个EventLoopGroup,一个是bossGroup, 一个是workerGroup。

       ç„¶åŽå°†è¿™ä¸¤ä¸ªgroup传入到ServerBootstrap中,并且添加KQueueServerSocketChannel作为channel。

       å…¶ä»–的内容和NIO server的内容是一样的。

       æŽ¥ä¸‹æ¥æˆ‘们看下基于Kqueue的netty客户端改如何跟server端建立连接:

EventLoopGroup group = new KQueueEventLoopGroup();try { Bootstrap b = new Bootstrap();b.group(group) .channel(KQueueSocketChannel.class) .handler(new NativeChatClientInitializer());// 建立连接Channel ch = b.connect(HOST, PORT).sync().channel();log.info("client channel: { }", ch);

       è¿™é‡Œä½¿ç”¨çš„是KQueueEventLoopGroup,并将KQueueEventLoopGroup放到Bootstrap中,并且为Bootstrap提供了和server端一致的KQueueSocketChannel。

       ç„¶åŽå°±æ˜¯å®¢æˆ·ç«¯å‘channel中写消息,这里我们直接从命令行输入:

// 从命令行输入ChannelFuture lastWriteFuture = null;BufferedReader in = new BufferedReader(new InputStreamReader(System.in));for (;;) { String line = in.readLine();if (line == null) { break;}// 将从命令行输入的一行字符写到channel中lastWriteFuture = ch.writeAndFlush(line + "\r\n");// 如果输入'再见',则等待server端关闭channelif ("再见".equalsIgnoreCase(line)) { ch.closeFuture().sync();break;}}

       ä¸Šé¢ä»£ç çš„意思是将命令行收到的消息写入到channel中,如果输入的是’再见’,则关闭channel。

       ä¸ºäº†èƒ½å¤Ÿå¤„理字符串,这里用到了三个编码解码器:

<dependencies><dependency><groupId>io.netty</groupId><artifactId>netty-transport-native-kqueue</artifactId><version>${ project.version}</version><classifier>osx-x_</classifier></dependency>...</dependencies>0

       åˆ†åˆ«æ˜¯è¡Œåˆ†å‰²å™¨ï¼Œå­—符编码器和字符解码器。

       è¿è¡Œä¸€ä¸‹çœ‹ï¼Œç¨‹åºè¿è¡Œæ²¡é—®é¢˜ï¼Œå®¢æˆ·ç«¯å’ŒæœåŠ¡å™¨ç«¯å¯ä»¥è¿›è¡Œé€šè®¯ã€‚

总结

       è¿™é‡Œæˆ‘们只以Kqueue为例介绍了netty中native传输协议的使用,具体的代码,大家可以参考:

       learn-netty4

       æ›´å¤šå†…容请参考?/post/

上为什么没人用t-io?

       t-io在质量设计上表现不错,但作者的自大态度令人不满,将其与netty相比时,似乎忽视了netty在业界的广泛认可与深厚底蕴。

       t-io的推崇者持有极端信仰,认为其他IO框架都不如t-io,这种观点明显过于偏激。

       t-io的文档需要改进,目前存在付费问题,同时其源码注释不足且缺乏关键点解析,这在一定程度上降低了用户的使用体验。

       对于netty这类主流框架的批评,似乎忽略了它们在不同场景下的适用性和强大功能,同时也质疑其他IO框架的存在价值,这观点看似武断。

       为了避免被这类偏执者的打扰,我选择匿名回复。

Netty原理-从NIO开始

       Netty是基于NIO的异步通信框架(曾经引入过AIO,后来放弃),故要说Netty原理我们要先从NIO开始。

        NIO 是JAVA在JDK4中引入的同步非阻塞通信模型,在NIO出现之前(JDK4之前)市场上只有一个BIO模型顾名思义BLOCKING IO (同步阻塞通信模型)

        BIO(BLOCKING I/O):

        BIO 为一个连接 一个线程的模式,当有连接时服务器会开启一个线程来处理请求

        若此请求啥都不想干此时线程会怎么样?

        此线程会进入阻塞模式(BLOCKING)!---啥也不干,干等着zzZZ~

        这种一连接,一线程的模式会造成服务器资源不必要的开销并且在大量连接访问时 服务器会发生什么?车道(线程)不足,车太多--我堵车了

        由此就出现了NIO

        ↓

        NIO(new/NONBLOCKING I/O):

        NIO为同步非阻塞通信模型,Select(多路复用器)为此模型的核心,实现了多个连接一个线程

        当有客户端连接请求时 此连接请求会被注册至select上,当select检测到此连接有I/O请求时才会打开一个线程去对此I/O请求进行处理-----单线程模型

        这个时候有人问了:这么多操作都在一个线程里,线程忙不过来怎么办?

        此时 由于网络请求、I/O读写、业务操作都在一个线程下,会导致在高并发的情况下存在性能瓶颈 于是乎有人就提出来 将业务操作丢到另一个线程怎么样?

        于是出现了第三种reactor模型-使用线程池进行操作网络请求、IO在一个线程,业务操作在另个一个线程 的业务分离----线程池模型

        从此图中可以看出此时 模型中使用一个线程池来进行网络请求、IO读取

        当读取完成后将业务操作绑定在线程池中另外的线程上-------网络IO与业务操作可以同步进行了!一切都完美了起来!

        但是!事情还没完!!这个时候又有人提出问题:在高并发的时候咋办?会不会有性能瓶颈

        因为网络IO是非常消耗CPU的,当网络请求与网络IO在同个线程中时,造CK的情况下单个线程并不足以支撑起所有的IO操作!因此也形成了在高并发状态下的性能瓶颈

        于是大佬们就想着,如果把IO拆出来让单个线程池去接收网络请求,用另一个线程池来进行IO与业务操作会不会更好

        于是第四种Reactor模型应运而生--主从Reactor多线程模型

        此模型中 mainReactor只用于接收网络请求,而subReactor中为一个线程池,线程池中每个线程上绑定一个select

        当mainReactor接收到请求时(一个描述符) 系统会生成一个新的描述符代表此连接生效,此时mainReactor会将新的描述符通过一个算法在线程池中选定一个线程 将此描述符绑定至此线程池上的select上,由此线程来对请求进行I/O 与业务操作

        从此百万连接高并发不是问题

        写到这 我们是不是想起了Netty的启动过程

        1、声明两个EventLoopGroup一个为boss(mainReactor)一个为worker(subReactor)

        EventLoopGroup(线程池)初始化的时候会生成(懒加载)指定数量的EventLoop(线程)若无指定 则会生成CPU数X2的线程

        2、声明一个启动辅助类Bootstrap并将EventLoopGroup注册到启动辅助类BootStrap上(bootStrap.group)

        接着再给bootstrap指定channel模型等属性,再添加上业务流水线(channelpipeline)并且在pipeline中添加上业务操作handler,(通过channelpipeline可以对传入数据为所欲为)

        3、绑定端口

        Netty启动完成

        这时候可能有人会问了:这和你上面说的reactor?NIO有啥关系?

        这个时候我们要这么看

        ↓

        若我们将boss与worker线程池设置为相同的一个线程池,那么会发生什么事?

        此时关注一下第三个Reactor模型时就会发现 当BOSS=WORKER时候 netty实现的就是第三种Reactor模型 使用线程池模型

        而当boss不等于worker的时候使用的就是第四种 主从多线程模型

        Netty就是基于Reactor模型来对NIO进行了易用化封装,从Netty源码中就可以看出来其实底层还都是NIO的接口

        此次处为自己读源码之后的理解 如有误请指正

        感恩

        反手拿下第一个赞

相关栏目:百科