Google Protobuf 编解码入门小例子以及netty 实例

Google 的 Protobuf 在业界非常流行,是很多商业项目的首选,下面是它的主要优点:

  1. 在谷歌内部长期使用,产品成熟度高
  2. 支持多种语言
  3. 编码后的消息更小,更加有利于存储和传输
  4. 编解码的性能非常高
  5. 支持不同协议版本的向前兼容
  6. 支持定义可选和必选字段

Protobuf 入门

Protobuf 是一个灵活、高效、结构化的数据序列化框架,相比于 XML 等传统的序列化工具,它更小、更快、更简单。

目前已经正在使用的有超过 48,162 种报文格式定义和超过 12,183 个 .proto 文件。他们用于 RPC 系统和持续数据存储系统。

环境准备

1. 安装Protobuf

win包&linux包百度云分享》》》

## windows 下
解压即可,发现有两文件:protoc.exe & readme.txt

## linux 下
tar -xzf protobuf-2.1.0.tar.gz 
cd protobuf-2.1.0 
./configure --prefix=$INSTALL_DIR 
make 
make check 
make install

2. 生成代码

protoc.exe 工具根据.proto文件生成代码,例如我们这有两个文件:

## SubscribeReq.proto

package netty;
option java_package = "com.miao.server.protobuf";
option java_outer_classname = "SubscribeReqProto";

message SubscribeReq{
  required int32 subReqID = 1;
  required string userName = 2;
  required string productName = 3;
  repeated string address = 4;
}
## SubscribeResp.proto

package netty;
option java_package = "com.miao.server.protobuf";
option java_outer_classname = "SubscribeRespProto";

message SubscribeResp{
  required int32 subReqID = 1;
  required int32 respCode =2;
  required string desc = 3;
}

然后通过命令行生成Java代码:

D:\**\** > protoc.exe --java_out=.\src SubscribeReq.ptoto

D:\**\** > protoc.exe --java_out=.\src SubscribeResp.proto

最后在当前目录的src目录下生成了 com\miao\server\protobuf\SubscribeReq.java & SubscribeResp.java 文件

入门小例子

将生成的SubscribeReq.java 拖到项目目录下:

新建主类 TestSubscribeReq.java:

public class TestSubscribeReq
{
    private static byte[] encode(SubscribeReqProto.SubscribeReq req){
        return req.toByteArray();
    }

    private static SubscribeReqProto.SubscribeReq decode(byte[] body) throws InvalidProtocolBufferException
    {
        return SubscribeReqProto.SubscribeReq.parseFrom(body);
    }

    private static SubscribeReqProto.SubscribeReq createSubscribeReq(){
        SubscribeReqProto.SubscribeReq.Builder builder =SubscribeReqProto.SubscribeReq.newBuilder();
        builder.setSubReqID(1);
        builder.setUserName("miaoqiao");
        builder.setProductName("Netty test");
        List<String> address=new ArrayList<>();
        address.add("Beijing");
        address.add("Dalian");
        address.add("Meiguo");
        address.add("huizhou");
        builder.addAllAddress(address);
        return builder.build();
    }

    public static void main(String[] args) throws InvalidProtocolBufferException
    {
        SubscribeReqProto.SubscribeReq req=createSubscribeReq();
        System.out.println("Before encode : "+req.toString());
        SubscribeReqProto.SubscribeReq req2=decode(encode(req));
        System.out.println("After decode : "+req.toString());
        System.out.println("Assert equal : --> "+req2.equals(req));
    }
}

运行主类,结果如下:

Before encode : subReqID: 1
userName: "miaoqiao"
productName: "Netty test"
address: "Beijing"
address: "Dalian"
address: "Meiguo"
address: "huizhou"

After decode : subReqID: 1
userName: "miaoqiao"
productName: "Netty test"
address: "Beijing"
address: "Dalian"
address: "Meiguo"
address: "huizhou"

Assert equal : --> true

netty 实例

服务端开发
// 服务端主程序

public class SubReqServer
{
    public void bind(int port) throws InterruptedException
    {
        EventLoopGroup boss=new NioEventLoopGroup();
        EventLoopGroup worker=new NioEventLoopGroup();
        try
        {
            ServerBootstrap b=new ServerBootstrap();
            b.group(boss, worker)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 100)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>()
                    {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception
                        {
                          // 解码器,用于半包处理
                           ch.pipeline().addLast(
                                    new ProtobufVarint32FrameDecoder()
                            );
                          // Protobuf 解码器,参数说明要解码的目标类
                           ch.pipeline().addLast(
                                    new ProtobufDecoder(
                                            SubscribeReqProto.SubscribeReq.getDefaultInstance()
                                    )
                            );
                            ch.pipeline().addLast(
                                    new ProtobufVarint32LengthFieldPrepender()
                            );
                            ch.pipeline().addLast(
                                    new ProtobufEncoder()
                            );
                            ch.pipeline().addLast(
                                    new SubReqServerHandler()
                            );

                        }
                    });
            ChannelFuture f=b.bind(port).sync();

