Netty-future and promise demonstration and rate control case

Netty-future and promise demonstration and rate control case

1. Demonstration of future and promise

1、Future

Future mode is a processing mode when dealing with asynchronous execution. It has two programming modes.
The first type: future tense-let the execution thread get a marked object of asynchronous call. If the execution thread calls future.get(), then it can wait for the result of the asynchronous execution.
 The second type: callback type-by adding a callback to the Future, the result of asynchronous execution can be obtained through the callback

Case 1:

/**
	 * 案例1:使用JDk的Future模式 -- 将来式 
	 */
	@Test
	public void test1() throws Exception {
		long begin = System.currentTimeMillis();

		ExecutorService executorService = Executors.newSingleThreadExecutor();

		Future<Integer> future = executorService.submit(new Callable<Integer>() {
			@Override
			public Integer call() throws Exception {
				System.out.println("执行耗时操作...");
				excuteAsync();
				return 100;
			}
		});
		System.out.println("计算结果:" + future.get());// get会阻塞主线程 - 直至结果完成
		System.out.println("主线程运算耗时:" + (System.currentTimeMillis() - begin) + "ms");
	}

    // As can be seen from the above results, the asynchronous operation of the execution thread is handed over to another thread for execution, and the Future mark is obtained. If .get can block until the result is complete!
Case 2:

/**
	 * 案例2:使用Netty的Future模式 -- 回调式  (因为jdk的Futrue不能注册监听)
	 * 什么是回调Future?即异步执行的结果,是通过我在Future上注册的监听拿到。
	 */
	@Test
	public void test2() throws Exception {
		EventExecutorGroup group = new DefaultEventExecutorGroup(4); // Netty线程池
		long begin = System.currentTimeMillis();
		io.netty.util.concurrent.Future<Integer> f = group.submit(new Callable<Integer>() {
			@Override
			public Integer call() throws Exception {
				System.out.println("执行耗时操作...");
				excuteAsync();
				return 100;
			}
		});
		// 注册的监听可以拿到异步执行的结果
		f.addListener(new FutureListener<Object>() {
			@Override
			public void operationComplete(io.netty.util.concurrent.Future<Object> objectFuture) throws Exception {
				System.out.println("计算结果::" + objectFuture.get());
				System.out.println("真实运行结果时间:" + ( System.currentTimeMillis() - begin ) + "ms");
			}
		});
		System.out.println("主线程运算耗时:" + (System.currentTimeMillis() - begin) + "ms");
		// 阻塞主线程
		new CountDownLatch(1).await();
	}

// From the above results, it can be seen that the asynchronous result received by the execution thread is obtained by registering a listener callback, and the main thread only executes for a short time.

Looking at the callback mode of Furue, we can think of the asynchronous operation mode of js. The asynchronous operation of js is originally an infinite callback mode. The emergence of ES6's Promise wraps the asynchronous operation into a Promise object and calls the callback method resolve( ), you can call back the processing result in the then method, so that although the execution is still asynchronous, the code appears to be synchronous.

Let's take a look at Jdk's CompletableFuture to add a listener to Future

Case 3:

 * 案例3: 下面我们用jdk的CompletableFuture演示类似Promise效果
	 */
	@Test
	public void test3() throws Exception {
		long l = System.currentTimeMillis();
		CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
			System.out.println("执行耗时操作...");
			excuteAsync();
			return 100;
		});
		completableFuture.whenComplete((result, e) -> {
			System.out.println("结果:" + result);
		});
		System.out.println("主线程运算耗时:" + (System.currentTimeMillis() - l) + "ms");
		new CountDownLatch(1).await();
	}
	

This is different from the use of js's promise. The asynchronous completion of js is received through the resolve function, and this is the return through the asynchronous execution function. Netty has made an upgraded version of Future, which is Promise. In addition to adding monitoring, promise can also manually control the completion of asynchronous execution.

2、promise

Case 4:


	/**
	 * 案例4:netty的promise的基本使用演示
	 * netty的promise对于future除了可以添加监听器外,最主要的是,promise对象可以设置完成状态等操作
	 */
	@Test
	public void test4() throws InterruptedException {
		
		// 构建promise  --- > js new Promise()
		Promise<String> promise = new DefaultPromise<>(GlobalEventExecutor.INSTANCE);
		
		// 给promise添加监听  --- > js  Promise.then(ele -> {} )
		promise.addListener(new GenericFutureListener<io.netty.util.concurrent.Future<String>>(){

			@Override
			public void operationComplete(io.netty.util.concurrent.Future<String> future) throws Exception {
			   if (future.isSuccess()) {
                    System.out.println("消息成功 success:" + future.get());
                } else {
                    System.out.println("消息失败 failure:" + future.cause());
                }
			}
			
		});
		
		// 给promise设置完成状态 --- > js resolve() 调用
		new Thread(() -> {
			excuteAsync();
			promise.setSuccess("结果"); // 设置完成状态 , 会触发监听
		}).start();
		
		new CountDownLatch(1).await();
	}

Second, the case of rate control

1. Demo function introduction

The rate control is mainly through the codecs of ChannelTrafficShapingHandler (client) and GlobalTrafficShapingHandler (server), the parameter is set to 10, and the default unit is byte/s.

such as:

    ch.pipeline().addLast(new ChannelTrafficShapingHandler(10, 10)); // client

   ch.pipeline().addLast(new GlobalTrafficShapingHandler(ch.eventLoop().parent(), 10, 10)); // server

