Netty(1) 编程模型

137 阅读3分钟

[TOC]

Netty-异步和事件驱动

核心组件

1. Channel, 写入设备的抽象,类似于文件描述符

2. 回调

a. 一个回调其实就是一个方法,一个指向 已经被提供给另外一个方法的引用。这使得后者可以在适当的时候调用前者。

3. Future

  1. 提供了另一种操作完成时,通知应用程序的方式。这个对象可以看作是一个异步操作的结果的占位符;它将在未来的某个时刻完成,并提供对其结果的访问。
  2. jdk预置了interface java.util.concurrent.Future,但其所提供的实现,只允许手动检查对应的操作是否已经完成,或者一直阻塞直到完成。
  3. ChannelFuture 提供了几种额外的方法,这些方法使得我们能够注册一个或者多个操作完成时被调用。由ChannelFutureListener提供的通知机制消除了手动检查对应操作是否完成的必要。

4. 事件和ChannelHandler

Server.java

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class Server {
	public static void main(String[] args) throws InterruptedException {
		//1. 创建两个线程组: 一个用于进行网络连接接受的 另一个用于我们的实际处理(网络通信的读写)
		EventLoopGroup bossGroup = new NioEventLoopGroup();
		EventLoopGroup workGroup = new NioEventLoopGroup();
		
		//2 通过辅助类去构造server/client
		ServerBootstrap b = new ServerBootstrap();
		b.group(bossGroup, workGroup)
		 .channel(NioServerSocketChannel.class)
		 .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
		 .option(ChannelOption.SO_BACKLOG, 1024)
		 .childOption(ChannelOption.TCP_NODELAY, true)
		 .childOption(ChannelOption.SO_RCVBUF, 1024 * 32)
		 .childOption(ChannelOption.SO_SNDBUF, 1024 * 32)
		 .childHandler(new ChannelInitializer<SocketChannel>() {
			@Override
			protected void initChannel(SocketChannel ch) throws Exception {
				ch.pipeline().addLast(new ServerHandler());	//1
			}
		});
		//服务器端绑定端口并启动服务
		ChannelFuture cf = b.bind(8765).sync();
		//使用channel级别的监听close端口 阻塞的方式
		cf.channel().closeFuture().sync();
		
		bossGroup.shutdownGracefully();
		workGroup.shutdownGracefully();
	}
}

ServerHandler.java

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class ServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
    	System.err.println("server channel active..");
    }
    
    /**
     * 真正的数据最终会走到这个方法进行处理
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        
    	//读取客户端的数据
    	ByteBuf buf = (ByteBuf) msg;
        byte[] request = new byte[buf.readableBytes()];
        buf.readBytes(request);
        String requestBody = new String(request, "utf-8");
        System.err.println("Server: " + requestBody);
        
        //返回响应数据
        String responseBody = "返回响应数据" + requestBody;
        ctx.writeAndFlush(Unpooled.copiedBuffer(responseBody.getBytes()));

    }

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

Client.java

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

public class Client {
	public static void main(String[] args) throws InterruptedException {
		//1. 创建两个线程组: 只需要一个线程组用于我们的实际处理(网络通信的读写)
		EventLoopGroup workGroup = new NioEventLoopGroup();
		
		//2 通过辅助类去构造server/client
		Bootstrap b = new Bootstrap();
		b.group(workGroup)
		 .channel(NioSocketChannel.class)
		 .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
		 .option(ChannelOption.SO_RCVBUF, 1024 * 32)
		 .option(ChannelOption.SO_SNDBUF, 1024 * 32)
		 .handler(new ChannelInitializer<SocketChannel>() {
			@Override
			protected void initChannel(SocketChannel ch) throws Exception {
				ch.pipeline().addLast(new ClientHandler());	//1
			}
		});
		//服务器端绑定端口并启动服务
		ChannelFuture cf = b.connect("127.0.0.1", 8765).syncUninterruptibly();
		//使用channel级别的监听close端口 阻塞的方式
		cf.channel().writeAndFlush(Unpooled.copiedBuffer("hello netty!".getBytes()));
		
		Thread.sleep(1000);
		
		cf.channel().writeAndFlush(Unpooled.copiedBuffer("hello netty!".getBytes()));
		
		cf.channel().closeFuture().sync();
		workGroup.shutdownGracefully();
	}
}

ClientHandler.java

import com.bfxy.netty.serialized.marshalling.Response;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;

public class ClientHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
    	System.err.println("client channel active..");
    }
    /**
     * 真正的数据最终会走到这个方法进行处理
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    	try {
			Response resp = (Response)msg;
			System.err.println("Client: " + resp.getId() + "," + resp.getName() + "," + resp.getResponseMessage());
		} finally {
			ReferenceCountUtil.release(msg);
		}
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.fireExceptionCaught(cause);
    }
}