zoukankan      html  css  js  c++  java
  • Google Protobuf结合Netty实践

    1.Win版Protobuf代码生成工具下载:

    https://github.com/protocolbuffers/protobuf/releases

    注意下载protoc-3.6.1-win32.zip

    2.编写.proto文件注意:

    指定包为外层目录名称,指定java包名为你想使用的java工程该序列化类包名,还需要指定生成Java类的名称。字段赋予的数值或代表次序,而不是字段默认值。示例:

    syntax = "proto2";
    package Protobuf;
    option java_package = "com.xiaobai.codec.protobuf";
    option java_outer_classname = "SubscribeReqProto";

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

    syntax = "proto2";
    package Protobuf;
    option java_package = "com.xiaobai.codec.protobuf";
    option java_outer_classname = "SubscribeRespProto";

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

    3.使用工具时需要注意

    a.需要指定一个--proto_path路径,根据错误提示:

    D:protoc-3.6.1-win32in>protoc.exe --java_out=F:NewAge ettydemosrcmainjav
    a F:NewAge ettydemoProtobufSubscribeReq.proto
    F:NewAge ettydemoProtobufSubscribeReq.proto: File does not reside within any
    path specified using --proto_path (or -I). You must specify a --proto_path whi
    ch encompasses this file. Note that the proto_path must be an exact prefix of t
    he .proto file names -- protoc is too dumb to figure out when two paths (e.g. ab
    solute and relative) are equivalent (it's harder than you think).

    这个路径需要是.proto文件所在目录

    b.需要在.proto文件中指定它的proto语法版本,这样生成时不会出现警告,根据提示,这个版本默认是proto2,可以设置为proto3:

    D:protoc-3.6.1-win32in>protoc.exe --proto_path=F:NewAge ettydemoProtobuf -
    -java_out=F:NewAge ettydemosrcmainjava F:NewAge ettydemoProtobufSubscri
    beReq.proto
    [libprotobuf WARNING T:srcgithubprotobufsrcgoogleprotobufcompilerparser.
    cc:562] No syntax specified for the proto file: SubscribeReq.proto. Please use '
    syntax = "proto2";' or 'syntax = "proto3";' to specify a syntax version. (Defaul
    ted to proto2 syntax.)

    参照:

    https://www.jianshu.com/p/42a480a45cd6

    https://www.cnblogs.com/gifisan/p/5976208.html?utm_source=itdadao&utm_medium=referral

    生成效果如下:

    Win版本安装参考与示例:

    https://www.cnblogs.com/tyw66/p/7352033.html

    Linux版本安装参考与示例:

    https://www.cnblogs.com/learn21cn/p/6206206.html

    https://www.cnblogs.com/luoxn28/p/5303517.html

    4.实践时采用了3.6.1版本代码生成,所以Java工程引入依赖也选择了3.6.1版本,且重新用proto3生成了代码,该语法不支持required字段,所以去掉:

    D:protoc-3.6.1-win32in>protoc.exe --proto_path=F:NewAge ettydemoProtobuf -
    -java_out=F:NewAge ettydemosrcmainjava F:NewAge ettydemoProtobufSubscri
    beResp.proto
    SubscribeResp.proto: Required fields are not allowed in proto3.
    SubscribeResp.proto: Required fields are not allowed in proto3.
    SubscribeResp.proto: Required fields are not allowed in proto3.

    syntax = "proto3";
    package Protobuf;
    option java_package = "com.xiaobai.codec.protobuf";
    option java_outer_classname = "SubscribeReqProto";

    message SubscribeReq{
    int32 subReqID = 1;
    string userName = 2;
    string productName = 3;
    repeated string address = 4;
    }
    syntax = "proto3";
    package Protobuf;
    option java_package = "com.xiaobai.codec.protobuf";
    option java_outer_classname = "SubscribeRespProto";

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

    注意Protobuf里面没有list,需要指定为repeated + 元素类型!!

    未写元素类型导致的报错:

    D:protoc-3.6.1-win32in>protoc.exe --proto_path=F:NewAge ettydemoProtobuf -
    -java_out=F:NewAge ettydemosrcmainjava F:NewAge ettydemoProtobufSubscri
    beReq.proto
    SubscribeReq.proto:10:22: Expected field name.

    参照(定义复杂对象):

    https://blog.csdn.net/hry2015/article/details/70766603

    https://blog.csdn.net/qq_33951440/article/details/80599712

    5.源码中的报错:@Override问题和支持1.6+的Java版本问题

    参考https://blog.csdn.net/jdjdndhj/article/details/70256789

    是因为IDEA源码版本设置的原因:

    6.编译报错

    Error:java: Compilation failed: internal java compiler error

    这是IDEA编译版本设置的原因,注意本项目(nettydemo)的编译版本也要设置成1.8,这里是坑:

    参考:https://www.cnblogs.com/comeluder/p/8215317.html

    7.序列化测试

    package com.xiaobai.codec.protobuf;

    import com.google.protobuf.InvalidProtocolBufferException;

    import java.util.ArrayList;
    import java.util.List;

    public class TestSubscribeReqProto {

    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("xiaobai");
    builder.setProductName("Java Supior");
    List<String> address = new ArrayList<>();
    address.add("Shenyang YUEXING");
    address.add("Jinzhou HUAGUANG");
    address.add("Beijing HAIDIAN");
    builder.addAllAddress(address);
    return builder.build();
    }

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

    输出结果:

    Before encoding:
    subReqID: 1
    userName: "xiaobai"
    productName: "Java Supior"
    address: "Shenyang YUEXING"
    address: "Jinzhou HUAGUANG"
    address: "Beijing HAIDIAN"

    After encoding and decoding:
    subReqID: 1
    userName: "xiaobai"
    productName: "Java Supior"
    address: "Shenyang YUEXING"
    address: "Jinzhou HUAGUANG"
    address: "Beijing HAIDIAN"

    Assert enqual: --> true

    8.结合netty(netty版本需要5.0.0.Alpha1

    ChannelHandlerAdapter版本问题:

    https://my.oschina.net/linwl/blog/1823315

    ChannelHandlerAdapter分析:

    https://www.cnblogs.com/wade-luffy/p/6222960.html

    GitHub:https://github.com/Xiaobai0419/nettydemo

    注意:

    a.例子中客户端和服务端可一直保持连接,和双向互传数据。如果一方断开,另一方报异常:

    java.io.IOException: 远程主机强迫关闭了一个现有的连接。
    at sun.nio.ch.SocketDispatcher.read0(Native Method)
    at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:43)
    at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:223)
    at sun.nio.ch.IOUtil.read(IOUtil.java:192)
    at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:380)
    at io.netty.buffer.UnpooledUnsafeDirectByteBuf.setBytes(UnpooledUnsafeDirectByteBuf.java:446)
    at io.netty.buffer.AbstractByteBuf.writeBytes(AbstractByteBuf.java:871)
    at io.netty.channel.socket.nio.NioSocketChannel.doReadBytes(NioSocketChannel.java:208)
    at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:119)
    at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:485)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:452)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:346)
    at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:794)
    at java.lang.Thread.run(Thread.java:748)

    需要进行处理。

    客户端重新连接,仍可与服务端继续通信。

    b.例子中序列化类int类型字段值为0无法传输该字段,其他值则可以,待查。

  • 相关阅读:
    由ORACLE:ORA-28001: the password has expired(密码过期)引起ODI资料库连接失败
    由ODI初始化资料档案库(RUC)引起修改ORACLE字符集(ZHS16GBK-AL32UTF8)
    如何面对被抛弃的System.Data.OracleClient
    CS231n 2016 通关 第二章-KNN 作业分析
    CS231n 2016 通关 第二章-KNN
    CS231n 2016 通关 第一章-内容介绍
    加特征加正则
    ML 徒手系列说明
    caffe 入门实例2 如何写一个模型
    caffe 入门实例3 fine-turning
  • 原文地址:https://www.cnblogs.com/free-wings/p/10213055.html
Copyright © 2011-2022 走看看