Netty4.x用POJO代替ByteBuf案例

講解一下內容:

  1. 什麼是pojo;
  2. 爲什麼使用pojo代替ByteBuf;
  3. 使用pojo代替ByteBuf案例;

1、什麼是POJO

POJO(Plain Ordinary Java Object)即普通Java類,具有一部分getter/setter方法的那種類就可以稱作POJO。

實際意義就是普通的JavaBeans(簡單的實體類),特點就是支持業務邏輯的協助類
POJO類的作用是方便程序員使用數據庫中的數據表,對於程序員來說,可以很方便的將POJO類當作對象來進行使用,也可以方便的調用其get,set方法。
但不允許有業務方法,即不包含業務邏輯或持久邏輯等。

2、爲什麼使用pojo代替ByteBuf

在 ChannelHandler 使用 POIO 的好處很明顯:通過從ChannelHandler 中提取出 ByteBuf 的代碼,將會使 ChannelHandler的實現變得更加可維護和可重用

3、使用pojo代替ByteBuf案例

在 TIME 客戶端和服務器的例子中,我們讀取的僅僅是一個32位的整形數據爲本次案例講解。
首先,讓我們定義一個新的類型叫做 UnixTime
UnixTime.java

package com.moreday.netty.pojo;

import java.util.Date;

/**
 * @ClassName UnixTime
 * @Description TODO(這裏用一句話描述這個類的作用)
 * @author 尋找手藝人
 * @Date 2020年4月11日 上午9:47:22
 * @version 1.0.0
 */
public class UnixTime {
	private final long value;

	public UnixTime() {
		this(System.currentTimeMillis() / 1000L + 2208988800L);
	}

	/**
	 * @Description TODO(這裏用一句話描述這個方法的作用)
	 * @param l
	 */
	public UnixTime(long value) {
		this.value = value;
	}

	@Override
	public String toString() {
		return new Date((getValue() - 2208988800L) * 1000L).toString();
	}

	/**
	 * @return the value
	 */
	public long getValue() {
		return value;
	}

}

現在我們定義 TimeDecoder 類,返回一個 UnixTime,以替代ByteBuf

package com.moreday.netty.pojo;

import java.util.List;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;

/**
 * @ClassName TimeDecoder
 * @Description TODO(這裏用一句話描述這個類的作用)
 * @author 尋找手藝人
 * @Date 2020年4月11日 上午9:53:07
 * @version 1.0.0
 */
public class TimeDecoder extends ByteToMessageDecoder{

	/* (非 Javadoc)
	 * Description:
	 * @see io.netty.handler.codec.ByteToMessageDecoder#decode(io.netty.channel.ChannelHandlerContext, io.netty.buffer.ByteBuf, java.util.List)
	 */
	@Override
	protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
		// TODO Auto-generated method stub
		if(in.readableBytes()<4) {
			return;
		}
//		out.add(in.readBytes(4));
		out.add(new UnixTime(in.readUnsignedInt()));
	}

}

下面TimeClientHandler 不再任何的 ByteBuf 代碼了

package com.moreday.netty.pojo;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

/**
 * @ClassName TimeClientHandler
 * @Description TODO(客戶端處理器)
 * @author 尋找手藝人
 * @Date 2020年4月9日 下午12:52:23
 * @version 1.0.0
 */
public class TimeClientHandler extends ChannelInboundHandlerAdapter {

