netty(九): 流量整形

1. 定义

流量整形是为了控制当前服务的流量输出,保证下游节点的正常处理,如图所示,将流量洪峰放入队列中,使用令牌桶算法来保证流量不会突破输出极限,保证下游收到的数据都是平稳的。

在这里插入图片描述

分为:
1) GlobalTrafficShapingHandler:全局流量整形,放在服务器端,表示所有链接该服务器的channel整体的流量不超过阈值
2)ChannelTrafficShapingHandler:表示单个channel的流量作出限制

客户端:启动代码

public class TrafficShappingClient {

    public void connect(int port, String host) throws Exception {

	EventLoopGroup group = new NioEventLoopGroup();
	try {
	    Bootstrap b = new Bootstrap();
	    b.group(group).channel(NioSocketChannel.class)
		    .option(ChannelOption.TCP_NODELAY, true)
		    .handler(new ChannelInitializer<SocketChannel>() {
			@Override
			public void initChannel(SocketChannel ch)
				throws Exception {
//			    ByteBuf delimiter = Unpooled.copiedBuffer("$_"
//				    .getBytes());
//			    ch.pipeline().addLast(
//				    new DelimiterBasedFrameDecoder(2048 * 1024,
//					    delimiter));
			    ch.pipeline().addLast(new StringDecoder());
			    ch.pipeline().addLast(new TrafficShappingClientHandler());
			}
		    });

	    ChannelFuture f = b.connect(host, port).sync();
	    f.channel().closeFuture().sync();
	} finally {
	    group.shutdownGracefully();
	}
    }

    /**
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
	int port = 18091;
	if (args != null && args.length > 0) {
	    try {
		port = Integer.valueOf(args[0]);
	    } catch (NumberFormatException e) {
	    }
	}
	new TrafficShappingClient().connect(port, "127.0.0.1");
    }
}

客户端:自定义的额handler


public class TrafficShappingClientHandler extends ChannelInboundHandlerAdapter {

    private static AtomicInteger SEQ = new AtomicInteger(0);

    static final byte[] ECHO_REQ = new byte[1024 * 1024];

    static ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        scheduledExecutorService.scheduleAtFixedRate(()
                -> {
            ByteBuf buf = null;
            for (int i = 0; i < 3; i++) {
                buf = Unpooled.copiedBuffer(ECHO_REQ);
                if (ctx.channel().isWritable()) {
                    SEQ.getAndAdd(buf.readableBytes());
                    ctx.write(buf);
                }
            }
            ctx.flush();
            int counter = SEQ.getAndSet(0);
            System.out.println("The client send rate is : " + (double)counter/(1024*1024) + " M/s");
        }, 0, 1000, TimeUnit.MILLISECONDS);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}

服务器:启动代码

public class TrafficShappingServer {
    public void bind(int port) throws Exception {

	EventLoopGroup bossGroup = new NioEventLoopGroup();
	EventLoopGroup workerGroup = new NioEventLoopGroup();
	try {
	    ServerBootstrap b = new ServerBootstrap();
	    b.group(bossGroup, workerGroup)
		    .channel(NioServerSocketChannel.class)
		    .option(ChannelOption.SO_BACKLOG, 100)
		    .handler(new LoggingHandler(LogLevel.INFO))

		    .childHandler(new ChannelInitializer<SocketChannel>() {
			@Override
			public void initChannel(SocketChannel ch)
				throws Exception {
				ch.pipeline().addLast("Channel Traffic Shaping",new GlobalTrafficShapingHandler(ch.eventLoop().parent(),1*1024 * 1024,1*1024 * 1024, 1000));
//			    ByteBuf delimiter = Unpooled.copiedBuffer("$_"
//				    .getBytes());
//			    ch.pipeline().addLast(
//				    new DelimiterBasedFrameDecoder(2048 * 1024,
//					    delimiter));
			    ch.pipeline().addLast(new StringDecoder());
			    ch.pipeline().addLast(new TrafficShapingServerHandler());
			}
		    });

	    ChannelFuture f = b.bind(port).sync();
	    f.channel().closeFuture().sync();
	} finally {
	    bossGroup.shutdownGracefully();
	    workerGroup.shutdownGracefully();
	}
    }

    public static void main(String[] args) throws Exception {
	int port = 18091;
	if (args != null && args.length > 0) {
	    try {
		port = Integer.valueOf(args[0]);
	    } catch (NumberFormatException e) {
	    }
	}
	new TrafficShappingServer().bind(port);
    }
}

服务器:自定义代码

@Sharable
public class TrafficShapingServerHandler extends ChannelInboundHandlerAdapter {

    // 设置一个计数器,用于计算流量的大小
    AtomicLong counter = new AtomicLong(0);
    ScheduledExecutorService es = Executors.newScheduledThreadPool(1);

    /*
     * 工具方法:开始流量计算
     * 这是一个定时线程,每秒计算读写的速率,然后将count设置为0,用于下一次计算1秒内的读写总和
     */
    public TrafficShapingServerHandler() {
        es.scheduleAtFixedRate(() ->
        {
            double counnterTmp = (double)counter.getAndSet(0)/(1024*1024);
            System.out.println("The server receive client rate is : " + counnterTmp + " M/s");
        }, 0, 1000, TimeUnit.MILLISECONDS);

    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg)
            throws Exception {
        String body = (String) msg;
        counter.addAndGet(body.getBytes().length);// 计算传入的数据的流量
        ByteBuf echo = Unpooled.copiedBuffer(body.getBytes());
        ctx.writeAndFlush(echo);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}

客户端每秒发送3M/s的数据,服务端流量限制为1M/s

服务端测试:
在这里插入图片描述

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

发布了275 篇原创文章 · 获赞 42 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/qq_35688140/article/details/104399955