编写网络应用程序时,因为数据在网络中传输的都是二进制字节码数据,在发送数据时就需要编码,接收数据时就需要解码
codec(编解码器)的组成:decoder(解码器)和encoder(编码器)
encoder负责把业务数据转换成字节码数据,decoder负责把字节码数据转换成业务数据
1.1、Netty本身编解码机制与问题
1)Netty提供的编码器和解码器
StringEncoder、StringDecoder,对字符串数据进行编码解码
ObjectEncoder、ObjectDecoder,对java对象进行编码解码
2)问题分析
ObjectEncoder、ObjectDecoder可以用来实现POJO对象或各种业务对象的编码和解码,底层使用的时java序列化技术,存在如下问题:
无法跨语言、java序列化效率低、序列化后的体积太大,是二进制编码的5倍多、序列化性能低
引入新的方案:google的protobuf
1.2、Protobuf介绍和使用
是google开源项目,全称Google Protocol Buffers,是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。如数据存储、RPC等
pb是以message方式来管理数据的
支持跨平台、跨语言。
高性能、高可靠性
使用protobuf编译器能自动生成代码,Protobuf是将类的定义使用.proto文件进行描述。在idea中编写proto文件时,可以下载插件
通过protoc 编译器根据.proto自动生成java代码
1)基础类型
| .proto类型 | java类型 | C++类型 | 备注 |
|---|---|---|---|
| double | double | double | |
| float | float | float | |
| int32 | int | int32 | 使用可变长编码方式。编码负数时不够高效——如果你的字段可能含有负数,那么请使用sint32。 |
| int64 | long | int64 | 使用可变长编码方式。编码负数时不够高效——如果你的字段可能含有负数,那么请使用sint64。 |
| unit32 | int[1] | unit32 | 总是4个字节。如果数值总是比总是比228大的话,这个类型会比uint32高效。 |
| unit64 | long[1] | unit64 | 总是8个字节。如果数值总是比总是比256大的话,这个类型会比uint64高效。 |
| sint32 | int | int32 | 使用可变长编码方式。有符号的整型值。编码时比通常的int32高效。 |
| sint64 | long | int64 | 使用可变长编码方式。有符号的整型值。编码时比通常的int64高效。 |
| fixed32 | int[1] | unit32 | |
| fixed64 | long[1] | unit64 | 总是8个字节。如果数值总是比总是比256大的话,这个类型会比uint64高效。 |
| sfixed32 | int | int32 | 总是4个字节。 |
| sfixed64 | long | int64 | 总是8个字节。 |
| bool | boolean | bool | |
| string | String | string | 一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。 |
| bytes | ByteString | string | 可能包含任意顺序的字节数据 |
2)特殊字段
| 英文 | 中文 | 备注 |
|---|---|---|
| enum | 枚举(数字从零开始) 作用是为字段指定某”预定义值序列” | enum Type {MAN = 0;WOMAN = 1; OTHER= 3;} |
| message | 消息体 | message User{} |
| repeated | 数组/集合 | repeated User users = 1 |
| import | 导入定义 | import "protos/other_protos.proto" |
| // | 注释 | //用于注释 |
| extend | 扩展 | extend User {} |
| package | 包名 | 相当于命名空间,用来防止不同消息类型的明明冲突 |
1.2.1、mac安装
brew install protobuf protobuf --version
1.2.2、编写proto文件
在/resources/proto下建立Student.proto文件,内容如下
syntax = "proto3"; option java_outer_classname = "StudentPOJO"; option java_package = "com.github.bjlhx15.netty.demo.netty.codec"; message Student{//会在StudentPOJO外部类生成一个内部类,是真实发送的数据 int32 id = 1 ;//1 属性序号 string name = 2; }
使用如下代码生成
protoc --java_out=../../java/ Student.proto
java代码如下
// Generated by the protocol buffer compiler. DO NOT EDIT! // source: Student.proto package com.github.bjlhx15.netty.demo.netty.codec; 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 { /** * <pre> *1 属性序号 * </pre> * * <code>int32 id = 1;</code> * @return The id. */ int getId(); /** * <code>string name = 2;</code> * @return The name. */ java.lang.String getName(); /** * <code>string name = 2;</code> * @return The bytes for name. */ com.google.protobuf.ByteString getNameBytes(); } /** * <pre> *会在StudentPOJO外部类生成一个内部类,是真实发送的数据 * </pre> * * Protobuf type {@code Student} */ public static final class Student extends com.google.protobuf.GeneratedMessageV3 implements // @@protoc_insertion_point(message_implements:Student) StudentOrBuilder { private static final long serialVersionUID = 0L; // Use Student.newBuilder() to construct. private Student(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) { super(builder); } private Student() { name_ = ""; } @java.lang.Override @SuppressWarnings({"unused"}) protected java.lang.Object newInstance( UnusedPrivateParameter unused) { return new Student(); } @java.lang.Override public final com.google.protobuf.UnknownFieldSet getUnknownFields() { return this.unknownFields; } private Student( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { this(); if (extensionRegistry == null) { throw new java.lang.NullPointerException(); } com.google.protobuf.UnknownFieldSet.Builder unknownFields = com.google.protobuf.UnknownFieldSet.newBuilder(); try { boolean done = false; while (!done) { int tag = input.readTag(); switch (tag) { case 0: done = true; break; case 8: { id_ = input.readInt32(); break; } case 18: { java.lang.String s = input.readStringRequireUtf8(); name_ = s; break; } default: { if (!parseUnknownField( input, unknownFields, extensionRegistry, tag)) { done = true; } break; } } } } catch (com.google.protobuf.InvalidProtocolBufferException e) { throw e.setUnfinishedMessage(this); } catch (java.io.IOException e) { throw new com.google.protobuf.InvalidProtocolBufferException( e).setUnfinishedMessage(this); } finally { this.unknownFields = unknownFields.build(); makeExtensionsImmutable(); } } public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.internal_static_Student_descriptor; } @java.lang.Override protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internalGetFieldAccessorTable() { return com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.internal_static_Student_fieldAccessorTable .ensureFieldAccessorsInitialized( com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student.class, com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student.Builder.class); } public static final int ID_FIELD_NUMBER = 1; private int id_; /** * <pre> *1 属性序号 * </pre> * * <code>int32 id = 1;</code> * @return The id. */ @java.lang.Override public int getId() { return id_; } public static final int NAME_FIELD_NUMBER = 2; private volatile java.lang.Object name_; /** * <code>string name = 2;</code> * @return The name. */ @java.lang.Override public java.lang.String getName() { java.lang.Object ref = name_; if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); name_ = s; return s; } } /** * <code>string name = 2;</code> * @return The bytes for name. */ @java.lang.Override public com.google.protobuf.ByteString getNameBytes() { java.lang.Object ref = name_; if (ref instanceof java.lang.String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); name_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { byte isInitialized = memoizedIsInitialized; if (isInitialized == 1) return true; if (isInitialized == 0) return false; memoizedIsInitialized = 1; return true; } @java.lang.Override public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { if (id_ != 0) { output.writeInt32(1, id_); } if (!getNameBytes().isEmpty()) { com.google.protobuf.GeneratedMessageV3.writeString(output, 2, name_); } unknownFields.writeTo(output); } @java.lang.Override public int getSerializedSize() { int size = memoizedSize; if (size != -1) return size; size = 0; if (id_ != 0) { size += com.google.protobuf.CodedOutputStream .computeInt32Size(1, id_); } if (!getNameBytes().isEmpty()) { size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, name_); } size += unknownFields.getSerializedSize(); memoizedSize = size; return size; } @java.lang.Override public boolean equals(final java.lang.Object obj) { if (obj == this) { return true; } if (!(obj instanceof com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student)) { return super.equals(obj); } com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student other = (com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student) obj; if (getId() != other.getId()) return false; if (!getName() .equals(other.getName())) return false; if (!unknownFields.equals(other.unknownFields)) return false; return true; } @java.lang.Override public int hashCode() { if (memoizedHashCode != 0) { return memoizedHashCode; } int hash = 41; hash = (19 * hash) + getDescriptor().hashCode(); hash = (37 * hash) + ID_FIELD_NUMBER; hash = (53 * hash) + getId(); hash = (37 * hash) + NAME_FIELD_NUMBER; hash = (53 * hash) + getName().hashCode(); hash = (29 * hash) + unknownFields.hashCode(); memoizedHashCode = hash; return hash; } public static com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student parseFrom( java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student parseFrom( java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student parseFrom( com.google.protobuf.ByteString data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student parseFrom( com.google.protobuf.ByteString data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student parseFrom(byte[] data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student parseFrom( byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student parseFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3 .parseWithIOException(PARSER, input); } public static com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student parseFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3 .parseWithIOException(PARSER, input, extensionRegistry); } public static com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student parseDelimitedFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3 .parseDelimitedWithIOException(PARSER, input); } public static com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student parseDelimitedFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3 .parseDelimitedWithIOException(PARSER, input, extensionRegistry); } public static com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student parseFrom( com.google.protobuf.CodedInputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3 .parseWithIOException(PARSER, input); } public static com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student parseFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3 .parseWithIOException(PARSER, input, extensionRegistry); } @java.lang.Override public Builder newBuilderForType() { return newBuilder(); } public static Builder newBuilder() { return DEFAULT_INSTANCE.toBuilder(); } public static Builder newBuilder(com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student prototype) { return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); } @java.lang.Override public Builder toBuilder() { return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this); } @java.lang.Override protected Builder newBuilderForType( com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { Builder builder = new Builder(parent); return builder; } /** * <pre> *会在StudentPOJO外部类生成一个内部类,是真实发送的数据 * </pre> * * Protobuf type {@code Student} */ public static final class Builder extends com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements // @@protoc_insertion_point(builder_implements:Student) com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.StudentOrBuilder { public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.internal_static_Student_descriptor; } @java.lang.Override protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internalGetFieldAccessorTable() { return com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.internal_static_Student_fieldAccessorTable .ensureFieldAccessorsInitialized( com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student.class, com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student.Builder.class); } // Construct using com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student.newBuilder() private Builder() { maybeForceBuilderInitialization(); } private Builder( com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { super(parent); maybeForceBuilderInitialization(); } private void maybeForceBuilderInitialization() { if (com.google.protobuf.GeneratedMessageV3 .alwaysUseFieldBuilders) { } } @java.lang.Override public Builder clear() { super.clear(); id_ = 0; name_ = ""; return this; } @java.lang.Override public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { return com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.internal_static_Student_descriptor; } @java.lang.Override public com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student getDefaultInstanceForType() { return com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student.getDefaultInstance(); } @java.lang.Override public com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student build() { com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student result = buildPartial(); if (!result.isInitialized()) { throw newUninitializedMessageException(result); } return result; } @java.lang.Override public com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student buildPartial() { com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student result = new com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student(this); result.id_ = id_; result.name_ = name_; onBuilt(); return result; } @java.lang.Override public Builder clone() { return super.clone(); } @java.lang.Override public Builder setField( com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) { return super.setField(field, value); } @java.lang.Override public Builder clearField( com.google.protobuf.Descriptors.FieldDescriptor field) { return super.clearField(field); } @java.lang.Override public Builder clearOneof( com.google.protobuf.Descriptors.OneofDescriptor oneof) { return super.clearOneof(oneof); } @java.lang.Override public Builder setRepeatedField( com.google.protobuf.Descriptors.FieldDescriptor field, int index, java.lang.Object value) { return super.setRepeatedField(field, index, value); } @java.lang.Override public Builder addRepeatedField( com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) { return super.addRepeatedField(field, value); } @java.lang.Override public Builder mergeFrom(com.google.protobuf.Message other) { if (other instanceof com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student) { return mergeFrom((com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student)other); } else { super.mergeFrom(other); return this; } } public Builder mergeFrom(com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student other) { if (other == com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student.getDefaultInstance()) return this; if (other.getId() != 0) { setId(other.getId()); } if (!other.getName().isEmpty()) { name_ = other.name_; onChanged(); } this.mergeUnknownFields(other.unknownFields); onChanged(); return this; } @java.lang.Override public final boolean isInitialized() { return true; } @java.lang.Override public Builder mergeFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student parsedMessage = null; try { parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); } catch (com.google.protobuf.InvalidProtocolBufferException e) { parsedMessage = (com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student) e.getUnfinishedMessage(); throw e.unwrapIOException(); } finally { if (parsedMessage != null) { mergeFrom(parsedMessage); } } return this; } private int id_ ; /** * <pre> *1 属性序号 * </pre> * * <code>int32 id = 1;</code> * @return The id. */ @java.lang.Override public int getId() { return id_; } /** * <pre> *1 属性序号 * </pre> * * <code>int32 id = 1;</code> * @param value The id to set. * @return This builder for chaining. */ public Builder setId(int value) { id_ = value; onChanged(); return this; } /** * <pre> *1 属性序号 * </pre> * * <code>int32 id = 1;</code> * @return This builder for chaining. */ public Builder clearId() { id_ = 0; onChanged(); return this; } private java.lang.Object name_ = ""; /** * <code>string name = 2;</code> * @return The name. */ public java.lang.String getName() { java.lang.Object ref = name_; if (!(ref instanceof java.lang.String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); name_ = s; return s; } else { return (java.lang.String) ref; } } /** * <code>string name = 2;</code> * @return The bytes for name. */ public com.google.protobuf.ByteString getNameBytes() { java.lang.Object ref = name_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); name_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } /** * <code>string name = 2;</code> * @param value The name to set. * @return This builder for chaining. */ public Builder setName( java.lang.String value) { if (value == null) { throw new NullPointerException(); } name_ = value; onChanged(); return this; } /** * <code>string name = 2;</code> * @return This builder for chaining. */ public Builder clearName() { name_ = getDefaultInstance().getName(); onChanged(); return this; } /** * <code>string name = 2;</code> * @param value The bytes for name to set. * @return This builder for chaining. */ public Builder setNameBytes( com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); name_ = value; onChanged(); return this; } @java.lang.Override public final Builder setUnknownFields( final com.google.protobuf.UnknownFieldSet unknownFields) { return super.setUnknownFields(unknownFields); } @java.lang.Override public final Builder mergeUnknownFields( final com.google.protobuf.UnknownFieldSet unknownFields) { return super.mergeUnknownFields(unknownFields); } // @@protoc_insertion_point(builder_scope:Student) } // @@protoc_insertion_point(class_scope:Student) private static final com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student DEFAULT_INSTANCE; static { DEFAULT_INSTANCE = new com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student(); } public static com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student getDefaultInstance() { return DEFAULT_INSTANCE; } private static final com.google.protobuf.Parser<Student> PARSER = new com.google.protobuf.AbstractParser<Student>() { @java.lang.Override public Student parsePartialFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return new Student(input, extensionRegistry); } }; public static com.google.protobuf.Parser<Student> parser() { return PARSER; } @java.lang.Override public com.google.protobuf.Parser<Student> getParserForType() { return PARSER; } @java.lang.Override public com.github.bjlhx15.netty.demo.netty.codec.StudentPOJO.Student getDefaultInstanceForType() { return DEFAULT_INSTANCE; } } private static final com.google.protobuf.Descriptors.Descriptor internal_static_Student_descriptor; private static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_Student_fieldAccessorTable; public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { return descriptor; } private static com.google.protobuf.Descriptors.FileDescriptor descriptor; static { java.lang.String[] descriptorData = { "\n\rStudent.proto\"#\n\007Student\022\n\n\002id\030\001 \001(\005\022\014" + "\n\004name\030\002 \001(\tB8\n)com.github.bjlhx15.netty" + ".demo.netty.codecB\013StudentPOJOb\006proto3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor .internalBuildGeneratedFileFrom(descriptorData, new com.google.protobuf.Descriptors.FileDescriptor[] { }); internal_static_Student_descriptor = getDescriptor().getMessageTypes().get(0); internal_static_Student_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_Student_descriptor, new java.lang.String[] { "Id", "Name", }); } // @@protoc_insertion_point(outer_class_scope) }