	  /* (非 Javadoc)
	 * Description:
	 * @see io.netty.channel.ChannelInboundHandlerAdapter#channelRead(io.netty.channel.ChannelHandlerContext, java.lang.Object)
	 */
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		// TODO Auto-generated method stub
//	    ByteBuf m = (ByteBuf) msg;
//	    try {
//	    	 long currentTimeMillis = (m.readUnsignedInt() - 2208988800L) * 1000L;
//             System.out.println(new Date(currentTimeMillis));
//             ctx.close();
//		} finally {
//			// TODO: handle finally clause
//			m.release();
//		}
		UnixTime m = (UnixTime) msg;
		System.out.println(m);
		 ctx.close();
	}
	/* (非 Javadoc)
	 * Description:
	 * @see io.netty.channel.ChannelInboundHandlerAdapter#exceptionCaught(io.netty.channel.ChannelHandlerContext, java.lang.Throwable)
	 */
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		// TODO Auto-generated method stub
		 cause.printStackTrace();
		 ctx.close();
	}
	
	
}

是不是變得更加簡單和優雅了?相同的技術可以被運用到服務端。讓我們修改一下 TimeServerHandler 的代碼

package com.moreday.netty.pojo;

import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

/**
 * 
 * @ClassName TimeServerHandler
 * @Description TODO(這裏用一句話描述這個類的作用)
 * @author 尋找手藝人
 * @Date 2020年4月9日 下午1:16:57
 * @version 1.0.0
 */

public class TimeServerHandler extends ChannelInboundHandlerAdapter {

	/*
	 * (非 Javadoc) Description:
	 * channelActive() 方法將會在連接被建立並且準備進行通信時被調用。
	 *      因此讓我們在這個方法裏完成一個代表當前時間的32位整數消息的構建工作。4個字節byte,一個字節8位,共計32bit
	 * @see
	 * io.netty.channel.ChannelInboundHandlerAdapter#channelActive(io.netty.channel.
	 * ChannelHandlerContext)
	 */
	@Override
	public void channelActive(final ChannelHandlerContext ctx) throws Exception {
		 
		final ChannelFuture f = ctx.writeAndFlush(new UnixTime());

		//寫操作已經完成他會通知他的監聽
		f.addListener(new ChannelFutureListener() {
           //這裏我們構建了一個匿名的 ChannelFutureListener 類用來在操作完成時關閉 Channel。
			//或者你可以使用簡單的預定義監聽器代碼: f.addListener(ChannelFutureListener.CLOSE);
			@Override
			public void operationComplete(ChannelFuture future) throws Exception {
				// TODO Auto-generated method stub
				assert f == future;
				ctx.close();
			}
		});
	}

	/*
	 * (非 Javadoc) Description:
	 * 
	 * @see io.netty.channel.ChannelInboundHandlerAdapter#exceptionCaught(io.netty.
	 * channel.ChannelHandlerContext, java.lang.Throwable)
	 */
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		// TODO Auto-generated method stub
		cause.printStackTrace();
		ctx.close();
	}

}

現在,唯一缺少的功能是一個編碼器,是ChannelOutboundHandler的實現,用來將 UnixTime 對象重新轉化爲一個 ByteBuf。這是比編寫一個解碼器簡單得多,因爲沒有需要處理的數據包編碼消息時拆分和組裝。

package com.moreday.netty.pojo;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;

/**
 * @ClassName TimeEncoder
 * @Description TODO(這裏用一句話描述這個類的作用)
 * @author xiayutian
 * @Date 2020年4月11日 上午10:15:09
 * @version 1.0.0
 */
public class TimeEncoder extends ChannelOutboundHandlerAdapter {
  /* (非 Javadoc)
 * Description:
 * @see io.netty.channel.ChannelOutboundHandlerAdapter#write(io.netty.channel.ChannelHandlerContext, java.lang.Object, io.netty.channel.ChannelPromise)
 */
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
	// TODO Auto-generated method stub
	UnixTime m = (UnixTime) msg;
	ByteBuf encode = ctx.alloc().buffer(4);
	encode.writeInt((int)m.getValue());
	//通過 ChannelPromise,當編碼後的數據被寫到了通道上 Netty 可以通過這個對象標記是成功還是失敗。
	ctx.write(encode,promise);
}	
}

最後的任務就是

在TimeClientHandler之前把TimeDecoder插入到ChannelPipeline;

 package com.moreday.netty.pojo;

