Netty笔记(优雅退出,流量控制,流量整形,内存池,读写队列积压,内存泄漏)

本文介绍了Netty服务端开发中的关键点,包括优雅关闭、内存池的高效使用、流量控制和流量整形。在优雅关闭部分,强调了同步与异步思维的重要性,以及如何实现优雅的关闭过程。内存池章节讨论了池化内存的优缺点及内存泄漏问题。流量控制部分解释了如何避免服务器因读写队列积压导致的性能问题。最后,文章提到了流量整形的常见误解,并建议在服务端设计中优先考虑客户端的处理能力。
摘要由CSDN通过智能技术生成

一个稳定高效的netty服务端开发.

我以通俗易懂的语言
记录了,优雅退出,流量控制,流量整形,内存池,读写队列积压,内存泄漏 等重要点
如果你看到了这文章,对你有一点帮助,可以点个赞

前言:


假设客户端每秒给服务器发送1G的数据
而服务器每秒一定要读取1G吗?服务端会在高峰时期导致读队列积压然后宕机吗

假设服务端每秒给客户端发送1G的数据
而客户端接收的过来吗? 高峰期服务端会造成发送队列积压导致雪崩吗

netty的bytebuf中的堆外内存,堆内存,池化和未池化,如何正确使用?
netty的work线程组如何保证高效执行.
netty的优雅退出之类的.
很多这样问题都很有意思.这是入坑netty最常见的疑问.


0.同步思维

需要先记得比较重要的一件事.
当我们调用了channel的write或writeandflush方法,并不代表数据真的发送过去了
这个时候对发送字节做统计是不准确的,netty的网络io操作都是异步的,并不是同步执行的.
异步和同步的思维是不同的,不是说调用了这个方法后就立刻执行.

如果想监听是否发送的成功,应该在write后返回的channelfuture对象中进行判断.
当write发送成功后,write就会回调channelfuturelisten的operationcomplete方法.
但是异步的回调方法的执行是由Work的NioEventloop线程调用的,
也要保证该线程不能阻塞,或者在回调内开启一个业务线程执行.并且也要考虑多线程的并发安全问题.

ByteBuf bt1 = Unpooled.buffer();
bt1.writeBytes("我爱netty".getBytes("UTF-8"));
ChannelFuture cf = ctx.writeAndFlush(bt1);
cf.addListener((f)->
{
   
	System.out.println("数据发送成功的回调");
});

1.Netty的优雅关闭 同步/异步思维

Main运行后,JVM管理的多个线程中(main主线程,GC守护线程,部分子线程)
只有非守护线程都运行完毕后,Jvm就会自动推出并且关闭守护线程,
守护线程常见的有GC垃圾回收,也可以自己设定线程为守护线程
Thread.setDaemon(true);可以用来进行一些无关紧要的日志输出为守护线程.

Netty中通过boostrap.bind(port)绑定端口这个方法,
并非在调用方线程执行,通俗的说,虽然是main线程调用的它,但它会用自己的线程执行这个操作
(也就是底层通过NioEventLoop线程执行)(调用方main,执行方nioEventLoop)

这个时候异步就产生了,main线程进行接下来的代码执行,NioEventLoop线程进行端口绑定.
而我们的多数工作都需要等端口绑定完毕后执行,所以需要在.bind(port)后面添加.sync()方法
它能保证让我们的main线程,在这个异步方法执行完毕后,再往后执行,会让main阻塞下来等待.

1.1 Netty的优雅关闭 意外情况

根据上面的特性来说
绑定完毕线程后,如果设置端口的时候,没设置sync(),
这个时候main线程没有被阻塞住,且main后面没有任何代码需要执行的时候
那么当NioEventLoop执行完毕绑定端口后,main线程代码也早已执行完毕,已经不存在了.
如果main中写了finally,就代表运行即退出
(一个线程绑定端口,一个线程运行完毕执行了finally,异步就混乱了)这是非常需要注意的.

注意的就是NioEventLoop是非守护线程,运行之后,不会主动退出,只有调用shutdown才会退出.
如果你的netty服务端意外退出了,那么很大概率是被调用了shutdown.

1.2 Netty的优雅关闭 优雅的执行过程

public static void main(String[] args) throws InterruptedException {
   
	EventLoopGroup boosGroup = new NioEventLoopGroup();
    EventLoopGroup workerGroup = new NioEventLoopGroup();
	try {
   
		ServerBootstrap sb = new ServerBootstrap();
		sb.group(boosGroup, workerGroup)
		.channel(NioServerSocketChannel.class)
		.childHandler(new ChannelInitializer<SocketChannel>() {
   
			@Override
			protected void initChannel(SocketChannel ch) throws Exception {
   
				//代码省略
			}
		});
		ChannelFuture cf = sb.bind(9999).sync();
		cf.channel().closeFuture().sync();//A标记
	} finally {
   
		boosGroup.shutdownGracefully();
		workerGroup.shutdownGracefully();
	}
}

一般shutdownGracefully会写在finally中比如上面的操作:
这个代码的运行也是没有任何问题的.但是它不优雅.
这样会有一个main线程在A标记处无限的阻塞
此时main线程最终作用,就是等待A标记处的sync的返回,这样也会浪费一个main线程…
它有点违背了netty的异步操作的概念了.

可以把shutdownGracefully函数写在cf.channel().closeFuture().addListener中,
这样main线程会主动释放无需在阻塞等待了.并且管道关闭的时候,会自动的进行优雅关闭.
优雅式:初始化netty服务端,绑定监听端口,向colosefuture注册监

  • 8
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值