Netty的简介与实战

Netty简介

一、背景与来源

  • Netty最初是由JBOSS提供的一个Java开源框架,现在已成为Github上的独立项目。
  • 它基于Java的NIO(New Input/Output)模型,提供了简单而强大的抽象,使得网络编程变得更加容易和高效。

二、特点与优势

  1. 高性能:Netty使用高效的Reactor模式,采用非阻塞I/O操作,以及优化的内存管理和缓冲区池,确保了高性能的数据处理和传输。与其他业界主流的NIO框架相比,Netty在吞吐量、延迟、资源消耗等方面都表现出色。
  2. 异步事件驱动:Netty基于事件驱动模型,能够轻松处理并发连接和高吞吐量的数据传输。它处理连接、读写、异常等事件,并通过事件处理器将这些事件传递给应用程序,使开发者能够集中处理业务逻辑。
  3. 支持多种协议:Netty支持多种传输协议,包括TCP、UDP、HTTP、HTTPS、WebSocket、Google Protocol Buffers等,还可以通过扩展支持其他自定义协议。这使得开发者能够更轻松地处理不同协议的网络通信。
  4. 可扩展性:Netty提供了灵活的Pipeline机制,允许开发者通过ChannelHandler链来处理网络事件,实现自定义的编解码器、拦截器等组件。通过这种模块化的设计,可以轻松扩展和定制Netty的功能。
  5. 易用性:Netty提供了简洁的API和详细的文档,使得开发者能够快速上手和实现复杂的网络功能。同时,Netty的社区非常活跃,有大量的资源和经验可供参考。
  6. 跨平台:Netty可运行在多种操作系统和Java版本上,保证了良好的跨平台兼容性。

三、核心组件与功能

Netty框架主要由以下几个核心组件构成,这些组件共同构建了Netty的整体架构,分别负责处理不同的功能和逻辑:

  1. Channel:Channel是Netty中的基本抽象,代表一个连接或通信的载体,可以是TCP连接、UDP套接字等。Channel负责I/O操作的执行,并维护连接的状态。
  2. EventLoop:EventLoop是Netty中的事件循环,负责处理I/O操作和任务调度。每个EventLoop都与一个线程关联,并分配给一个或多个Channel。EventLoop负责将事件分发给对应的ChannelHandler进行处理。
  3. ChannelHandler:ChannelHandler是Netty中的处理器接口,负责处理网络事件,如连接建立、数据读写、异常处理等。开发者可以实现自定义的ChannelHandler以处理特定的业务逻辑。
  4. ChannelPipeline:ChannelPipeline是一个ChannelHandler的链表,负责管理和调度ChannelHandler。当一个网络事件发生时,ChannelPipeline会按照链表顺序将事件传递给各个ChannelHandler,直到其中一个处理器处理了事件或者到达链表尾部。
  5. ChannelHandlerContext:ChannelHandlerContext是ChannelHandler与ChannelPipeline之间的桥梁,允许ChannelHandler与Pipeline以及其他Handler进行交互。通过ChannelHandlerContext,Handler可以访问Channel、Pipeline,以及发送事件给其他Handler。
  6. ByteBuf:ByteBuf是Netty中的字节缓冲区,用于存储和处理字节数据。相较于Java的ByteBuffer,ByteBuf提供了更高效的内存管理和更简洁的API,支持自动扩容、复合缓冲区等特性。
  7. Bootstrap:Bootstrap是Netty中的启动类,用于配置和启动客户端或服务器。通过Bootstrap,开发者可以设置Channel的初始化参数、事件处理器等,以及绑定端口和启动监听。

四、应用场景与实例

Netty被广泛应用于分布式系统、实时通信、游戏开发等场景。例如,RocketMQ、Elasticsearch、Dubbo等知名的开源项目和大型企业都使用了Netty作为底层网络通信框架。这些应用通过Netty的高性能和灵活的设计,实现了高效、可靠的网络通信。

实战

netty服务器