            f.channel().closeFuture().sync();
        } finally
        {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws InterruptedException
    {
        int port = 8005;
        new SubReqServer().bind(port);
    }
}
// 服务端Handler

@Sharable
public class SubReqServerHandler extends ChannelInboundHandlerAdapter
{
    @Override
    public void channelRead(ChannelHandlerContext ctx , Object msg) throws Exception
    {
        SubscribeReqProto.SubscribeReq req= (SubscribeReqProto.SubscribeReq) msg;
        if("miaoqiao".equalsIgnoreCase(req.getUserName())){
            System.out.println("Server accept client req : ["+req.toString()+"]");
            ctx.writeAndFlush(reqp(req.getSubReqID()));
        }
    }

    private SubscribeRespProto.SubscribeResp reqp(int subReqID){
        SubscribeRespProto.SubscribeResp.Builder builder =
                SubscribeRespProto.SubscribeResp.newBuilder();

        builder.setSubReqID(subReqID);
        builder.setRespCode(0);
        builder.setDesc("Netty book order succeed, 3 days later send to the destination");
        return builder.build();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx , Throwable cause) throws Exception
    {
        super.exceptionCaught(ctx , cause);
    }
}
客户端开发
public class SubReqClient
{
    public void connect(int port, String host) throws InterruptedException
    {
        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
                        protected void initChannel(SocketChannel ch) throws Exception
                        {
                            ch.pipeline().addLast(
                                    new ProtobufVarint32FrameDecoder()
                            );
                            ch.pipeline().addLast(
                                    new ProtobufDecoder(
                                            SubscribeRespProto.SubscribeResp.getDefaultInstance()
                                    )
                            );
                            ch.pipeline().addLast(
                                    new ProtobufVarint32LengthFieldPrepender()
                            );
                            ch.pipeline().addLast(
                                    new ProtobufEncoder()
                            );
                            ch.pipeline().addLast(new SubReqClientHandler());
                        }
                    });

            ChannelFuture f=b.connect(host, port).sync();

            f.channel().closeFuture().sync();
        } finally
        {
            group.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws InterruptedException
    {
        int port=8005;
        String host="127.0.0.1";
        new SubReqClient().connect(port, host);
    }
}
// 客户端Handler

public class SubReqClientHandler extends ChannelInboundHandlerAdapter
{
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception
    {
        for (int i = 0; i < 10; i++)
        {
            ctx.write(subReq(i));
        }
        ctx.flush();
    }

    private SubscribeReqProto.SubscribeReq subReq(int i){
        SubscribeReqProto.SubscribeReq.Builder builder=
                SubscribeReqProto.SubscribeReq.newBuilder();

        builder.setSubReqID(i);
        builder.setUserName("miaoqiao");
        builder.setProductName("Netty Book for Protobuf");
        List<String> address=new ArrayList<>();
        address.add("beijing");
        address.add("dalian");
        address.add("shanghai");
        builder.addAllAddress(address);
        return builder.build();
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx , Object msg) throws Exception
    {
        System.out.println("Receive server response : {"+msg+"]");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx , Throwable cause) throws Exception
    {
        cause.printStackTrace();
        ctx.flush();
    }
}
结果测试
// 服务端 console

Server accept client req : [subReqID: 0
userName: "miaoqiao"
productName: "Netty Book for Protobuf"
address: "beijing"
address: "dalian"
address: "shanghai"
]
Server accept client req : [subReqID: 1
userName: "miaoqiao"
productName: "Netty Book for Protobuf"
address: "beijing"
address: "dalian"
address: "shanghai"
]
Server accept client req : [subReqID: 2
userName: "miaoqiao"
productName: "Netty Book for Protobuf"
address: "beijing"
address: "dalian"
address: "shanghai"
]
Server accept client req : [subReqID: 3
userName: "miaoqiao"
productName: "Netty Book for Protobuf"
address: "beijing"
address: "dalian"
address: "shanghai"
]
Server accept client req : [subReqID: 4
userName: "miaoqiao"
productName: "Netty Book for Protobuf"
address: "beijing"
address: "dalian"
address: "shanghai"
]
......

// 客户端 console

Receive server response : {subReqID: 0
respCode: 0
desc: "Netty book order succeed, 3 days later send to the destination"
]
Receive server response : {subReqID: 1
respCode: 0
desc: "Netty book order succeed, 3 days later send to the destination"
]
Receive server response : {subReqID: 2
respCode: 0
desc: "Netty book order succeed, 3 days later send to the destination"
]
Receive server response : {subReqID: 3
respCode: 0
desc: "Netty book order succeed, 3 days later send to the destination"
]
Receive server response : {subReqID: 4
respCode: 0
desc: "Netty book order succeed, 3 days later send to the destination"
]
......

猜你喜欢

转载自blog.csdn.net/qq_24871519/article/details/82631986