輕量級RPC框架-NIO&Netty

RPC(Remote Procedure Call Protocol)——遠程過程調用協議,它是一種通過網絡從遠程計算機程序上請求服務,而不需要了解底層網絡技術的協議。

NIO與IO區別:
IO流程:
在這裏插入圖片描述

  1. 先將文件內容從磁盤中拷貝到操作系統buffer
  2. 再從操作系統buffer拷貝到程序應用buffer
  3. 從程序buffer拷貝到socket buffer
  4. 從socket buffer拷貝到協議引擎.

NIO流程:

NIO技術省去了將操作系統的read buffer拷貝到程序的buffer, 以及從程序buffer拷貝到socket buffer的步驟, 直接將 read buffer 拷貝到 socket buffer. java 的 FileChannel.transferTo() 方法就是這樣的實現, 這個實現是依賴於操作系統底層的sendFile()實現。

總結:NIO支持更大的併發量,適合服務端編程。

Netty:
是比較流行的nio框架,用來代替socketServer,以下是netty發送字符串例子。

客戶端:EchoClient

 /**
  * • 連接服務器 • 寫數據到服務器 • 等待接受服務器返回相同的數據 • 關閉連接
  * 
  */
public class EchoClient {

	private final String host;
	private final int port;

	public EchoClient(String host, int port) {
		this.host = host;
		this.port = port;
	}

	public void start() throws Exception {
		EventLoopGroup nioEventLoopGroup = null;
		try {
			// 客戶端引導類
			Bootstrap bootstrap = new Bootstrap();
			// EventLoopGroup可以理解爲是一個線程池,這個線程池用來處理連接、接受數據、發送數據
			nioEventLoopGroup = new NioEventLoopGroup();
			bootstrap.group(nioEventLoopGroup)//多線程處理
					.channel(NioSocketChannel.class)//指定通道類型爲NioServerSocketChannel,一種異步模式,OIO阻塞模式爲OioServerSocketChannel
					.remoteAddress(new InetSocketAddress(host, port))//地址
					.handler(new ChannelInitializer<SocketChannel>() {//業務處理類
								@Override
								protected void initChannel(SocketChannel ch)
										throws Exception {
									ch.pipeline().addLast(new EchoClientHandler());//註冊handler
								}
							});
			// 鏈接服務器
			ChannelFuture channelFuture = bootstrap.connect().sync();
			channelFuture.channel().closeFuture().sync();
		} finally {
			nioEventLoopGroup.shutdownGracefully().sync();
		}
	}

	public static void main(String[] args) throws Exception {
		new EchoClient("localhost", 20000).start();
	}
}

EchoClientHandler

 public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
	// 客戶端連接服務器後被調用
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		System.out.println("客戶端連接服務器,開始發送數據……");
		byte[] req = "QUERY TIME ORDER".getBytes();//消息
		ByteBuf firstMessage = Unpooled.buffer(req.length);//發送類
		firstMessage.writeBytes(req);//發送
		ctx.writeAndFlush(firstMessage);//flush
	}

	// • 從服務器接收到數據後調用
	@Override
	protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg)
			throws Exception {
		System.out.println("client 讀取server數據..");
		// 服務端返回消息後
		ByteBuf buf = (ByteBuf) msg;
		byte[] req = new byte[buf.readableBytes()];
		buf.readBytes(req);
		String body = new String(req, "UTF-8");
		System.out.println("服務端數據爲 :" + body);
	}

	// • 發生異常時被調用
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
			throws Exception {
		System.out.println("client exceptionCaught..");
		// 釋放資源
		ctx.close();
	}
}

服務端:EchoServer

  /**
   * •配置服務器功能,如線程、端口 • 實現服務器處理程序,它包含業務邏輯,決定當有一個請求連接或接收數據時該做什麼
   * 
   */
public class EchoServer {

	private final int port;

	public EchoServer(int port) {
		this.port = port;
	}

	public void start() throws Exception {
		EventLoopGroup eventLoopGroup = null;
		try {
			//server端引導類
			ServerBootstrap serverBootstrap = new ServerBootstrap();
			//連接池處理數據
			eventLoopGroup = new NioEventLoopGroup();
			//裝配bootstrap
			serverBootstrap.group(eventLoopGroup)
			.channel(NioServerSocketChannel.class)//指定通道類型爲NioServerSocketChannel,一種異步模式,OIO阻塞模式爲OioServerSocketChannel
			.localAddress("localhost",port)//設置InetSocketAddress讓服務器監聽某個端口已等待客戶端連接。
			.childHandler(new ChannelInitializer<Channel>() {//設置childHandler執行所有的連接請求
				@Override
				protected void initChannel(Channel ch) throws Exception {
					ch.pipeline().addLast(new EchoServerHandler());//註冊handler
				}
					});
			// 最後綁定服務器等待直到綁定完成,調用sync()方法會阻塞直到服務器完成綁定,然後服務器等待通道關閉,因爲使用sync(),所以關閉操作也會被阻塞。
			ChannelFuture channelFuture = serverBootstrap.bind().sync();
			System.out.println("開始監聽,端口爲:" + channelFuture.channel().localAddress());
			channelFuture.channel().closeFuture().sync();
		} finally {
			eventLoopGroup.shutdownGracefully().sync();
		}
	}

	public static void main(String[] args) throws Exception {
		new EchoServer(20000).start();
	}
}

EchoServerHandler:

public class EchoServerHandler extends ChannelInboundHandlerAdapter {

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg)
			throws Exception {
		System.out.println("server 讀取數據……");
		//讀取數據
        ByteBuf buf = (ByteBuf) msg;
        byte[] req = new byte[buf.readableBytes()];
        buf.readBytes(req);
        String body = new String(req, "UTF-8");
        System.out.println("接收客戶端數據:" + body);
        //向客戶端寫數據
        System.out.println("server向client發送數據");
        String currentTime = new Date(System.currentTimeMillis()).toString();
        ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());
        ctx.write(resp);
        
	}

	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
		System.out.println("server 讀取數據完畢..");
        ctx.flush();//刷新後纔將數據發出到SocketChannel
	}

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

}

二、自定義RPC框架

代碼略,代碼較多,還沒研究明白。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章