@Componentpublic class NettyServer {static final Logger log = LoggerFactory.getLogger(NettyServer.class);/*** 端口号*/@Value("${webSocket.netty.port:8888}")int port;EventLoopGroup bossGroup;EventLoopGroup workGroup;@AutowiredProjectInitializer nettyInitializer;@PostConstructpublic void start() throws InterruptedException {new Thread(() -> {bossGroup = new NioEventLoopGroup();workGroup = new NioEventLoopGroup();ServerBootstrap bootstrap = new ServerBootstrap();// bossGroup辅助客户端的tcp连接请求, workGroup负责与客户端之前的读写操作bootstrap.group(bossGroup, workGroup);// 设置NIO类型的channelbootstrap.channel(NioServerSocketChannel.class);// 设置监听端口bootstrap.localAddress(new InetSocketAddress(port));// 设置管道bootstrap.childHandler(nettyInitializer);// 配置完成,开始绑定server,通过调用sync同步方法阻塞直到绑定成功ChannelFuture channelFuture = null;try {channelFuture = bootstrap.bind().sync();log.info("Server started and listen on:{}", channelFuture.channel().localAddress());// 对关闭通道进行监听channelFuture.channel().closeFuture().sync();} catch (InterruptedException e) {e.printStackTrace();}}).start();}/*** 释放资源*/@PreDestroypublic void destroy() throws InterruptedException {if (bossGroup != null) {bossGroup.shutdownGracefully().sync();}if (workGroup != null) {workGroup.shutdownGracefully().sync();}}
}

Netty配置

管理全局Channel以及用户对应的channel(推送消息)

  public class NettyConfig {/*** 定义全局单利channel组 管理所有channel*/private static volatile ChannelGroup channelGroup = null;/*** 存放请求ID与channel的对应关系*/private static volatile ConcurrentHashMap<String, Channel> channelMap = null;/*** 定义两把锁*/private static final Object lock1 = new Object();private static final Object lock2 = new Object();public static ChannelGroup getChannelGroup() {if (null == channelGroup) {synchronized (lock1) {if (null == channelGroup) {channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);}}}return channelGroup;}public static ConcurrentHashMap<String, Channel> getChannelMap() {if (null == channelMap) {synchronized (lock2) {if (null == channelMap) {channelMap = new ConcurrentHashMap<>();}}}return channelMap;}public static Channel getChannel(String userId) {if (null == channelMap) {return getChannelMap().get(userId);}return channelMap.get(userId);}}

管道配置

@Component
public class ProjectInitializer extends ChannelInitializer<SocketChannel> {/*** webSocket协议名*/static final String WEBSOCKET_PROTOCOL = "WebSocket";/*** webSocket路径*/@Value("${webSocket.netty.path:/webSocket}")String webSocketPath;@AutowiredWebSocketHandler webSocketHandler;@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {// 设置管道ChannelPipeline pipeline = socketChannel.pipeline();// 流水线管理通道中的处理程序(Handler),用来处理业务// webSocket协议本身是基于http协议的,所以这边也要使用http编解码器pipeline.addLast(new HttpServerCodec());pipeline.addLast(new ObjectEncoder());// 以块的方式来写的处理器pipeline.addLast(new ChunkedWriteHandler());pipeline.addLast(new HttpObjectAggregator(8192));pipeline.addLast(new WebSocketServerProtocolHandler(webSocketPath, WEBSOCKET_PROTOCOL, true, 65536 * 10));// 自定义的handler,处理业务逻辑pipeline.addLast(webSocketHandler);}
}

自定义handler

