netty组件详解-上

netty服务端示例:

private void doStart() throws InterruptedException {System.out.println("netty服务已启动");// 线程组EventLoopGroup group = new NioEventLoopGroup();try {// 创建服务器端引导类ServerBootstrap server = new ServerBootstrap();// 初始化服务器配置server.group(group) // 配置处理客户端的连接线程组.channel(NioServerSocketChannel.class) // 指定channel为 NioServerSocketChannel.localAddress(port) // 配置服务端口号.childHandler(new ChannelInitializer<SocketChannel>() { // 指定客户端通信的处理类,添加到pipline中,进行初始化@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {socketChannel.pipeline().addLast(new EchoServerHandler());}});// 绑定端口,sync()会阻塞到完成ChannelFuture sync = server.bind().sync();// 阻塞当前线程,直到服务器的ServerChannel被关闭sync.channel().closeFuture().sync();}finally {// 关闭资源group.shutdownGracefully().sync();}}

netty各组件解析;

  1. EventLoop 与 EventGroup
    EventLoop : 单线程+任务队列
    EventGroup: 多个EventLoop
    在这里插入图片描述
    思考问题: netty底层每个channel中的事件都是由同一个EventLoop来处理的,而EventLoop是单线程的,这样无需考虑为了并发冲突而加锁的问题,提升了性能。并发高效的本质,不是关注如何科学安全的加锁,而是想尽办法避免加锁,来提升性能。
  2. channel接口
    每个channel都会被注册到一个EventLoop上,以下是Channel抽象出的方法
    在这里插入图片描述
    每个channel都有自己的生命周期,channel在生命周期的不同节点会回调不同的处理函数:
    (1)当channel被注册到EventLoop中时,会调用isRegistered()方法,来确认是否注册成功
    (2)每个channel在处理事件时,都有一个对应的pipeline,这个pipeline中以责任链模式来处理事件
  3. channelPipeline &&channelHandler
    channelPipeline 的实现是一个双向链表,链表的每个节点对应一个channelHandler,每个channel在处理事件时会调用channelPipeline中的各个channelHandler
    channelHandler也有自己的生命周期,在添加到channelPipeline 或被移除出channelPipeline 时,会调用相应的生命周期方法。
    在这里插入图片描述
  4. channelPipeline的入站事件和出站事件
    netty如何在同一个channelPipeline中区分出出站事件链路和入站事件链路?
    在这里插入图片描述
    在这里插入图片描述
  5. ChanelHandlerContext 上下文
    表示ChannelPipeline和ChannelHandler关联
    ChannelPipeline 是双向链表
    ChanelHandlerContext 维护了双向链表的pre和 next 指针
    具体实现:
    在这里插入图片描述
    ChanelHandlerContext 的作用不仅仅只是维护了指针信息,而且还需要控制channelPipeline中每个ChannelHandler处理的方向和数据流动,比如像下面这些:
    在这里插入图片描述
    ChanelHandlerContext 中的写方法区别:
    在这里插入图片描述
    pipline中有一系列链式的处理逻辑:
    在这里插入图片描述
    ctx.write(in) / ctx.writeAndFlush(in):
    在某个入站事件中的handler中直接找到pipline中最近的出站事件节点,在出站事件中输出数据.
    ctx.pipeline().write(in) /ctx.channel().write(in)
    在当前入站事件handler结束后,继续按照pipline中handler的顺序依次处理后,在输出数据。
    对比上面两种做法:
    可根据业务需求进行优化,不经过pipeline直接返回的效率更快。
  6. channelHandler的适配器
    channelHandler根据功能,设计了几种适配器,其中包括ChannelOutboundHandlerAdapter出站事件适配器
    问题:为什么ChannelOutboundHandlerAdapter中包含一个read()事件方法
    netty将read()动作打包成一个读事件放到了pipeLine中
  7. channelHandler的并发共享机制:
    根据netty的设计,每个socketchannel都是由一个EventLoop线程处理的,每个channel中包含一个pipeline,而pipeline中的每个handler在使用的时候都是重新new 的一个实例,由于每个socket都是独立的线程隔离的,因此每个socket是线程安全的。
    但有些业务场景需要各个socket之间共享通信,比如要统计服务器接收到的报文总数。此时就要维护一个共享变量 total,每来一个新的
    socket都要去进行 total+1的操作,此时就会产生并发安全问题。
    netty如何解决这个问题呢,我们可以定义一个共享的hander,这个hander的定义成一个全局共享的,每个socketchannel的pipeline都添加这个handler,通过这个并发共享的handler来实现socket进程间的通信,代码如下:
    (1)首先定义一个共享的handler,其内部实现统计报文的业务逻辑:
// 这个注解的含义就是声明这个handler是共享的
// 如果不声明这个注解的话,在添加到pipline中时会报错
@ChannelHandler.Sharable
public class MessageCountHandler extends ChannelDuplexHandler {private static final Logger LOG = LoggerFactory.getLogger(MessageCountHandler.class);private AtomicLong inCount = new AtomicLong(0);private AtomicLong outCount = new AtomicLong(0);@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {LOG.info("收到报文总数:"+inCount.incrementAndGet());super.channelRead(ctx, msg);}@Overridepublic void flush(ChannelHandlerContext ctx) throws Exception {LOG.info("发出报文总数:"+outCount.incrementAndGet());super.flush(ctx);}
}

(2)服务器端实现

public void start() throws InterruptedException {// 将统计报文的handler定义成共享变量final MessageCountHandler messageCountHandler = new MessageCountHandler();/*线程组*/EventLoopGroup boss  = new NioEventLoopGroup();EventLoopGroup work  = new NioEventLoopGroup();try {/*服务端启动类*/ServerBootstrap b = new ServerBootstrap();b.group(boss,work).channel(NioServerSocketChannel.class)/*指定使用NIO的通信模式*/.localAddress(new InetSocketAddress(port))/*指定监听端口*/ .childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new LineBasedFrameDecoder(1024));ch.pipeline().addLast(messageCountHandler); // 添加一个共享的hander到pipeline中ch.pipeline().addLast(new EchoServerMCHandler());}});ChannelFuture f = b.bind().sync();/*异步绑定到服务器,sync()会阻塞到完成*/LOG.info("服务器启动完成");f.channel().closeFuture().sync();/*阻塞当前线程,直到服务器的ServerChannel被关闭*/} finally {boss.shutdownGracefully().sync();work.shutdownGracefully().sync();}}
  1. netty内存泄露与资源释放注意事项
    netty底层的实现机制是java的nio,因此其通信机制是面向缓冲区的,在nio中,当channel中有事件发生时,比如读事件时,会将数据读取到一块直接内存中,当我们处理完这部分数据的时候,应该将这块内存资源释放掉,以防止内存泄露。那么这部分netty是怎么做的呢?
    (1)netty中数据的处理是在pipeline中,由每个handler进行处理,那么netty在定义pipeline时,默认在链表的头尾帮我们各自实现了一个handler用于资源分配与释放的。
    源码:
    在这里插入图片描述
    从这个ch.pipeline()方法点进去后,找到对应实现

在这里插入图片描述
在这里插入图片描述
我们可以发现,pipeline的创建是基于DefaultChannelPipeline.class这个类
在这里插入图片描述
我们找到DefaultChannelPipeline.class这个类,可以看到这head和tail在pipeline初始化的时候就被添加进去了
在这里插入图片描述
我们找到这个方法看看这个headContext,可以看到他继承自AbstractChannelHandlerContext,并实现了入站和出站事件处理方法
在这里插入图片描述
可以看到这是一个内部类,可以看到,他是实现了资源释放及异常处理的方法
在这里插入图片描述
正常情况下,如果事件在pipeline中正常传递的情况下,我们无需手动去管理资源,但是有一种情况,需要手动释放资源
在这里插入图片描述
上面的情况就是,当事件读取发生异常,或因为某些业务需求,不能将该事件向pipeline中传递时,需要自己实现资源释放逻辑
在这里插入图片描述
此外,大部分的业务逻辑是在入站事件中资源在某个handler中读取异常时终止传递,否则就正常传递,针对这种业务,netty还单独实现了一个handler来实现异常时自动释放资源,即 SimpleChannelInboundHandler:在这里插入图片描述
实现SimpleChannelInboundHandler后,在发生异常时我们无需手动去释放资源,看源码:

在这里插入图片描述
因此,到这里,关于资源释放的问题,我们可以有三种做法:
(1)无论何时都保证让业务在pipeline中正常传递,依靠DefaultPipeLine中的head和tail来保证资源的释放
(2)在代码逻辑中手动释放资源 如: ctx.fireChannelRead(msg);
(3)继承SimpleChannelInboundHandler这个类,重写channelRead0(ChannelHandlerContext ctx, ByteBuf msg)方法

  1. 同时处理入站和出站事件
    netty中根据业务模型为我们提供了 ChannelInboundHandlerAdapter 和 ChannelOutboundHandler分别处理入站和出站事件,但是有时,我们需要同时处理入站和出站事件,这里netty为我们提供了 ChannelDuplexHandler 这个实现,我们看源码:
    在这里插入图片描述

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

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

相关文章

苹果APP安装包ipa如何安装在手机上

苹果APP安装包ipa如何安装在手机上 苹果APP的安装比安卓复杂且困难&#xff0c;很多人不知道如何将ipa文件安装到手机上。以下是几种苹果APP安装在iOS设备的方式&#xff0c;供大家参考。 一、上架App Store 这是最正规的方式。虽然审核过程复杂、时间较长&#xff0c;且审核…

数据可视化组件有什么用?

数据可视化组件在数据分析中扮演着至关重要&角色。 通过图表、图形和交互式界面&#xff0c;数据可视化组件帮助将复杂的数据转化为易于理解的视觉展示。这种形式的数据呈现有助于发现模式、趋势和异常&#xff0c;并能够快速有效地传达数据的含义和洞察。 下面简单举两个…

使用Visual Studio打造强大的程序,从添加第三方库开始

使用Visual Studio打造强大的程序&#xff0c;从添加第三方库开始 博主简介一、引言二、理解第三方库三、下载和安装第三方库四、示例代码和演示五、总结 博主简介 &#x1f4a1;一个热爱分享高性能服务器后台开发知识的博主&#xff0c;目标是通过理论与代码实践的结合&#x…

【状态估计】基于FOMIAUKF、分数阶模块、模型估计、多新息系数的电池SOC估计研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

SpringBoot项目中MVC使用--【JSB系列之010】

SpringBoot系列文章目录 SpringBoot知识范围-学习步骤【JSB系列之000】 文章目录 SpringBoot系列文章目录Http协议是马冬梅Cookie机制Session机制Token MVC模型本章的专注内容UserController代码 ThymeleafLets GO!总结作业配套资源题外话 Http协议是马冬梅 HTTP简介 1. HTTP…

润和软件与华秋达成生态共创合作,共同推动物联网硬件创新

7月11日&#xff0c;在2023慕尼黑上海电子展现场&#xff0c;江苏润开鸿数字科技有限公司(以下简称“润开鸿”)与深圳华秋电子有限公司(以下简称“华秋”)签署了生态共创战略合作协议&#xff0c;共同推动物联网硬件生态繁荣发展。当前双方主要基于润开鸿的硬件产品及解决方案开…

完整的电商平台后端API开发总结

对于开发一个Web项目来说&#xff0c;无论是电商还是其他品类的项目&#xff0c;注册与登录模块都是必不可少的&#xff1b;注册登录功能也是我们在日常生活中最长接触的&#xff0c;对于这个业务场景的需求与逻辑大概是没有什么需要详细介绍的&#xff0c;市面上常见的邮箱注册…

混合背包(01+完全+多重背包大杂烩)

因为我们知道求解多重背包时&#xff0c;是将其进行二进制优化为01背包问题&#xff0c;那么我们就将01背包和多重背包看成一种情况&#xff0c;然后只要处理&#xff0c;完全背包和01背包问题即可&#xff08;详细看下方代码&#xff09; #include<bits/stdc.h> using n…

【ArcGIS Pro二次开发】(47):要素类追加至空库(批量)

本工具主要是针对国空数据入库而做的。 如果你手头已经整理了一部分要素类数据&#xff0c;但是数据格式&#xff0c;字段值可能并没有完全按照规范设置好&#xff0c;需要将这些数据按规范批量和库&#xff0c;就可以尝试用这个工具。 准备数据&#xff1a;标准空库、你已做…

Python、Selenium实现问卷星自动填写(内含适配个人问卷的方法)

&#x1f9d1;‍&#x1f4bb;作者名称&#xff1a;DaenCode &#x1f3a4;作者简介&#xff1a;啥技术都喜欢捣鼓捣鼓&#xff0c;喜欢分享技术、经验、生活。 &#x1f60e;人生感悟&#xff1a;尝尽人生百味&#xff0c;方知世间冷暖。 &#x1f4d6;所属专栏&#xff1a;Py…

SpringMVC的数据响应-直接回写json字符串

一般我们操作对象&#xff0c;将对象转变为json 这时导入json 转换工具的包 包1 包2-json数据绑定 包3 返回的就是json字符串你直接返回就行了 返回一个json格式的字符串 直接回写就加这个res.... 内部字符串要进行相应的转意 能够看到json字符串 能不能你封装对象&#xff0c…

【蓝图】p28-p29按键+鼠标点击实现开关门

p28-p29按键鼠标点击实现开关门 p28&#xff0c;创建门的蓝图类创建一个Actor注意&#xff08;当门的中心点不在边角上时&#xff09; 蓝图三个旋转区别按E键开关门使鼠标点击也可以开门可能遇到的bug问题 p28&#xff0c;创建门的蓝图类 actor和组件的区别、门的轴心点修改 …

【Ajax】笔记-取消请求

在进行AJAX(Asynchronous JavaScript and XML) 请求时&#xff0c;有时候我们需要取消正在进行的请求。取消请求可以帮助我们提高用户体验&#xff0c;病减少不必要的网络流量和服务器负载。 取消请求的方法 在AJAX请求中&#xff0c;我们可以使用以下方法来取消正在进行的请求…

golang 日志库logrus和lumberjack 日志切割库实践

package mainimport (log "github.com/Sirupsen/logrus""gopkg.in/natefinch/lumberjack.v2" )func main() {logger : &lumberjack.Logger{// 日志输出文件路径Filename: "/var/log/myapp/foo.log",// 日志文件最大 size, 单位是 MBMaxSiz…

数字 IC 设计职位经典笔/面试题(二)

共100道经典笔试、面试题目&#xff08;文末可全领&#xff09; FPGA 中可以综合实现为 RAM/ROM/CAM 的三种资源及其注意事项&#xff1f; 三种资源&#xff1a;BLOCK RAM&#xff0c;触发器&#xff08;FF&#xff09;&#xff0c;查找表&#xff08;LUT&#xff09;&#xf…

ROS:pluginlib

目录 一、前言二、概念三、作用四实际用例4.1需求4.2流程4.3准备4.4创建基类4.5创建插件4.6注册插件4.7构建插件库4.8使插件可用于ROS工具链4.8.1配置xml4.8.2导出插件 4.9使用插件4.10执行 一、前言 pluginlib直译是插件库&#xff0c;所谓插件字面意思就是可插拔的组件&…

河北幸福消费金融基于 Apache Doris 构建实时数仓,查询提速 400 倍!

本文导读&#xff1a; 随着河北幸福消费金融的客户数量和放贷金额持续上升&#xff0c;如何依托大数据、数据分析等技术来提供更好决策支持、提高工作效率和用户体验&#xff0c;成为了当前亟需解决的问题。基于此&#xff0c;公司决定搭建数据中台&#xff0c;从基于 TDH 的离…

Windows 如何锁定文件

一、背景 如果应用程序有操作本地文件的功能&#xff08;如&#xff1a;读、写、复制、移动、删除等等&#xff09;&#xff0c;那么在测试或调试该应用程序时&#xff0c;肯定需要测试文件被其他应用程序锁定时&#xff0c;你的应用程序是如何处理的。 那么如何在本地模拟文件…

Ceph(分布式文件系统)

Ceph(分布式文件系统) 1、存储基础 单机存储设备 ●DAS&#xff08;直接附加存储&#xff0c;是直接接到计算机的主板总线上去的存储&#xff09; IDE、SATA、SCSI、SAS、USB 接口的磁盘 所谓接口就是一种存储设备驱动下的磁盘设备&#xff0c;提供块级别的存储 ●NAS&#xf…

基于springboot的地铁轨道交通运营系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…