1.介绍
MessagePack是一种有效的二进制序列化格式。它使您可以在多种语言(如JSON)之间交换数据。但是它更快,更小。小整数被编码为一个字节,典型的短字符串除字符串本身外仅需要一个额外的字节。它的数据格式与json类似,但是在存储时对数字、多字节字符、数组等都做了很多优化,减少了无用的字符,二进制格式,也保证不用字符化带来额外的存储空间的增加
2.案例
<dependency>
<groupId>org.msgpack</groupId>
<artifactId>msgpack</artifactId>
<version>0.6.12</version>
</dependency>
public static void main(String[] args) throws Exception {
List<String> list = new ArrayList<>();
list.add("messagepack");
list.add("viver");
// 序列化
MessagePack messagePack = new MessagePack();
byte[] write = messagePack.write(list);
// 反序列化
List<String> stringList = messagePack.read(write, Templates.tList(Templates.TString));
System.out.println(stringList.get(0));
System.out.println(stringList.get(1));
}
3.编解码器开发
/**
* 编码
*
* @author liulei
**/
public class MsgPackEncoder extends MessageToByteEncoder<Object> {
@Override
protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
MessagePack msgpack = new MessagePack();
byte[] bytes = msgpack.write(msg);
out.writeBytes(bytes);
}
}
/**
* 解码
*
* @author liulei
**/
public class MsgpackDecoder extends MessageToMessageDecoder<ByteBuf> {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
int length = msg.readableBytes();
byte[] array = new byte[length];
// 数据报 msg 中获取需要解码的 byte 数组
msg.getBytes(msg.readerIndex(), array, 0 , length);
MessagePack msgpack = new MessagePack();
// 调用 MessagePack 的 read 方法将其反序列化为 Objcet 对象
out.add(msgpack.read(array));
}
}
/**
* 创建服务启动类
*
* @author liulei
*/
@Slf4j
public class EchoServerRun {
public static final int port = 8888;
public static void main(String[] args) {
EchoServerRun echoServerRun = new EchoServerRun();
echoServerRun.run();
}
public void run(){
final EChoServerHandler eChoServerHandler = new EChoServerHandler();
// 创建线程池
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
// 创建服务启动类
ServerBootstrap serverBootstrap = new ServerBootstrap();
// 给启动引导类进行配置
serverBootstrap.group(eventLoopGroup).channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new LengthFieldBasedFrameDecoder(
65535, 0, 4, 0, 4));
pipeline.addLast(new LengthFieldPrepender(4));
pipeline.addLast( new MsgpackDecoder());
pipeline.addLast( new MsgPackEncoder());
pipeline.addLast(eChoServerHandler);
}
});
try {
// 端口绑定
ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
channelFuture.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
}
finally {
// 优雅关闭
eventLoopGroup.shutdownGracefully();
}
}
}
/**
* 服务handler
*
* @author liulei
*/
@Slf4j
@ChannelHandler.Sharable
public class EChoServerHandler extends ChannelInboundHandlerAdapter {
public EChoServerHandler() {
super();
log.info("EChoServerHandler方法执行");
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
super.channelRegistered(ctx);
log.info("channelRegistered方法执行");
}
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
super.channelUnregistered(ctx);
log.info("channelUnregistered方法执行");
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
log.info("channelActive方法执行");
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
super.channelInactive(ctx);
log.info("channelInactive方法执行");
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("服务端接受到消息:"+msg);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
super.channelReadComplete(ctx);
log.info("channelReadComplete方法执行");
// 数据读完后回调用此方法
// 读取完后关闭
//ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
super.userEventTriggered(ctx, evt);
log.info("userEventTriggered方法执行");
}
@Override
public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
super.channelWritabilityChanged(ctx);
log.info("channelWritabilityChanged方法执行");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
log.info(cause.getMessage());
ctx.close();
}
@Override
public boolean isSharable() {
log.info("isSharable方法执行");
return super.isSharable();
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
super.handlerAdded(ctx);
log.info("handlerAdded方法执行");
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
super.handlerRemoved(ctx);
log.info("handlerRemoved方法执行");
}
}
@Message
@Data
public class UserInfo{
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
/**
* @author liulei
**/
@Slf4j
public class EChoClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
super.channelRegistered(ctx);
}
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
super.channelUnregistered(ctx);
}
/**
* 连接通道建立
*
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
for (int i = 0; i < 100; i++) {
UserInfo userInfo = new UserInfo();
userInfo.setAge(i);
userInfo.setName("hh"+i);
ctx.writeAndFlush(userInfo);
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
super.channelInactive(ctx);
}
/**
* 数据连接被建立
*
* @param ctx
* @param msg
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
super.channelReadComplete(ctx);
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
super.userEventTriggered(ctx, evt);
}
@Override
public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
super.channelWritabilityChanged(ctx);
}
/**
* 捕获一个异常时调用
*
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
log.info(cause.getMessage());
ctx.close();
}
}
/**
* @author liulei
**/
public class EChoClientRun {
public static void main(String[] args) {
EChoClientRun eChoClientRun = new EChoClientRun();
eChoClientRun.run();
}
public void run() {
final EChoClientHandler eChoServerHandler = new EChoClientHandler();
// 创建线程池
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
// 启动引导类
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
// 对包长度对接收到的数据进行解码
pipeline.addLast(new LengthFieldBasedFrameDecoder(
65535, 0, 4, 0, 4));
// LengthFieldPrepender 添加4个消息长度的字段
pipeline.addLast(new LengthFieldPrepender(4));
pipeline.addLast(new MsgpackDecoder());
pipeline.addLast(new MsgPackEncoder());
pipeline.addLast(eChoServerHandler);
}
});
//绑定一个端口,返回未来的通道 .bind() --> udp
try {
ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 8888).sync();
Channel channel = channelFuture.channel();
//channel.writeAndFlush(Unpooled.copiedBuffer("123", CharsetUtil.UTF_8));
// 当channel被关闭的时候会通知此处关闭chanel(closeFuture方法)
channelFuture.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
eventLoopGroup.shutdownGracefully();
}
}
}