After the client receives the server message normally, it sends a uuid message to the server. After the server channelActive, it needs to send a message to the client, and after sending it, send a message to the client again. This requires Promise mode monitoring. It is sent after completion, and we need to do a rate monitoring on the server side to verify whether the rate control codec used above is true and effective.

2. Code

Client:

public class NettyClient {

	public static void main(String[] args) throws Exception {
		new NettyClient().bind("127.0.0.1", 7000);
	}

	public void bind(String address, int port) throws Exception {
		EventLoopGroup loopGroup = new NioEventLoopGroup();
		try {
			Bootstrap b = new Bootstrap();
			b.group(loopGroup).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true)
					.handler(new ChannelInitializer<SocketChannel>() {
						@Override
						protected void initChannel(SocketChannel ch) throws Exception {
							ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
							ch.pipeline().addLast(new ChannelTrafficShapingHandler(10, 10)); // 速率控制
							ch.pipeline().addLast(new StringDecoder(Charset.forName("UTF-8")));
							ch.pipeline().addLast(new StringEncoder(Charset.forName("UTF-8")));
							ch.pipeline().addLast(new MyClientHandler());
						}
					});
			ChannelFuture future = b.connect(address, port).sync();
			future.channel().closeFuture().sync();
		} finally {
			loopGroup.shutdownGracefully();
		}
	}

}
public class MyClientHandler extends  SimpleChannelInboundHandler<String>{

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
    	System.out.println("客户端接收消息:" + msg + ",长度:" + msg.length());
        ctx.write(UUID.randomUUID().toString() + "\r\n", ctx.voidPromise());
    }

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

Server:


public class NettyServer {

	public static void main(String[] args) {
		NettyServer server = new NettyServer();
		server.bind(7000);
	}

	public void bind(int port) {
		EventLoopGroup bossGroup = new NioEventLoopGroup(1);
		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
						protected void initChannel(SocketChannel ch) throws Exception {
							ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
							
							// 流量控制  速率 10 bytes/s 
							ch.pipeline().addLast(new GlobalTrafficShapingHandler(ch.eventLoop().parent(), 10, 10));

							ch.pipeline().addLast(new StringDecoder(Charset.forName("UTF-8")));
							ch.pipeline().addLast(new StringEncoder(Charset.forName("UTF-8")));
							ch.pipeline().addLast(new MyServerHandler());
						}
					});
			ChannelFuture future = b.bind(port).sync();
			System.out.println("server start now");
			future.channel().closeFuture().sync();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			bossGroup.shutdownGracefully();
			workerGroup.shutdownGracefully();
		}
	}

}
public class MyServerHandler extends SimpleChannelInboundHandler<String> {

	private Runnable task; // 监控任务

	private AtomicLong msgLeng = new AtomicLong(); // 消费消息长度

	private AtomicLong firstTime = new AtomicLong(System.currentTimeMillis()); // 记录上次时间
	
	/**handler添加时触发**/
	@Override
	public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
		task = () -> {
			while (true) {
				try {
					// 获取消息
					long length = msgLeng.getAndSet(0);
					if (0 == length)
						continue;

					// 计算时间
					long currentTime = System.currentTimeMillis();
					long time = currentTime - firstTime.get();
					firstTime.set(currentTime);

					// 计算速率
					System.out.println("数据发送速率(KB/S):" + length * 1000 / time);
					Thread.sleep(50);
				} catch (InterruptedException ignored) {
				}
			}
		};
		super.handlerAdded(ctx);
	}
	
	/**channel连接后发送数据,发送数据完成,promise触发监听,实际上调用是我们传入的回调函数,再次发送数据**/
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		sendData(ctx);
		new Thread(task).start();
	}

	public void sendData(ChannelHandlerContext ctx) {
		// 写数据时第二个参数是Promise,这样写完数据后,可由传入的promise设置setSuccess() or setFailfua
		ctx.write("111111111122222222223333333333\r\n",
				getChannelProgressivePromise(ctx, new Consumer<ChannelProgressiveFuture>() {
					@Override
					public void accept(ChannelProgressiveFuture channelProgressiveFuture) {
						if (ctx.channel().isWritable()) {
							sendData(ctx);
						}
					}
				}));
	}

	public ChannelProgressivePromise getChannelProgressivePromise(ChannelHandlerContext ctx,
			Consumer<ChannelProgressiveFuture> completedAction) {
		// 创建Promise
		ChannelProgressivePromise channelProgressivePromise = ctx.newProgressivePromise();
		// Promise创建监听
		channelProgressivePromise.addListener(new ChannelProgressiveFutureListener() {
			@Override
			public void operationProgressed(ChannelProgressiveFuture future, long progress, long total)
					throws Exception {
				System.out.println("服务端处理数量:" + progress);
				msgLeng.getAndSet(progress);
			}

			@Override
			public void operationComplete(ChannelProgressiveFuture future) throws Exception {
				if (future.isSuccess()) {
					System.out.println("服务端提醒:消息发送成功!");
					// action的accept方法会递归调用发送数据方法
					Optional.ofNullable(completedAction).ifPresent(action -> action.accept(future));
				}
			}
		});
		return channelProgressivePromise;
	}

	@Override
	protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
		System.out.println("NettyServer接收到消息:" + msg);
	}

}

The final execution effect, the printing rate is 10 byte/s, and our client does not monitor, but the bytes sent are uuid32. Finally, the speed of the server reading the client data is the same as the server writing the data Promise to complete the printing rhythm , It means that the verification of the rate decoder is correct.

 

end!

 

 

Guess you like

Origin blog.csdn.net/shuixiou1/article/details/115029458