1.ProtoBuf简介
Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。
protocol buffers 是一种语言无关、平台无关、可扩展的序列化结构数据的方法,它可用于(数据)通信协议、数据存储等。
Protocol Buffers 是一种灵活,高效,自动化机制的结构数据序列化方法-可类比 XML,但是比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单。
你可以定义数据的结构,然后使用特殊生成的源代码轻松的在各种数据流中使用各种语言进行编写和读取结构数据。你甚至可以更新数据结构,而不破坏由旧数据结构编译的已部署程序。
2.定义.proto文件(参考https://www.cnblogs.com/dkblog/archive/2012/03/27/2419010.html)
定义包:option java_package = "com.cc.netty.serialized.protobuf";
定义类名约定文件名称+Module:option java_outer_classname = "RequestModule";
option java_package = "com.cc.netty.serialized.protobuf";
option java_outer_classname = "RequestModule";
message Request {
required string id = 1;
required int32 sequence = 2;
required string name = 3;
repeated string favorite = 4;
}
option java_package = "com.cc.netty.serialized.protobuf";
option java_outer_classname = "ResponseModule";
message Response {
required string id = 1;
required int32 code = 2;
required string name = 3;
repeated string tags = 4;
}
option java_package = "com.cc.netty.serialized.protobuf";
option java_outer_classname = "UserModule";
message User {
required string userId = 1;
required int32 age = 2;
required string userName = 3;
repeated string favorite = 4;
}
message Group {
required int64 groupId = 1;
required string groupName = 2;
}
3.生成对应类
linux:https://blog.youkuaiyun.com/xiexievv/article/details/47396725;
windows平台下载protoc.exe,编写.bat运行;
4.序列化应用
package com.cc.netty.serialized.protobuf;
import com.cc.netty.serialized.protobuf.UserModule.User.Builder;
import com.google.protobuf.InvalidProtocolBufferException;
import java.util.Arrays;
import com.cc.netty.serialized.protobuf.UserModule.User;
public class UserSerial {
//将一个object对象序列化为byte[]数组
public static byte[] serialObject2Bytes() {
Builder userBuilder = UserModule.User.newBuilder();
//设置userBuilder参数
userBuilder
.setUserId("1001")
.setAge(28)
.setUserName("张三")
.addFavorite("足球")
.addFavorite("蓝球");
//通过userBuilder.build()构建user对象
User user = userBuilder.build();
/**
* 序列化机制:
* java序列化机制:比如一个int类型,在内存中占4个字节的长度,也就是32位。
*
* protobuf序列化机制:是按照实际的数据大小去进行动态伸缩的,因此我们没有真正占用到4个字节的int类型,
* protobuf会帮我们做序列化(节省实际空间)
*/
byte[] userBytes = user.toByteArray();
System.out.println(Arrays.toString(userBytes));
return userBytes;
}
//反序列化:将一个byte[]数组转换为Object对象
public static User serialBytes2Object(byte[] bytes) {
try {
return UserModule.User.parseFrom(bytes);
} catch (InvalidProtocolBufferException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
//serialObject2Bytes();
//输出结果:[10, 4, 49, 48, 48, 49, 16, 28, 26, 6, -27, -68, -96, -28, -72, -119, 34, 6, -24, -74, -77, -25, -112, -125, 34, 6, -24, -109, -99, -25, -112, -125]
byte[] userBytes = serialObject2Bytes();
User user = serialBytes2Object(userBytes);
System.out.println(
"userID:"+user.getUserId()+
",userAge:"+user.getAge()+
",userName:"+user.getUserName()+
",FavoriteList:"+user.getFavoriteList());
}
}
5.Netty整合Protobuf使用
package com.cc.netty.serialized.protobuf;
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;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
public class Server {
public void bind(int port) throws Exception {
// 配置服务端的NIO线程组
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))//使用netty的LoggingHandler打印日志
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) {
//在处理业务前加入Protobuf三个解码器和一个编码器
ch.pipeline().addLast(new ProtobufVarint32FrameDecoder());
ch.pipeline().addLast(new ProtobufDecoder(RequestModule.Request.getDefaultInstance()));
ch.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
ch.pipeline().addLast(new ProtobufEncoder());
ch.pipeline().addLast(new ServerHandler());//自定义业务逻辑处理器
}
});
// 绑定端口,同步等待成功
ChannelFuture f = b.bind(port).sync();
System.out.println("Server Start .. ");
// 等待服务端监听端口关闭
f.channel().closeFuture().sync();
} finally {
// 优雅退出,释放线程池资源
bossGroup.shutdownGracefully();
workerGroup.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 (NumberFormatException e) {
// 采用默认值
}
}
new Server().bind(port);
}
}
package com.cc.netty.serialized.protobuf;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
@Sharable
public class ServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
RequestModule.Request req = (RequestModule.Request) msg;
if ("Netty".equalsIgnoreCase(req.getName())) {
System.out.println("服务器端:" + "id : " + req.getId() + ", name : " +req.getName());
ctx.writeAndFlush(response(req.getId()));
}
}
//response方法构建Protobuf的ResponseModule对象
private ResponseModule.Response response(String id) {
ResponseModule.Response.Builder builder = ResponseModule.Response.newBuilder();
builder.setId(id);
builder.setCode(Integer.parseInt(id));
builder.setName("responseName"+ id);
return builder.build();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();// 发生异常,关闭链路
}
}
package com.cc.netty.serialized.protobuf;
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.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
public class Client {
public void connect(int port, String host) throws Exception {
// 配置客户端NIO线程组
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ProtobufVarint32FrameDecoder());
ch.pipeline().addLast(new ProtobufDecoder(ResponseModule.Response.getDefaultInstance()));
ch.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
ch.pipeline().addLast(new ProtobufEncoder());
ch.pipeline().addLast(new ClientHandler());
}
});
// 发起异步连接操作
ChannelFuture f = b.connect(host, port).sync();
System.out.println("Client Start .. ");
// 当代客户端链路关闭
f.channel().closeFuture().sync();
} finally {
// 优雅退出,释放NIO线程组
group.shutdownGracefully();
}
}
/**
* @param args
* @throws Exception
*/
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) {
// 采用默认值
}
}
new Client().connect(port, "127.0.0.1");
}
}
package com.cc.netty.serialized.protobuf;
import com.cc.netty.serialized.protobuf.ResponseModule.Response;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class ClientHandler extends ChannelInboundHandlerAdapter {
/**
* Creates a client-side handler.
*/
public ClientHandler() {
}
@Override
public void channelActive(ChannelHandlerContext ctx) {
System.out.println("激活...");
for (int i = 0; i < 100; i++) {
ctx.writeAndFlush(request(i));
}
// ctx.flush();
}
//request方法构建Protobuf的RequestModule对象
private RequestModule.Request request(int i) {
RequestModule.Request.Builder builder = RequestModule.Request.newBuilder();
builder.setId(i+"");
builder.setName("Netty");
builder.setSequence(i);
builder.addFavorite("足球");
builder.addFavorite("篮球");
return builder.build();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ResponseModule.Response resp = (Response) msg;
System.out.println("客户端:" + "id : " + resp.getId() + ", name : " +resp.getName());
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
启动server:
启动client: