Netty 入门实例

2024-06-23 06:12
文章标签 netty 入门 实例

本文主要是介绍Netty 入门实例,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 1. 概述
  • 2. 代码实例
    • 2.1 服务端
    • 2.2 客户端
    • 2.3 运行截图
  • 3. 整体结构
  • 4. 重要组件
    • 4.1 EventLoopGroup、EventLoop
    • 4.2 Handler & Pipeline
    • 4.3 ByteBuf
  • 参考文献

1. 概述

Netty 是一款用于高效开发网络应用的 NIO 网络框架,它大大简化了网络应用的开发过程。

Netty 相比 JDK NIO 的优势:

● 易用性:Netty 在 NIO 基础上进行了更高层次的封装,屏蔽了 NIO 的复杂性,大大降低了开发者的上手难度;
● 稳定性:Netty 修复和完善了 JDK NIO 较多已知问题,例:select 空转导致 CPU 消耗 100%,TCP 断线重连,keep-alive 检测等;
● 可扩展性: 可定制化的线程模型,用户可以通过启动的配置参数选择 Reactor 线程模型;另一个是可扩展的事件驱动模型,将框架层和业务层的关注点分离,开发者只需要关注 ChannelHandler

Netty 比 JDK NIO 更低的资源消耗:

● 对象池复用技术。 Netty 通过复用对象,避免频繁创建和销毁带来的开销;
● 零拷贝技术。 除了操作系统级别的零拷贝技术外,Netty 提供了更多面向用户态的零拷贝技术,例如 Netty 在 I/O 读写时直接使用 DirectBuffer,从而避免了数据在堆内存和堆外内存之间的拷贝;

2. 代码实例

2.1 服务端


import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;import java.nio.charset.CharacterCodingException;
import java.nio.charset.StandardCharsets;/*** Netty服务端示例01*/
public class NettyServer01 {/*** 主函数,服务器的入口点* @param args 命令行参数*/public static void main(String[] args) {// 创建BossGroup和WorkerGroup,分别处理连接接受和数据读写NioEventLoopGroup bossEventLoopGroup = new NioEventLoopGroup(2);NioEventLoopGroup workerEventLoopGroup = new NioEventLoopGroup(8);new ServerBootstrap() // 初始化ServerBootstrap.group(bossEventLoopGroup, workerEventLoopGroup) // 设置EventLoopGroup.channel(NioServerSocketChannel.class) // 指定服务器通道类.childHandler(new ChannelInitializer<NioSocketChannel>() { // 设置通道初始化器/*** 初始化通道,添加处理器到通道的管道中* @param ch 当前初始化的通道*/protected void initChannel(NioSocketChannel ch) {// 添加多个处理器,分别处理入站和出站事件ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {/*** 处理入站数据* @param ctx 通道上下文* @param msg 接收到的消息对象*/@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {ByteBuf byteBuf = inbound((ByteBuf) msg, "1");ctx.fireChannelRead(byteBuf);}});ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws CharacterCodingException {ByteBuf byteBuf = inbound((ByteBuf) msg, "2");ctx.fireChannelRead(byteBuf);}});ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {/*** 处理入站数据,将处理后的数据写回通道* @param ctx 通道上下文* @param msg 接收到的消息对象*/@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {ByteBuf byteBuf = inbound((ByteBuf) msg, "3");ctx.channel().write(byteBuf);}});ch.pipeline().addLast(new ChannelOutboundHandlerAdapter() {/*** 处理出站数据,在数据写出前进行加工* @param ctx 通道上下文* @param msg 要写出的消息对象* @param promise 写操作的承诺*/@Overridepublic void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {ByteBuf byteBuf = outbound((ByteBuf) msg, "4");ctx.writeAndFlush(msg);ctx.write(byteBuf, promise);}});ch.pipeline().addLast(new ChannelOutboundHandlerAdapter() {@Overridepublic void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {ByteBuf byteBuf = outbound((ByteBuf) msg, "5");ctx.write(byteBuf, promise);}});ch.pipeline().addLast(new ChannelOutboundHandlerAdapter() {@Overridepublic void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {ByteBuf byteBuf = outbound((ByteBuf) msg, "6");ctx.write(byteBuf, promise);}});}}).bind(8080); // 绑定端口并启动服务器}/*** 对出站数据进行处理* @param msg 待处理的ByteBuf对象* @param no 数据标识号* @return 处理后的ByteBuf对象*/private static ByteBuf outbound(ByteBuf msg, String no) {ByteBuf byteBuf = msg;String output = byteBufToString(byteBuf);System.out.printf("\n\noutbound%s output: %s", no, output);stringWriteToByteBuf(byteBuf, String.format("\noutbound%s 已处理", no));return byteBuf;}/*** 对入站数据进行处理* @param msg 待处理的ByteBuf对象* @param no 数据标识号* @return 处理后的ByteBuf对象*/private static ByteBuf inbound(ByteBuf msg, String no) {String input = byteBufToString(msg);System.out.printf("\n\ninbound%s input: %s\n", no, input);stringWriteToByteBuf(msg, String.format("\ninbound%s 已处理", no));return msg;}/*** 将ByteBuf对象转换为字符串* @param msg 待转换的ByteBuf对象* @return 字符串表示的数据*/private static String byteBufToString(ByteBuf msg) {return msg.toString(StandardCharsets.UTF_8);}/*** 将字符串写入ByteBuf对象* @param byteBuf 待写入的ByteBuf对象* @param msg 要写入的字符串数据*/private static void stringWriteToByteBuf(ByteBuf byteBuf, String msg) {byteBuf.writeBytes(msg.getBytes(StandardCharsets.UTF_8));}
}

2.2 客户端

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringEncoder;import java.nio.charset.StandardCharsets;public class NettyClient01 {public static void main(String[] args) {new Bootstrap().group(new NioEventLoopGroup()).channel(NioSocketChannel.class).handler(new ChannelInitializer<Channel>() {@Overrideprotected void initChannel(Channel ch) {ch.pipeline().addLast(new StringEncoder());ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {System.out.println(((ByteBuf) msg).toString(StandardCharsets.UTF_8));}});}}).connect("127.0.0.1", 8080).addListener((ChannelFutureListener) future -> {future.channel().writeAndFlush("hello,world");});}
}

2.3 运行截图

服务端截图:

在这里插入图片描述

客户端截图:
在这里插入图片描述

3. 整体结构

● Boss EventLoopGroup: 负责监听网络连接事件,把新网络连接的Channel 注册到 Worker EventLoopGroup
● Worker EventLoopGroup: 分配一个 EventLoop 负责处理该 Channel 的读写事件,每个 EventLoop 都是单线程的,所以该连接线程安全的,通过 Selector 进行事件循环
● 客户端发起 I/O 读写事件时,服务端 EventLoop 会进行数据的读取,然后通过 Pipeline 触发各种监听器进行数据的加工处理。客户端数据会被传递到 ChannelPipeline 的第一个 ChannelInboundHandler 中,数据处理完成后,将加工完成的数据传递给下一个 ChannelInboundHandler。当数据写回客户端时,会将处理结果在 ChannelPipeline 的 ChannelOutboundHandler 中传播,最后到达客户端。

在这里插入图片描述

4. 重要组件

4.1 EventLoopGroup、EventLoop

EventLoop本质上是一个单线程执行器,维护了一个 Selector,里面有run方法处理Channel上源源不断的io事件。EventLoop继承了ScheduledExecutorService、和自己的OrderedEventExecutor,OrderedEventExecutor 提供了inEventLoop(java.lang.Thread thread)、EventExecutorGroup parent()、EventExecutor next() 等方法。

EventLoopGroup 是一组 EventLoop,Channel一般会调用 EventLoopGroup 的 register 方法绑定其中一个EventLoop,后续这个Channel上 的io都由这个EventLoop处理,EventLoop又是单线程的,保证了单个Channel io 事件处理的线程安全性。

4.2 Handler & Pipeline

ChannelHandler 用来处理 Channel 上的各种事件,分为入站、出站两种。所有 ChannelHandler 被连成一串,就是 Pipeline
● 入站处理器通常是 ChannelInboundHandlerAdapter 的子类,主要用来读取客户端数据,写回结果
● 出站处理器通常是 ChannelOutboundHandlerAdapter 的子类,主要对写回结果进行加工

ChannelInboundHandlerAdapter 是按照 addLast 的顺序执行的,而 ChannelOutboundHandlerAdapter 是按照 addLast 的逆序执行的。ChannelPipeline 的实现是一个 ChannelHandlerContext(包装了 ChannelHandler) 组成的双向链表

4.3 ByteBuf

传送

参考文献

  • 黑马 Netty教程
  • 拉钩教育 Netty 核心原理剖析与 RPC 实践 若地老师
    在这里插入图片描述

这篇关于Netty 入门实例的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1086352

相关文章

