- 底层都基于 JAVA 序列化,从 JDK 1.1 版本就提供,只需实现 java.io.Serializable并生成序列ID即可。
- JAVA序列化的目的,网络传输及持久化。
- Java序列化从JDK 1.1版本就己经提供,它不需要添加额外的类库,只需实现 java.io.Serializable并生成序列ID即可,但是在远程服务调用(RPC)时,很少宜接使用Java序列化进行消息的编解码和传输, 这又是什么原因呢?
- 无法跨语言,编码后其他语言无法正常解码
- 编码后的码流太大
- 性能差
MessagePack是一个高效的二进制序列化框架,它像JSON 一样支持不同语言间的数 据交换,但是它的性能更快,序列化之后的码流也更小。
编解码高效,性能高;
序列化之后的码流小;
支持跨语言。
Google的Protobuf在业界非常流行,很多商业项目选择Protobuf作为编解码框架。
在谷歌内部长期使用,产品成熟度髙:
跨语言、支持多种语言,包括C++、Java和Python:
编码后的消息更小,更加冇利于存储和传输:
编解码的性能非常高:
支持不同协议版本的前向兼容:
支持定义可选和必选字段。
JBoss Marshalling是一个Java对象序列化包,对JDK默认的序列化框架进行了优化, 但又保持跟java.io.Serializablc接口的兼容,同时増加了 -些可调的参数和附加的特性, 这些参数和特性可通过工厂类进行配置。
客户端产生业务数据,经过 ProtobufEncoder 编码器编码,客户端接受后通过 ProtobufDecoder 解码。
//版本
syntax = "proto3";
// 生成的外部类型名称
option java_outer_classname = "StudentPOJO";
// protobuf 使用 message 管理数据
message Student{ // 会在 StudentPOJO 外部类生成内部类, 真正发送的 pojo 对象
// 1 标识属性序号,不代表值
int32 id = 1;
string name = 2;
}
// https://developers.google.com/protocol-buffers/docs/proto 文档
google protobuf 会根据此文件生成对应的 java类,此内部类对相应 message 定义的内容。
StudentPOJO 需要通过 protoc.exe 生成,protoc.exe --java_out=. Student.proto
public final class StudentPOJO {
private StudentPOJO() {
}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistryLite registry) {
}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistry registry) {
registerAllExtensions(
(com.google.protobuf.ExtensionRegistryLite) registry);
}
public interface StudentOrBuilder extends
// @@protoc_insertion_point(interface_extends:Student)
com.google.protobuf.MessageOrBuilder {
/**
*
* 1 标识属性序号,不代表值
*
*
* int32 id = 1;
*/
int getId();
/**
* string name = 2;
*/
java.lang.String getName();
/**
* string name = 2;
*/
com.google.protobuf.ByteString
getNameBytes();
}
public static final class Student extends com.google.protobuf.GeneratedMessageV3 implements StudentOrBuilder {
private static final long serialVersionUID = 0L;
// Use Student.newBuilder() to construct.
private Student(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
super(builder);
}
private Student() {
id_ = 0;
name_ = "";
}
...
}
}
package cn.painnote.netty.codec.protocolBuf;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
public class NettyServer {
/**
* Description netty 服务端
*
* @author Qian YuHua
*/
public static void main(String[] args) throws Exception {
// Boss group 处理链接请求 NioEventLoopGroup 默认构造函数线程数为 0,CPU * 2
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
// Worker group 处理数据客户端业务
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// 服务器端启动对象
ServerBootstrap bootstrap = new ServerBootstrap();
// 设置线程组
bootstrap.group(bossGroup, workerGroup)
// 使用 NioServerSocketChannel 作为服务器通道实现类
.channel(NioServerSocketChannel.class)
/*
* ChannelOption.SO_BACKLOG对应的是tcp/ip协议listen函数中的backlog参数,
* 函数listen(int socketfd,int backlog)用来初始化服务端可连接队列,
* 服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接,
* 多个客户端来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理,
* backlog参数指定了队列的大小
*/
.option(ChannelOption.SO_BACKLOG, 128)
.childHandler(new ChannelInitializer<SocketChannel>() {//创建一个通道初始化对象(匿名对象)
//给pipeline 设置处理器
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// 制定对哪种对象进行解码
ch.pipeline().addLast("decoder", new ProtobufDecoder(StudentPOJO.Student.getDefaultInstance()));
ch.pipeline().addLast(new NettyServerHandler());
}
}); // 给我们的workerGroup 的 EventLoop 对应的管道设置处理器
System.out.println(".....server is ready...");
//绑定一个端口并且同步, 生成了一个 ChannelFuture 对象
//启动服务器(并绑定端口)
ChannelFuture channelFuture = bootstrap.bind(6666).sync();
//对关闭通道进行监听
channelFuture.channel().closeFuture().sync();
} catch (Exception e) {
System.out.println(e);
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
package cn.painnote.netty.codec.protocolBuf;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
import java.util.concurrent.TimeUnit;
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
StudentPOJO.Student student = (StudentPOJO.Student) msg;
System.out.println("客户端发送消息是:" + " id " + student.getId() + " name " + student.getName());
}
}
2.2.1 客户端及处理器
package cn.painnote.netty.codec.protocolBuf;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
public class NettyClient {
public static void main(String[] args) throws Exception {
NioEventLoopGroup eventExecutors = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventExecutors)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// 加入 protobufEncoder
ch.pipeline().addLast("encoder", new ProtobufEncoder());
ch.pipeline().addLast(new NettyClientHandler()); //加入自己的处理器
}
});
System.out.println("client ok..");
ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6666).sync();
//给关闭通道进行监听
channelFuture.channel().closeFuture().sync();
}
}
package cn.painnote.netty.codec.protocolBuf;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
/**
*
* Title:
* Description: Function Description
* @author qyh
*/
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("client " + ctx);
// 发送 Student 到服务器
StudentPOJO.Student student = StudentPOJO.Student.newBuilder().setId(111).setName("钱育华").build();
ctx.writeAndFlush(student);
}
}