java序列化缺点
1. 无法跨语言
目前几乎所有流行的java RPC通信框架都没有使用java序列化作为编解码框架。原因就在于无法跨语言。
2. 序列化后的码流太大
MessagePack
编码高效,性能高;
反序列化后码流小;
支持跨语言。
需要依赖的jar
<dependency>
<groupId>org.msgpack</groupId>
<artifactId>msgpack</artifactId>
<version>0.6.12</version>
</dependency>
小坑:书上么有明确指出序列化对象要使用注解@Message标识,导致服务端总是收不到数据,坑的一批。
@Message
public class UserInfo
利用Netty的半包编码和解码器
LengthFieldPrepender 和 LengthFieldBaseFrameDecoder , 轻松解决TCP粘包和半包问题
.childHandler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast("frameDecoder", new LengthFieldBasedFrameDecoder(65535, 0, 2, 0, 2));
socketChannel.pipeline().addLast("msgpack decoder", new MsgpackDecoder());
socketChannel.pipeline().addLast("frameEncoder", new LengthFieldPrepender(2));
socketChannel.pipeline().addLast("msgpack encoder", new MsgpackEncoder());
socketChannel.pipeline().addLast(new EchoServerMsgHandler());
}
}
(这里半包或粘包会导致数据少读)
自定义编码器
public class MsgpackEncoder extends MessageToByteEncoder<Object> {
@Override
protected void encode(ChannelHandlerContext channelHandlerContext, Object o, ByteBuf byteBuf) throws Exception {
MessagePack msgpack = new MessagePack();
byte[] raw = msgpack.write(o);
byteBuf.writeBytes(raw);
}
}
自定义解码器
public class MsgpackDecoder extends MessageToMessageDecoder<ByteBuf> {
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
final byte[] array;
final int length = byteBuf.readableBytes();
array = new byte[length];
byteBuf.getBytes(byteBuf.readerIndex(), array, 0, length);
MessagePack msgpack = new MessagePack();
list.add(msgpack.read(array));
}
}
Server
public class EchoMsgServer {
public void bind(int port){
EventLoopGroup bossGroup = new NioEventLoopGroup();
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>() {