import io.netty.bootstrap.Bootstrap;
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.nio.NioSocketChannel;

/**
 * @ClassName TimeClient
 * @Description TODO(時間客戶端)
 * @author 尋找手藝人
 * @Date 2020年4月9日 下午1:35:18
 * @version 1.0.0
 */
public class TimeClient {

	
	public void connect(int port,String host) throws Exception {
		
		//配置客戶端NIO線程
		EventLoopGroup group = new NioEventLoopGroup();
		try {
			//BootStrap 和 ServerBootstrap 類似,不過他是對非服務端的 channel 而言
			//,比如客戶端或者無連接傳輸模式的 channel。
			Bootstrap b = new Bootstrap();
			//NioSocketChannel這個類在客戶端channel 被創建時使用
			b.group(group).channel(NioSocketChannel.class)
			.option(ChannelOption.TCP_NODELAY,true)
			.handler(new ChannelInitializer<NioSocketChannel>() {

				@Override
				protected void initChannel(NioSocketChannel ch) throws Exception {
					// TODO Auto-generated method stub
					ch.pipeline().addLast(new TimeDecoder(),new TimeClientHandler());
				}
			});
			//發起異步鏈接操作,啓動客戶端
			//我們用 connect() 方法代替了 bind() 方法
			ChannelFuture f = b.connect(host, port).sync();
			
			//等待客戶端鏈路關閉
			f.channel().closeFuture().sync();
		} finally {
			//優雅退出
			group.shutdownGracefully();
		}
	}
	
	public static void main(String[] args) throws Exception {
		int port = 8080;
		if(args!=null && args.length>0) {
			try {
			   port = Integer.valueOf(args[0]);
				
			} catch (Exception e) {
				// TODO: handle exception
			}
		}
		new TimeClient().connect(port, "127.0.0.1");
	}
}

在 TimeServerHandler 之前把 TimeEncoder 插入到ChannelPipeline;

package com.moreday.netty.pojo;

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;

 
/**
 * @ClassName TimeServer
 * @Description TODO(時間服務器服務端)
 * @author 尋找手藝人
 * @Date 2020年4月9日 下午1:36:04
 * @version 1.0.0
 */
public class TimeServer {
    public void bind(int port) throws Exception{
    	//配置服務端線程組
    	EventLoopGroup parentGroup = new NioEventLoopGroup();
    	EventLoopGroup childGroup = new NioEventLoopGroup();
    	
    	try {
    		ServerBootstrap b = new ServerBootstrap();
        	b.group(parentGroup, childGroup)
        	.channel(NioServerSocketChannel.class)
        	.option(ChannelOption.SO_BACKLOG,1024)
        	.childHandler(new ChildChannelHandler());
        	
        	
        	//綁定端口,同步等待成功
        	ChannelFuture future = b.bind(port).sync();
        	//等待服務端監聽端口關閉
        	future.channel().closeFuture().sync();	
		} finally {
			// TODO: handle finally clause
			//優雅退出,釋放線程資源
			parentGroup.shutdownGracefully();
			childGroup.shutdownGracefully();
		}
    }
    private class ChildChannelHandler extends ChannelInitializer<SocketChannel>{

		@Override
		protected void initChannel(SocketChannel ch) throws Exception {
			// TODO Auto-generated method stub
			ch.pipeline().addLast(new TimeEncoder(),new TimeServerHandler());
		}
    	
    }
    public static void main(String[] args) throws Exception {
		int port = 8080;
			if(args!=null&&args.length>0) {
				try {
				port = Integer.valueOf(args[0]);
				} catch (NumberFormatException e) {
					// TODO: handle exception
				}
			}
			new TimeServer().bind(port);
	}
}

到此,用POJO代替ByteBuf案例以講解完成。
下篇博文我們介紹用Netty與Google protocol buffer整合案例(Protocol入門到開發),大家加油!

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