 @Component@ChannelHandler.Sharablepublic class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {private static final Logger log = LoggerFactory.getLogger(NettyServer.class);/*** 一旦连接,第一个被执行*/@Overridepublic void handlerAdded(ChannelHandlerContext ctx) throws Exception {log.info("有新的客户端链接:[{}]", ctx.channel().id().asLongText());// 添加到channelGroup 通道组NettyConfig.getChannelGroup().add(ctx.channel());}/*** 读取数据*/@Overrideprotected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {log.info("服务器收到消息:{}", msg.text());// 获取用户ID,关联channelJSONObject jsonObject = JSONUtil.parseObj(msg.text());String uid = jsonObject.getStr("uid");NettyConfig.getChannelMap().put(uid, ctx.channel());// 将用户ID作为自定义属性加入到channel中,方便随时channel中获取用户IDAttributeKey<String> key = AttributeKey.valueOf("userId");ctx.channel().attr(key).setIfAbsent(uid);// 回复消息ctx.channel().writeAndFlush(new TextWebSocketFrame("服务器收到消息啦"));}@Overridepublic void handlerRemoved(ChannelHandlerContext ctx) throws Exception {log.info("用户下线了:{}", ctx.channel().id().asLongText());// 删除通道NettyConfig.getChannelGroup().remove(ctx.channel());removeUserId(ctx);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {log.info("异常:{}", cause.getMessage());// 删除通道NettyConfig.getChannelGroup().remove(ctx.channel());removeUserId(ctx);ctx.close();}/*** 删除用户与channel的对应关系*/private void removeUserId(ChannelHandlerContext ctx) {AttributeKey<String> key = AttributeKey.valueOf("userId");String userId = ctx.channel().attr(key).get();NettyConfig.getChannelMap().remove(userId);}}推送消息接口及实现类public interface PushMsgService {/*** 推送给指定用户*/void pushMsgToOne(String userId, String msg);/*** 推送给所有用户*/void pushMsgToAll(String msg);}@Servicepublic class PushMsgServiceImpl implements PushMsgService {@Overridepublic void pushMsgToOne(String userId, String msg) {Channel channel = NettyConfig.getChannel(userId);if (Objects.isNull(channel)) {throw new RuntimeException("未连接socket服务器");}channel.writeAndFlush(new TextWebSocketFrame(msg));}@Overridepublic void pushMsgToAll(String msg) {NettyConfig.getChannelGroup().writeAndFlush(new TextWebSocketFrame(msg));}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/58248.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

macOS 15 Sequoia dmg格式转用于虚拟机的iso格式教程

想要把dmg格式转成iso格式&#xff0c;然后能在虚拟机上用&#xff0c;最起码新版的macOS镜像是不能用UltraISO&#xff0c;dmg2iso这种软件了&#xff0c;你直接转放到VMware里绝对读不出来&#xff0c;办法就是&#xff0c;在Mac系统中转换为cdr&#xff0c;然后再转成iso&am…

Unity3D学习FPS游戏(3)玩家第一人称视角转动和移动

前言&#xff1a;上一篇实现了角色简单的移动控制&#xff0c;但是实际游戏中玩家的视角是可以转动的&#xff0c;并根据转动后视角调整移动正前方。本篇实现玩家第一人称视角转动和移动&#xff0c;觉得有帮助的话可以点赞收藏支持一下&#xff01; 玩家第一人称视角 修复小问…

mysql 十把锁之《小猫钓鱼》

元数据锁&#xff1a;在这个美丽的森林里&#xff0c;小猫们决定要把钓鱼的成果记录下来。于是&#xff0c;它们首先需要创建一个 “鱼表” 来存放钓鱼的信息。当开始创建鱼表的时候&#xff0c;数据库自动为这个表加上了元数据锁。这个锁是为了防止在表的结构定义等元数据被修…

NAT技术和代理服务器

NAT IP原理 之前我们讨论了, IPv4协议中, IP地址数量不充足的问题 NAT技术当前解决IP地址不够用的主要手段, 是路由器的一个重要功能;NAT能够将私有IP对外通信时转为全局IP. 也就是就是一种将私有IP和全局IP相互转化的技术方法:很多学校, 家庭, 公司内部采用每个终端设置私有…

学习力体系

一、故事引入 在一个小镇上&#xff0c;有一位年轻的学者小明&#xff0c;梦想成为一名科 学家。然而&#xff0c;他总是感到学习困难&#xff0c;知识无法长久保持。有一天&#xff0c;他遇到了一位智者&#xff0c;智者告诉他&#xff1a;“学习的力量能够改变命运。”小明意…

批处理操作的优化

原来的代码 Override Transactional(rollbackFor Exception.class) public void batchAddQuestionsToBank(List<Long> questionIdList, Long questionBankId, User loginUser) {// 参数校验ThrowUtils.throwIf(CollUtil.isEmpty(questionIdList), ErrorCode.PARAMS_ERR…

013:无人机航线规划的概念

摘要&#xff1a;航线规划是无人机任务规划的核心环节&#xff0c;它决定了无人机在整个任务执行过程中的飞行路径。航线规划需要考虑多种因素&#xff0c;包括飞行时间、飞行距离、能源消耗、飞行安全等。。 一、概述 1. 概念与目标 无人机航线规划&#xff0c;是指在特定任…

2023IKCEST第五届“一带一路”国际大数据竞赛--社交网络中多模态虚假 媒体内容核查top11

比赛链接&#xff1a;https://aistudio.baidu.com/competition/detail/1030/0/introduction PPT链接&#xff1a;https://www.ikcest.org/bigdata2024/zlxz/list/page.html 赛题 社交网络中多模态虚假媒体内容核查 背景 随着新媒体时代信息媒介的多元化发展&#xff0c;各种内容…

GitHub Star 数量前 5 的开源应用程序生成器

欢迎来的 GitHub Star 数量排名系列文章的第 7 篇——最受欢迎的应用程序生成器。 之前我们已经详细探讨过&#xff1a;在 GitHub 上最受欢迎的——无代码工具、低代码项目、内部工具、CRUD项目、自部署项目和 Airtable 开源替代品。累计超过 50 个优质项目&#xff01;&#…

python 跳过当前循环

在 Python 中&#xff0c;可以使用 continue 语句来跳过当前循环的剩余部分&#xff0c;并继续下一次循环。continue 语句用于跳过循环体中剩余的语句&#xff0c;并立即开始下一次迭代。 以下是一个简单的示例&#xff0c;演示了如何在 for 循环中使用 continue 语句&#xf…

橘子多开同步器 v6.0 免费版

下载&#xff1a; 【1】https://drive.uc.cn/s/ddb0774e92924?public1 【2】https://pan.quark.cn/s/b5b1aae8c331 橘子多开同步器是一款专门为了游戏工作室而打造的免费游戏客户端多开同步工具&#xff0c;涵盖了包括客户端多开、客户端键鼠同步、智能防封等功能。 功能介…

Linux 进程优先级 进程切换

目录 优先级 概念 为什么优先级要限制在一定范围内 进程切换 方式 EIP寄存器(程序计数器) 进程在运行时会使用寄存器来保存临时数据 进程的上下文是什么&#xff1f; 进程的上下文保存到哪&#xff1f; 内核栈或专门的上下文结构也在内核空间&#xff1f;那为什么不直…

海外逆向代购:新机遇下的跨境赚钱之道

所谓逆向代购&#xff0c;即利用海外客源&#xff0c;将中国的优质商品反向代购至海外市场&#xff0c;实现跨境赚钱的同时&#xff0c;也让更多中国商品走向世界。 近年来&#xff0c;随着中国经济的飞速发展和消费水平的不断提升&#xff0c;中国商品在全球市场上的认可度越来…

基于springboot的旅游出行指南

文章目录 项目介绍主要功能截图:部分代码展示设计总结项目获取方式🍅 作者主页:超级无敌暴龙战士塔塔开 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系🍅 项目介绍 基于springboot的旅游出行指南,java项目…

C++算法练习-day19——18.四数之和

题目来源&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 题目思路分析 题目要求在给定的整数数组 nums 和一个目标值 target 中&#xff0c;找出所有独特的四元组&#xff08;四个数&#xff09;&#xff0c;使得这四个数的和等于 target。需要注意的是&#xff0c;解…

Svelte 5 正式发布:新一代前端框架!

10 月 22 日&#xff0c;Svelte 5 正式发布&#xff01;该版本带来的更新主要包括&#xff1a; 重写框架&#xff1a;Svelte 5 是从头开始重写的&#xff0c;使得应用更快、更小、更可靠&#xff0c;并且代码更一致和符合习惯。 向后兼容&#xff1a;Svelte 5 几乎完全向后兼容…

把代码绑定到WPF中的textblock中

在WPF中&#xff0c;将数据绑定到TextBlock控件中是一个常见的操作&#xff0c;这样可以动态显示数据源中的数据。以下是如何将数据绑定到TextBlock的步骤&#xff1a; 定义数据源&#xff1a; 首先&#xff0c;你需要有一个数据源&#xff0c;它可以是一个属性&#xff0c;这个…

一个简单的例子,说明Matrix类的妙用

在Android、前端或者别的平台的软件开发中&#xff0c;有时会遇到类似如下需求&#xff1a; 将某个图片显示到指定的区域&#xff1b;要求不改变图片本身的宽高比&#xff0c;进行缩放&#xff1b;要求最大限度的居中填充到显示区域。 以下示意图可以简单描绘该需求 以Androi…

ETL、ELT和反向ETL都有什么不同?怎么选择?

数据处理是现代企业中不可或缺的一部分。随着数据量的不断增长&#xff0c;如何高效地处理、转换和加载数据变得尤为重要。本文将介绍三种常见的数据处理方式&#xff1a;ETL、ELT和反向ETL&#xff0c;帮助读者更好地理解和选择适合自己业务需求的方式。 一、ETL 定义&#…

Apache POI—读写Office格式文件

Apache POI 是一个开源的 Java 库&#xff0c;用于读写 Microsoft Office 格式的文件&#xff0c;主要包括 Excel、Word 和 PowerPoint 等文档。POI 对 Excel 文件的支持最为完善&#xff0c;通过 POI 可以方便地进行 Excel 文件的创建、编辑、读取等操作。 1. Apache POI 简介…