C++必修:模版的入门到实践

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:C++学习 贝蒂的主页:Betty’s blog 1. 泛型编程 首先让我们来思考一个问题,如何实现一个交换函数? void swap(int& x, int& y){int tmp = x;x = y;y = tmp;} 相信大家很快就能写出上面这段代码,但是如果要求这个交换函数支持字符型

零基础STM32单片机编程入门(一)初识STM32单片机

文章目录 一.概要二.单片机型号命名规则三.STM32F103系统架构四.STM32F103C8T6单片机启动流程五.STM32F103C8T6单片机主要外设资源六.编程过程中芯片数据手册的作用1.单片机外设资源情况2.STM32单片机内部框图3.STM32单片机管脚图4.STM32单片机每个管脚可配功能5.单片机功耗数据6.FALSH编程时间,擦写次数7.I/O高低电平电压表格8.外设接口

swiper实例

大家好,我是燐子,今天给大家带来swiper实例   微信小程序中的 swiper 组件是一种用于创建滑动视图的容器组件,常用于实现图片轮播、广告展示等效果。它通过一系列的子组件 swiper-item 来定义滑动视图的每一个页面。 基本用法   以下是一个简单的 swiper 示例代码:   WXML(页面结构) <swiper autoplay="true" interval="3

Java面试题:通过实例说明内连接、左外连接和右外连接的区别

在 SQL 中,连接(JOIN)用于在多个表之间组合行。最常用的连接类型是内连接(INNER JOIN)、左外连接(LEFT OUTER JOIN)和右外连接(RIGHT OUTER JOIN)。它们的主要区别在于它们如何处理表之间的匹配和不匹配行。下面是每种连接的详细说明和示例。 表示例 假设有两个表:Customers 和 Orders。 Customers CustomerIDCus

SpringBoot集成Netty,Handler中@Autowired注解为空

最近建了个技术交流群,然后好多小伙伴都问关于Netty的问题,尤其今天的问题最特殊,功能大概是要在Netty接收消息时把数据写入数据库,那个小伙伴用的是 Spring Boot + MyBatis + Netty,所以就碰到了Handler中@Autowired注解为空的问题 参考了一些大神的博文,Spring Boot非controller使用@Autowired注解注入为null的问题,得到

ps基础入门

1.基础      1.1新建文件      1.2创建指定形状      1.4移动工具          1.41移动画布中的任意元素          1.42移动画布          1.43修改画布大小          1.44修改图像大小      1.5框选工具      1.6矩形工具      1.7图层          1.71图层颜色修改          1

C++入门01

1、.h和.cpp 源文件 (.cpp)源文件是C++程序的实际实现代码文件,其中包含了具体的函数和类的定义、实现以及其他相关的代码。主要特点如下:实现代码: 源文件中包含了函数、类的具体实现代码,用于实现程序的功能。编译单元: 源文件通常是一个编译单元,即单独编译的基本单位。每个源文件都会经过编译器的处理,生成对应的目标文件。包含头文件: 源文件可以通过#include指令引入头文件,以使

LVGL快速入门笔记

目录 一、基础知识 1. 基础对象(lv_obj) 2. 基础对象的大小(size) 3. 基础对象的位置(position) 3.1 直接设置方式 3.2 参照父对象对齐 3.3 获取位置 4. 基础对象的盒子模型(border-box) 5. 基础对象的样式(styles) 5.1 样式的状态和部分 5.1.1 对象可以处于以下状态States的组合: 5.1.2 对象

C语言入门系列:探秘二级指针与多级指针的奇妙世界

文章目录 一,指针的回忆杀1,指针的概念2,指针的声明和赋值3,指针的使用3.1 直接给指针变量赋值3.2 通过*运算符读写指针指向的内存3.2.1 读3.2.2 写 二,二级指针详解1,定义2,示例说明3,二级指针与一级指针、普通变量的关系3.1,与一级指针的关系3.2,与普通变量的关系,示例说明 4,二级指针的常见用途5,二级指针扩展到多级指针 小结 C语言的学习之旅中,二级

打造坚固的SSH防护网:端口敲门入门指南

欢迎来到我的博客,代码的世界里,每一行都是一个故事 🎏:你只管努力,剩下的交给时间 🏠 :小破站 打造坚固的SSH防护网:端口敲门入门指南 前言什么是端口敲门端口敲门的优点1. 增强安全性2. 动态防火墙规则3. 隐匿服务4. 改善日志管理5. 灵活性和兼容性6. 低资源消耗7. 防御暴力破解和扫描8. 便于合法用户访问9. 适用于不同类型的服务 端口敲