1.Google Protobuf是什么?
Protobuf .是Google的语言无关,平台无关的可扩展机制,用于序列化结构化数据, 相比与 xml 等传统的序列化工具, 它更小、更快、更简单。
github地址:protobuf源码地址
优点:
- 在谷歌内部长期使用, 产品成熟度高
- 跨语言、支持多种语言, 包括 C++、Java 和 Python
- 编码后的消息更小, 更加有利于存储和传输
- 编解码的性能非常高
- 支持不同协议版本的前向兼容
- 支持定义可选和必选字段
2.ProtoBuf的使用
2.1 安装
protobuf的安装编译器使用的是c++,如果安装运行环境本身使用c++,可以直接按照c++的方式进行源码的编译和安装。如果不是c++环境,建议直接下载编译好的二进制文件。具体方法参看github地址:https://github.com/protocolbuffers/protobuf
2.2 结合Netty使用
Netty作为网络传输框架,结合protobuf序列化技术实现客户端和服务器的信息通信。
客户端使用protobuf将对象序列化成字节码,而服务器端通过protobuf将对象反序列化成原本对象。
2.2.1 定义.proto文件
syntax ="proto3";
option optimize_for = SPEED;
option java_package = "com.hosea.protobuf.nettydemo";
option java_outer_classname="DataInfo";
message RequestUser{
optional string studentNo= 1;
optional string password = 2;
}
message ResponseClass{
optional string classNo= 1;
}
2.2.2 使用protoc进行编译
使用Protobuf编译器进行编译,生成DataInfo对象,将生成的类代码文件拷贝到相应工程中。
2.2.3 添加maven依赖
添加protobuf-java包依赖
<!--
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.13.0</version>
</dependency>
2.2.4 客户端编码
public class ProtoClient {
public static void main(String[] args) throws Exception{
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
try{
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class)
.handler(new ProtoClientInitializer());
ChannelFuture channelFuture = bootstrap.connect("localhost",8899).sync();
channelFuture.channel().closeFuture().sync();
}finally {
eventLoopGroup.shutdownGracefully();
}
}
}
public class ProtoClientInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//解码器,通过Google Protocol Buffers序列化框架动态的切割接收到的ByteBuf
pipeline.addLast(new ProtobufVarint32FrameDecoder());
//将接收到的二进制文件解码成具体的实例,这边接收到的是服务端的ResponseClass对象实列
pipeline.addLast(new ProtobufDecoder(DataInfo.ResponseClass.getDefaultInstance()));
//Google Protocol Buffers编码器
pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
//Google Protocol Buffers编码器
pipeline.addLast(new ProtobufEncoder());
pipeline.addLast(new ProtoClientHandler());
}
}
public class ProtoClientHandler extends SimpleChannelInboundHandler<DataInfo.ResponseBank> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, DataInfo.ResponseClass msg) throws Exception {
System.out.println(msg.getClassNo());
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
DataInfo.RequestUser user = DataInfo.RequestUser.newBuilder() .setStudentNo("12345").setPassword("123456").build();
ctx.channel().writeAndFlush(user);
}
}
2.2.5 服务端编码
public class ProtoServer {
public static void main(String[] args) throws Exception{
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup wokerGroup = new NioEventLoopGroup();
try{
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup,wokerGroup).channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ProtoServerInitializer());
ChannelFuture channelFuture = serverBootstrap.bind(8899).sync();
channelFuture.channel().closeFuture().sync();
}finally {
bossGroup.shutdownGracefully();
wokerGroup.shutdownGracefully();
}
}
}
public class ProtoServerInitializer extends ChannelInitializer<SocketChannel>{
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//解码器,通过Google Protocol Buffers序列化框架动态的切割接收到的ByteBuf
pipeline.addLast(new ProtobufVarint32FrameDecoder());
//服务器端接收的是客户端RequestUser对象,所以这边将接收对象进行解码生产实列
pipeline.addLast(new ProtobufDecoder(DataInfo.RequestUser.getDefaultInstance()));
//Google Protocol Buffers编码器
pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
//Google Protocol Buffers编码器
pipeline.addLast(new ProtobufEncoder());
pipeline.addLast(new ProtoServerHandler());
}
}
public class ProtoServerHandler extends SimpleChannelInboundHandler<DataInfo.RequestUser> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, DataInfo.RequestUser msg) throws Exception {
System.out.println(msg.getStudentNo());
System.out.println(msg.getPassword());
DataInfo.ResponseClass class = DataInfo.ResponseClass.newBuilder().setClassNo("001701");
ctx.channel().writeAndFlush(bank);
}
}
3.总结
其实大多数RPC框架底层实现都是使用序列化框架和NIO通信框架进行结合。