TCP 协议
tcp是一个面向流的协议 当两端的流动速度 不一致的时候 ,就会出现 某一方的宕机
故障场景
这是一个 Client 不断向服务端发送数据的例子
public class LoadRunnerSleepClientHandler extends ChannelInboundHandlerAdapter {
static final int SIZE = Integer.parseInt(System.getProperty("size", "10240"));
private final ByteBuf firstMessage;
Runnable loadRunner;
AtomicLong sendSum = new AtomicLong(0);
Runnable profileMonitor;
/**
* Creates a client-side handler.
*/
public LoadRunnerSleepClientHandler() {
firstMessage = Unpooled.buffer(SIZE);
for (int i = 0; i < firstMessage.capacity(); i++) {
firstMessage.writeByte((byte) i);
}
}
@Override
public void channelActive(final ChannelHandlerContext ctx) {
loadRunner = new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
ByteBuf msg = null;
while (true) {
byte[] body = new byte[SIZE];
msg = Unpooled.wrappedBuffer(body);
ctx.writeAndFlush(msg);
try {
TimeUnit.MILLISECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
new Thread(loadRunner, "LoadRunner-Thread").start();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ReferenceCountUtil.release(msg);
}
当通道激活的时候 不断向服务端发送数据
下面是我做的测试 在30S之后 内存暴增 GC暴增 CPU的满负荷 电脑卡的不行
显然 Netty将发送操作封装为Writetask 在NioEventLoop 中执行
解决方法
使用Netty的高水位 限制发送的流量速度
public final class LoadRunnerClient {
static final String HOST = System.getProperty("host", "127.0.0.1");
static final int PORT = Integer.parseInt(System.getProperty("port", "18085"));
@SuppressWarnings({"unchecked", "deprecation"})
public static void main(String[] args) throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, 10 * 1024 * 1024)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new LoadRunnerClientHandler());
// p.addLast(new LoadRunnerWaterClientHandler());
p.addLast(new LoadRunnerSleepClientHandler());
}
});
ChannelFuture f = b.connect(HOST, PORT).sync();
f.channel().closeFuture().sync();
} finally {
group.shutdownGracefully();
}
}
}
ChannelOption.WRITE_BUFFER_HIGH_WATER-MARK
限制了最大的发送值是10M
@Override
public void channelActive(final ChannelHandlerContext ctx) {
ctx.channel().config().setWriteBufferHighWaterMark(10 * 1024 * 1024);
loadRunner = new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
ByteBuf msg = null;
while (true) {
if (ctx.channel().isWritable()) {
msg = Unpooled.wrappedBuffer("Netty OOM Example".getBytes());
ctx.writeAndFlush(msg);
} else {
LOG.warning("The write queue is busy : " + ctx.channel().unsafe().outboundBuffer().nioBufferSize());
}
}
}
};
new Thread(loadRunner, "LoadRunner-Thread").start();
}
当channel 激活的时候 设置了10M的限制
在发送的时候 当可以写的时候 才回去发送
运行平稳