浏览代码

Merge branch 'feature_concurrent' of algorithm/recommend-feature into master

dingyunpeng 11 月之前
父节点
当前提交
7b26028060
共有 15 个文件被更改,包括 3578 次插入10 次删除
  1. 981 0
      recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/feature/model/feature/FeatureKeyProto.java
  2. 67 0
      recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/feature/model/feature/FeatureKeyProtoOrBuilder.java
  3. 110 0
      recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/feature/model/feature/FeatureV2.java
  4. 237 0
      recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/feature/model/feature/FeatureV2Service.java
  5. 288 0
      recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/feature/model/feature/FeatureV2ServiceGrpc.java
  6. 770 0
      recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/feature/model/feature/MultiGetFeatureRequest.java
  7. 33 0
      recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/feature/model/feature/MultiGetFeatureRequestOrBuilder.java
  8. 893 0
      recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/feature/model/feature/MultiGetFeatureResponse.java
  9. 58 0
      recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/feature/model/feature/MultiGetFeatureResponseOrBuilder.java
  10. 28 0
      recommend-feature-client/src/main/proto/com/tzld/piaoquan/recommend/feature/featureV2.proto
  11. 8 8
      recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/common/ThreadPoolFactory.java
  12. 33 0
      recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/grpcservice/FeatureV2GrpcService.java
  13. 70 0
      recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/service/FeatureV2Service.java
  14. 1 1
      recommend-feature-service/src/main/resources/application-dev.yml
  15. 1 1
      recommend-feature-service/src/main/resources/application-test.yml

+ 981 - 0
recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/feature/model/feature/FeatureKeyProto.java

@@ -0,0 +1,981 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/feature/featureV2.proto
+
+package com.tzld.piaoquan.recommend.feature.model.feature;
+
+/**
+ * Protobuf type {@code FeatureKeyProto}
+ */
+public final class FeatureKeyProto extends
+    com.google.protobuf.GeneratedMessageV3 implements
+    // @@protoc_insertion_point(message_implements:FeatureKeyProto)
+    FeatureKeyProtoOrBuilder {
+private static final long serialVersionUID = 0L;
+  // Use FeatureKeyProto.newBuilder() to construct.
+  private FeatureKeyProto(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    super(builder);
+  }
+  private FeatureKeyProto() {
+    uniqueKey_ = "";
+    tableName_ = "";
+  }
+
+  @java.lang.Override
+  @SuppressWarnings({"unused"})
+  protected java.lang.Object newInstance(
+      UnusedPrivateParameter unused) {
+    return new FeatureKeyProto();
+  }
+
+  @java.lang.Override
+  public final com.google.protobuf.UnknownFieldSet
+  getUnknownFields() {
+    return this.unknownFields;
+  }
+  private FeatureKeyProto(
+      com.google.protobuf.CodedInputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    this();
+    if (extensionRegistry == null) {
+      throw new java.lang.NullPointerException();
+    }
+    int mutable_bitField0_ = 0;
+    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 10: {
+            java.lang.String s = input.readStringRequireUtf8();
+
+            uniqueKey_ = s;
+            break;
+          }
+          case 18: {
+            java.lang.String s = input.readStringRequireUtf8();
+
+            tableName_ = s;
+            break;
+          }
+          case 26: {
+            if (!((mutable_bitField0_ & 0x00000001) != 0)) {
+              fieldValue_ = com.google.protobuf.MapField.newMapField(
+                  FieldValueDefaultEntryHolder.defaultEntry);
+              mutable_bitField0_ |= 0x00000001;
+            }
+            com.google.protobuf.MapEntry<java.lang.String, java.lang.String>
+            fieldValue__ = input.readMessage(
+                FieldValueDefaultEntryHolder.defaultEntry.getParserForType(), extensionRegistry);
+            fieldValue_.getMutableMap().put(
+                fieldValue__.getKey(), fieldValue__.getValue());
+            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.tzld.piaoquan.recommend.feature.model.feature.FeatureV2.internal_static_FeatureKeyProto_descriptor;
+  }
+
+  @SuppressWarnings({"rawtypes"})
+  @java.lang.Override
+  protected com.google.protobuf.MapField internalGetMapField(
+      int number) {
+    switch (number) {
+      case 3:
+        return internalGetFieldValue();
+      default:
+        throw new RuntimeException(
+            "Invalid map field number: " + number);
+    }
+  }
+  @java.lang.Override
+  protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internalGetFieldAccessorTable() {
+    return com.tzld.piaoquan.recommend.feature.model.feature.FeatureV2.internal_static_FeatureKeyProto_fieldAccessorTable
+        .ensureFieldAccessorsInitialized(
+            com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto.class, com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto.Builder.class);
+  }
+
+  public static final int UNIQUE_KEY_FIELD_NUMBER = 1;
+  private volatile java.lang.Object uniqueKey_;
+  /**
+   * <code>string unique_key = 1;</code>
+   * @return The uniqueKey.
+   */
+  @java.lang.Override
+  public java.lang.String getUniqueKey() {
+    java.lang.Object ref = uniqueKey_;
+    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();
+      uniqueKey_ = s;
+      return s;
+    }
+  }
+  /**
+   * <code>string unique_key = 1;</code>
+   * @return The bytes for uniqueKey.
+   */
+  @java.lang.Override
+  public com.google.protobuf.ByteString
+      getUniqueKeyBytes() {
+    java.lang.Object ref = uniqueKey_;
+    if (ref instanceof java.lang.String) {
+      com.google.protobuf.ByteString b = 
+          com.google.protobuf.ByteString.copyFromUtf8(
+              (java.lang.String) ref);
+      uniqueKey_ = b;
+      return b;
+    } else {
+      return (com.google.protobuf.ByteString) ref;
+    }
+  }
+
+  public static final int TABLE_NAME_FIELD_NUMBER = 2;
+  private volatile java.lang.Object tableName_;
+  /**
+   * <code>string table_name = 2;</code>
+   * @return The tableName.
+   */
+  @java.lang.Override
+  public java.lang.String getTableName() {
+    java.lang.Object ref = tableName_;
+    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();
+      tableName_ = s;
+      return s;
+    }
+  }
+  /**
+   * <code>string table_name = 2;</code>
+   * @return The bytes for tableName.
+   */
+  @java.lang.Override
+  public com.google.protobuf.ByteString
+      getTableNameBytes() {
+    java.lang.Object ref = tableName_;
+    if (ref instanceof java.lang.String) {
+      com.google.protobuf.ByteString b = 
+          com.google.protobuf.ByteString.copyFromUtf8(
+              (java.lang.String) ref);
+      tableName_ = b;
+      return b;
+    } else {
+      return (com.google.protobuf.ByteString) ref;
+    }
+  }
+
+  public static final int FIELD_VALUE_FIELD_NUMBER = 3;
+  private static final class FieldValueDefaultEntryHolder {
+    static final com.google.protobuf.MapEntry<
+        java.lang.String, java.lang.String> defaultEntry =
+            com.google.protobuf.MapEntry
+            .<java.lang.String, java.lang.String>newDefaultInstance(
+                com.tzld.piaoquan.recommend.feature.model.feature.FeatureV2.internal_static_FeatureKeyProto_FieldValueEntry_descriptor, 
+                com.google.protobuf.WireFormat.FieldType.STRING,
+                "",
+                com.google.protobuf.WireFormat.FieldType.STRING,
+                "");
+  }
+  private com.google.protobuf.MapField<
+      java.lang.String, java.lang.String> fieldValue_;
+  private com.google.protobuf.MapField<java.lang.String, java.lang.String>
+  internalGetFieldValue() {
+    if (fieldValue_ == null) {
+      return com.google.protobuf.MapField.emptyMapField(
+          FieldValueDefaultEntryHolder.defaultEntry);
+    }
+    return fieldValue_;
+  }
+
+  public int getFieldValueCount() {
+    return internalGetFieldValue().getMap().size();
+  }
+  /**
+   * <code>map&lt;string, string&gt; field_value = 3;</code>
+   */
+
+  @java.lang.Override
+  public boolean containsFieldValue(
+      java.lang.String key) {
+    if (key == null) { throw new java.lang.NullPointerException(); }
+    return internalGetFieldValue().getMap().containsKey(key);
+  }
+  /**
+   * Use {@link #getFieldValueMap()} instead.
+   */
+  @java.lang.Override
+  @java.lang.Deprecated
+  public java.util.Map<java.lang.String, java.lang.String> getFieldValue() {
+    return getFieldValueMap();
+  }
+  /**
+   * <code>map&lt;string, string&gt; field_value = 3;</code>
+   */
+  @java.lang.Override
+
+  public java.util.Map<java.lang.String, java.lang.String> getFieldValueMap() {
+    return internalGetFieldValue().getMap();
+  }
+  /**
+   * <code>map&lt;string, string&gt; field_value = 3;</code>
+   */
+  @java.lang.Override
+
+  public java.lang.String getFieldValueOrDefault(
+      java.lang.String key,
+      java.lang.String defaultValue) {
+    if (key == null) { throw new java.lang.NullPointerException(); }
+    java.util.Map<java.lang.String, java.lang.String> map =
+        internalGetFieldValue().getMap();
+    return map.containsKey(key) ? map.get(key) : defaultValue;
+  }
+  /**
+   * <code>map&lt;string, string&gt; field_value = 3;</code>
+   */
+  @java.lang.Override
+
+  public java.lang.String getFieldValueOrThrow(
+      java.lang.String key) {
+    if (key == null) { throw new java.lang.NullPointerException(); }
+    java.util.Map<java.lang.String, java.lang.String> map =
+        internalGetFieldValue().getMap();
+    if (!map.containsKey(key)) {
+      throw new java.lang.IllegalArgumentException();
+    }
+    return map.get(key);
+  }
+
+  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 (!getUniqueKeyBytes().isEmpty()) {
+      com.google.protobuf.GeneratedMessageV3.writeString(output, 1, uniqueKey_);
+    }
+    if (!getTableNameBytes().isEmpty()) {
+      com.google.protobuf.GeneratedMessageV3.writeString(output, 2, tableName_);
+    }
+    com.google.protobuf.GeneratedMessageV3
+      .serializeStringMapTo(
+        output,
+        internalGetFieldValue(),
+        FieldValueDefaultEntryHolder.defaultEntry,
+        3);
+    unknownFields.writeTo(output);
+  }
+
+  @java.lang.Override
+  public int getSerializedSize() {
+    int size = memoizedSize;
+    if (size != -1) return size;
+
+    size = 0;
+    if (!getUniqueKeyBytes().isEmpty()) {
+      size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, uniqueKey_);
+    }
+    if (!getTableNameBytes().isEmpty()) {
+      size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, tableName_);
+    }
+    for (java.util.Map.Entry<java.lang.String, java.lang.String> entry
+         : internalGetFieldValue().getMap().entrySet()) {
+      com.google.protobuf.MapEntry<java.lang.String, java.lang.String>
+      fieldValue__ = FieldValueDefaultEntryHolder.defaultEntry.newBuilderForType()
+          .setKey(entry.getKey())
+          .setValue(entry.getValue())
+          .build();
+      size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(3, fieldValue__);
+    }
+    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.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto)) {
+      return super.equals(obj);
+    }
+    com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto other = (com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto) obj;
+
+    if (!getUniqueKey()
+        .equals(other.getUniqueKey())) return false;
+    if (!getTableName()
+        .equals(other.getTableName())) return false;
+    if (!internalGetFieldValue().equals(
+        other.internalGetFieldValue())) 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) + UNIQUE_KEY_FIELD_NUMBER;
+    hash = (53 * hash) + getUniqueKey().hashCode();
+    hash = (37 * hash) + TABLE_NAME_FIELD_NUMBER;
+    hash = (53 * hash) + getTableName().hashCode();
+    if (!internalGetFieldValue().getMap().isEmpty()) {
+      hash = (37 * hash) + FIELD_VALUE_FIELD_NUMBER;
+      hash = (53 * hash) + internalGetFieldValue().hashCode();
+    }
+    hash = (29 * hash) + unknownFields.hashCode();
+    memoizedHashCode = hash;
+    return hash;
+  }
+
+  public static com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto parseFrom(
+      java.nio.ByteBuffer data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto parseFrom(
+      java.nio.ByteBuffer data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto parseFrom(
+      com.google.protobuf.ByteString data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto parseFrom(
+      com.google.protobuf.ByteString data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto parseFrom(byte[] data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto parseFrom(
+      byte[] data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto parseFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto 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.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto parseDelimitedFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto 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.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto parseFrom(
+      com.google.protobuf.CodedInputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto 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.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto 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;
+  }
+  /**
+   * Protobuf type {@code FeatureKeyProto}
+   */
+  public static final class Builder extends
+      com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
+      // @@protoc_insertion_point(builder_implements:FeatureKeyProto)
+      com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProtoOrBuilder {
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return com.tzld.piaoquan.recommend.feature.model.feature.FeatureV2.internal_static_FeatureKeyProto_descriptor;
+    }
+
+    @SuppressWarnings({"rawtypes"})
+    protected com.google.protobuf.MapField internalGetMapField(
+        int number) {
+      switch (number) {
+        case 3:
+          return internalGetFieldValue();
+        default:
+          throw new RuntimeException(
+              "Invalid map field number: " + number);
+      }
+    }
+    @SuppressWarnings({"rawtypes"})
+    protected com.google.protobuf.MapField internalGetMutableMapField(
+        int number) {
+      switch (number) {
+        case 3:
+          return internalGetMutableFieldValue();
+        default:
+          throw new RuntimeException(
+              "Invalid map field number: " + number);
+      }
+    }
+    @java.lang.Override
+    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return com.tzld.piaoquan.recommend.feature.model.feature.FeatureV2.internal_static_FeatureKeyProto_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto.class, com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto.Builder.class);
+    }
+
+    // Construct using com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto.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();
+      uniqueKey_ = "";
+
+      tableName_ = "";
+
+      internalGetMutableFieldValue().clear();
+      return this;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.Descriptor
+        getDescriptorForType() {
+      return com.tzld.piaoquan.recommend.feature.model.feature.FeatureV2.internal_static_FeatureKeyProto_descriptor;
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto getDefaultInstanceForType() {
+      return com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto.getDefaultInstance();
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto build() {
+      com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto result = buildPartial();
+      if (!result.isInitialized()) {
+        throw newUninitializedMessageException(result);
+      }
+      return result;
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto buildPartial() {
+      com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto result = new com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto(this);
+      int from_bitField0_ = bitField0_;
+      result.uniqueKey_ = uniqueKey_;
+      result.tableName_ = tableName_;
+      result.fieldValue_ = internalGetFieldValue();
+      result.fieldValue_.makeImmutable();
+      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.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto) {
+        return mergeFrom((com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto)other);
+      } else {
+        super.mergeFrom(other);
+        return this;
+      }
+    }
+
+    public Builder mergeFrom(com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto other) {
+      if (other == com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto.getDefaultInstance()) return this;
+      if (!other.getUniqueKey().isEmpty()) {
+        uniqueKey_ = other.uniqueKey_;
+        onChanged();
+      }
+      if (!other.getTableName().isEmpty()) {
+        tableName_ = other.tableName_;
+        onChanged();
+      }
+      internalGetMutableFieldValue().mergeFrom(
+          other.internalGetFieldValue());
+      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.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto parsedMessage = null;
+      try {
+        parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        parsedMessage = (com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto) e.getUnfinishedMessage();
+        throw e.unwrapIOException();
+      } finally {
+        if (parsedMessage != null) {
+          mergeFrom(parsedMessage);
+        }
+      }
+      return this;
+    }
+    private int bitField0_;
+
+    private java.lang.Object uniqueKey_ = "";
+    /**
+     * <code>string unique_key = 1;</code>
+     * @return The uniqueKey.
+     */
+    public java.lang.String getUniqueKey() {
+      java.lang.Object ref = uniqueKey_;
+      if (!(ref instanceof java.lang.String)) {
+        com.google.protobuf.ByteString bs =
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        uniqueKey_ = s;
+        return s;
+      } else {
+        return (java.lang.String) ref;
+      }
+    }
+    /**
+     * <code>string unique_key = 1;</code>
+     * @return The bytes for uniqueKey.
+     */
+    public com.google.protobuf.ByteString
+        getUniqueKeyBytes() {
+      java.lang.Object ref = uniqueKey_;
+      if (ref instanceof String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        uniqueKey_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+    /**
+     * <code>string unique_key = 1;</code>
+     * @param value The uniqueKey to set.
+     * @return This builder for chaining.
+     */
+    public Builder setUniqueKey(
+        java.lang.String value) {
+      if (value == null) {
+    throw new NullPointerException();
+  }
+  
+      uniqueKey_ = value;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string unique_key = 1;</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearUniqueKey() {
+      
+      uniqueKey_ = getDefaultInstance().getUniqueKey();
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string unique_key = 1;</code>
+     * @param value The bytes for uniqueKey to set.
+     * @return This builder for chaining.
+     */
+    public Builder setUniqueKeyBytes(
+        com.google.protobuf.ByteString value) {
+      if (value == null) {
+    throw new NullPointerException();
+  }
+  checkByteStringIsUtf8(value);
+      
+      uniqueKey_ = value;
+      onChanged();
+      return this;
+    }
+
+    private java.lang.Object tableName_ = "";
+    /**
+     * <code>string table_name = 2;</code>
+     * @return The tableName.
+     */
+    public java.lang.String getTableName() {
+      java.lang.Object ref = tableName_;
+      if (!(ref instanceof java.lang.String)) {
+        com.google.protobuf.ByteString bs =
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        tableName_ = s;
+        return s;
+      } else {
+        return (java.lang.String) ref;
+      }
+    }
+    /**
+     * <code>string table_name = 2;</code>
+     * @return The bytes for tableName.
+     */
+    public com.google.protobuf.ByteString
+        getTableNameBytes() {
+      java.lang.Object ref = tableName_;
+      if (ref instanceof String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        tableName_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+    /**
+     * <code>string table_name = 2;</code>
+     * @param value The tableName to set.
+     * @return This builder for chaining.
+     */
+    public Builder setTableName(
+        java.lang.String value) {
+      if (value == null) {
+    throw new NullPointerException();
+  }
+  
+      tableName_ = value;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string table_name = 2;</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearTableName() {
+      
+      tableName_ = getDefaultInstance().getTableName();
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string table_name = 2;</code>
+     * @param value The bytes for tableName to set.
+     * @return This builder for chaining.
+     */
+    public Builder setTableNameBytes(
+        com.google.protobuf.ByteString value) {
+      if (value == null) {
+    throw new NullPointerException();
+  }
+  checkByteStringIsUtf8(value);
+      
+      tableName_ = value;
+      onChanged();
+      return this;
+    }
+
+    private com.google.protobuf.MapField<
+        java.lang.String, java.lang.String> fieldValue_;
+    private com.google.protobuf.MapField<java.lang.String, java.lang.String>
+    internalGetFieldValue() {
+      if (fieldValue_ == null) {
+        return com.google.protobuf.MapField.emptyMapField(
+            FieldValueDefaultEntryHolder.defaultEntry);
+      }
+      return fieldValue_;
+    }
+    private com.google.protobuf.MapField<java.lang.String, java.lang.String>
+    internalGetMutableFieldValue() {
+      onChanged();;
+      if (fieldValue_ == null) {
+        fieldValue_ = com.google.protobuf.MapField.newMapField(
+            FieldValueDefaultEntryHolder.defaultEntry);
+      }
+      if (!fieldValue_.isMutable()) {
+        fieldValue_ = fieldValue_.copy();
+      }
+      return fieldValue_;
+    }
+
+    public int getFieldValueCount() {
+      return internalGetFieldValue().getMap().size();
+    }
+    /**
+     * <code>map&lt;string, string&gt; field_value = 3;</code>
+     */
+
+    @java.lang.Override
+    public boolean containsFieldValue(
+        java.lang.String key) {
+      if (key == null) { throw new java.lang.NullPointerException(); }
+      return internalGetFieldValue().getMap().containsKey(key);
+    }
+    /**
+     * Use {@link #getFieldValueMap()} instead.
+     */
+    @java.lang.Override
+    @java.lang.Deprecated
+    public java.util.Map<java.lang.String, java.lang.String> getFieldValue() {
+      return getFieldValueMap();
+    }
+    /**
+     * <code>map&lt;string, string&gt; field_value = 3;</code>
+     */
+    @java.lang.Override
+
+    public java.util.Map<java.lang.String, java.lang.String> getFieldValueMap() {
+      return internalGetFieldValue().getMap();
+    }
+    /**
+     * <code>map&lt;string, string&gt; field_value = 3;</code>
+     */
+    @java.lang.Override
+
+    public java.lang.String getFieldValueOrDefault(
+        java.lang.String key,
+        java.lang.String defaultValue) {
+      if (key == null) { throw new java.lang.NullPointerException(); }
+      java.util.Map<java.lang.String, java.lang.String> map =
+          internalGetFieldValue().getMap();
+      return map.containsKey(key) ? map.get(key) : defaultValue;
+    }
+    /**
+     * <code>map&lt;string, string&gt; field_value = 3;</code>
+     */
+    @java.lang.Override
+
+    public java.lang.String getFieldValueOrThrow(
+        java.lang.String key) {
+      if (key == null) { throw new java.lang.NullPointerException(); }
+      java.util.Map<java.lang.String, java.lang.String> map =
+          internalGetFieldValue().getMap();
+      if (!map.containsKey(key)) {
+        throw new java.lang.IllegalArgumentException();
+      }
+      return map.get(key);
+    }
+
+    public Builder clearFieldValue() {
+      internalGetMutableFieldValue().getMutableMap()
+          .clear();
+      return this;
+    }
+    /**
+     * <code>map&lt;string, string&gt; field_value = 3;</code>
+     */
+
+    public Builder removeFieldValue(
+        java.lang.String key) {
+      if (key == null) { throw new java.lang.NullPointerException(); }
+      internalGetMutableFieldValue().getMutableMap()
+          .remove(key);
+      return this;
+    }
+    /**
+     * Use alternate mutation accessors instead.
+     */
+    @java.lang.Deprecated
+    public java.util.Map<java.lang.String, java.lang.String>
+    getMutableFieldValue() {
+      return internalGetMutableFieldValue().getMutableMap();
+    }
+    /**
+     * <code>map&lt;string, string&gt; field_value = 3;</code>
+     */
+    public Builder putFieldValue(
+        java.lang.String key,
+        java.lang.String value) {
+      if (key == null) { throw new java.lang.NullPointerException(); }
+      if (value == null) { throw new java.lang.NullPointerException(); }
+      internalGetMutableFieldValue().getMutableMap()
+          .put(key, value);
+      return this;
+    }
+    /**
+     * <code>map&lt;string, string&gt; field_value = 3;</code>
+     */
+
+    public Builder putAllFieldValue(
+        java.util.Map<java.lang.String, java.lang.String> values) {
+      internalGetMutableFieldValue().getMutableMap()
+          .putAll(values);
+      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:FeatureKeyProto)
+  }
+
+  // @@protoc_insertion_point(class_scope:FeatureKeyProto)
+  private static final com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto DEFAULT_INSTANCE;
+  static {
+    DEFAULT_INSTANCE = new com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto();
+  }
+
+  public static com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto getDefaultInstance() {
+    return DEFAULT_INSTANCE;
+  }
+
+  private static final com.google.protobuf.Parser<FeatureKeyProto>
+      PARSER = new com.google.protobuf.AbstractParser<FeatureKeyProto>() {
+    @java.lang.Override
+    public FeatureKeyProto parsePartialFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return new FeatureKeyProto(input, extensionRegistry);
+    }
+  };
+
+  public static com.google.protobuf.Parser<FeatureKeyProto> parser() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.google.protobuf.Parser<FeatureKeyProto> getParserForType() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto getDefaultInstanceForType() {
+    return DEFAULT_INSTANCE;
+  }
+
+}
+

+ 67 - 0
recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/feature/model/feature/FeatureKeyProtoOrBuilder.java

@@ -0,0 +1,67 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/feature/featureV2.proto
+
+package com.tzld.piaoquan.recommend.feature.model.feature;
+
+public interface FeatureKeyProtoOrBuilder extends
+    // @@protoc_insertion_point(interface_extends:FeatureKeyProto)
+    com.google.protobuf.MessageOrBuilder {
+
+  /**
+   * <code>string unique_key = 1;</code>
+   * @return The uniqueKey.
+   */
+  java.lang.String getUniqueKey();
+  /**
+   * <code>string unique_key = 1;</code>
+   * @return The bytes for uniqueKey.
+   */
+  com.google.protobuf.ByteString
+      getUniqueKeyBytes();
+
+  /**
+   * <code>string table_name = 2;</code>
+   * @return The tableName.
+   */
+  java.lang.String getTableName();
+  /**
+   * <code>string table_name = 2;</code>
+   * @return The bytes for tableName.
+   */
+  com.google.protobuf.ByteString
+      getTableNameBytes();
+
+  /**
+   * <code>map&lt;string, string&gt; field_value = 3;</code>
+   */
+  int getFieldValueCount();
+  /**
+   * <code>map&lt;string, string&gt; field_value = 3;</code>
+   */
+  boolean containsFieldValue(
+      java.lang.String key);
+  /**
+   * Use {@link #getFieldValueMap()} instead.
+   */
+  @java.lang.Deprecated
+  java.util.Map<java.lang.String, java.lang.String>
+  getFieldValue();
+  /**
+   * <code>map&lt;string, string&gt; field_value = 3;</code>
+   */
+  java.util.Map<java.lang.String, java.lang.String>
+  getFieldValueMap();
+  /**
+   * <code>map&lt;string, string&gt; field_value = 3;</code>
+   */
+
+  java.lang.String getFieldValueOrDefault(
+      java.lang.String key,
+      java.lang.String defaultValue);
+  /**
+   * <code>map&lt;string, string&gt; field_value = 3;</code>
+   */
+
+  java.lang.String getFieldValueOrThrow(
+      java.lang.String key);
+}

+ 110 - 0
recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/feature/model/feature/FeatureV2.java

@@ -0,0 +1,110 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/feature/featureV2.proto
+
+package com.tzld.piaoquan.recommend.feature.model.feature;
+
+public final class FeatureV2 {
+  private FeatureV2() {}
+  public static void registerAllExtensions(
+      com.google.protobuf.ExtensionRegistryLite registry) {
+  }
+
+  public static void registerAllExtensions(
+      com.google.protobuf.ExtensionRegistry registry) {
+    registerAllExtensions(
+        (com.google.protobuf.ExtensionRegistryLite) registry);
+  }
+  static final com.google.protobuf.Descriptors.Descriptor
+    internal_static_MultiGetFeatureRequest_descriptor;
+  static final 
+    com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internal_static_MultiGetFeatureRequest_fieldAccessorTable;
+  static final com.google.protobuf.Descriptors.Descriptor
+    internal_static_FeatureKeyProto_descriptor;
+  static final 
+    com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internal_static_FeatureKeyProto_fieldAccessorTable;
+  static final com.google.protobuf.Descriptors.Descriptor
+    internal_static_FeatureKeyProto_FieldValueEntry_descriptor;
+  static final 
+    com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internal_static_FeatureKeyProto_FieldValueEntry_fieldAccessorTable;
+  static final com.google.protobuf.Descriptors.Descriptor
+    internal_static_MultiGetFeatureResponse_descriptor;
+  static final 
+    com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internal_static_MultiGetFeatureResponse_fieldAccessorTable;
+  static final com.google.protobuf.Descriptors.Descriptor
+    internal_static_MultiGetFeatureResponse_FeatureEntry_descriptor;
+  static final 
+    com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internal_static_MultiGetFeatureResponse_FeatureEntry_fieldAccessorTable;
+
+  public static com.google.protobuf.Descriptors.FileDescriptor
+      getDescriptor() {
+    return descriptor;
+  }
+  private static  com.google.protobuf.Descriptors.FileDescriptor
+      descriptor;
+  static {
+    java.lang.String[] descriptorData = {
+      "\n3com/tzld/piaoquan/recommend/feature/fe" +
+      "atureV2.proto\032\031google/protobuf/any.proto" +
+      "\0320com/tzld/piaoquan/recommend/feature/co" +
+      "mmon.proto\"?\n\026MultiGetFeatureRequest\022%\n\013" +
+      "feature_key\030\001 \003(\0132\020.FeatureKeyProto\"\243\001\n\017" +
+      "FeatureKeyProto\022\022\n\nunique_key\030\001 \001(\t\022\022\n\nt" +
+      "able_name\030\002 \001(\t\0225\n\013field_value\030\003 \003(\0132 .F" +
+      "eatureKeyProto.FieldValueEntry\0321\n\017FieldV" +
+      "alueEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028" +
+      "\001\"\232\001\n\027MultiGetFeatureResponse\022\027\n\006result\030" +
+      "\001 \001(\0132\007.Result\0226\n\007feature\030\002 \003(\0132%.MultiG" +
+      "etFeatureResponse.FeatureEntry\032.\n\014Featur" +
+      "eEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\0012X" +
+      "\n\020FeatureV2Service\022D\n\017MultiGetFeature\022\027." +
+      "MultiGetFeatureRequest\032\030.MultiGetFeature" +
+      "ResponseB8\n1com.tzld.piaoquan.recommend." +
+      "feature.model.featureP\001\210\001\001b\006proto3"
+    };
+    descriptor = com.google.protobuf.Descriptors.FileDescriptor
+      .internalBuildGeneratedFileFrom(descriptorData,
+        new com.google.protobuf.Descriptors.FileDescriptor[] {
+          com.google.protobuf.AnyProto.getDescriptor(),
+          com.tzld.piaoquan.recommend.feature.model.common.Common.getDescriptor(),
+        });
+    internal_static_MultiGetFeatureRequest_descriptor =
+      getDescriptor().getMessageTypes().get(0);
+    internal_static_MultiGetFeatureRequest_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_MultiGetFeatureRequest_descriptor,
+        new java.lang.String[] { "FeatureKey", });
+    internal_static_FeatureKeyProto_descriptor =
+      getDescriptor().getMessageTypes().get(1);
+    internal_static_FeatureKeyProto_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_FeatureKeyProto_descriptor,
+        new java.lang.String[] { "UniqueKey", "TableName", "FieldValue", });
+    internal_static_FeatureKeyProto_FieldValueEntry_descriptor =
+      internal_static_FeatureKeyProto_descriptor.getNestedTypes().get(0);
+    internal_static_FeatureKeyProto_FieldValueEntry_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_FeatureKeyProto_FieldValueEntry_descriptor,
+        new java.lang.String[] { "Key", "Value", });
+    internal_static_MultiGetFeatureResponse_descriptor =
+      getDescriptor().getMessageTypes().get(2);
+    internal_static_MultiGetFeatureResponse_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_MultiGetFeatureResponse_descriptor,
+        new java.lang.String[] { "Result", "Feature", });
+    internal_static_MultiGetFeatureResponse_FeatureEntry_descriptor =
+      internal_static_MultiGetFeatureResponse_descriptor.getNestedTypes().get(0);
+    internal_static_MultiGetFeatureResponse_FeatureEntry_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_MultiGetFeatureResponse_FeatureEntry_descriptor,
+        new java.lang.String[] { "Key", "Value", });
+    com.google.protobuf.AnyProto.getDescriptor();
+    com.tzld.piaoquan.recommend.feature.model.common.Common.getDescriptor();
+  }
+
+  // @@protoc_insertion_point(outer_class_scope)
+}

+ 237 - 0
recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/feature/model/feature/FeatureV2Service.java

@@ -0,0 +1,237 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/feature/featureV2.proto
+
+package com.tzld.piaoquan.recommend.feature.model.feature;
+
+/**
+ * Protobuf service {@code FeatureV2Service}
+ */
+public  abstract class FeatureV2Service
+    implements com.google.protobuf.Service {
+  protected FeatureV2Service() {}
+
+  public interface Interface {
+    /**
+     * <code>rpc MultiGetFeature(.MultiGetFeatureRequest) returns (.MultiGetFeatureResponse);</code>
+     */
+    public abstract void multiGetFeature(
+        com.google.protobuf.RpcController controller,
+        com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest request,
+        com.google.protobuf.RpcCallback<com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse> done);
+
+  }
+
+  public static com.google.protobuf.Service newReflectiveService(
+      final Interface impl) {
+    return new FeatureV2Service() {
+      @java.lang.Override
+      public  void multiGetFeature(
+          com.google.protobuf.RpcController controller,
+          com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest request,
+          com.google.protobuf.RpcCallback<com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse> done) {
+        impl.multiGetFeature(controller, request, done);
+      }
+
+    };
+  }
+
+  public static com.google.protobuf.BlockingService
+      newReflectiveBlockingService(final BlockingInterface impl) {
+    return new com.google.protobuf.BlockingService() {
+      public final com.google.protobuf.Descriptors.ServiceDescriptor
+          getDescriptorForType() {
+        return getDescriptor();
+      }
+
+      public final com.google.protobuf.Message callBlockingMethod(
+          com.google.protobuf.Descriptors.MethodDescriptor method,
+          com.google.protobuf.RpcController controller,
+          com.google.protobuf.Message request)
+          throws com.google.protobuf.ServiceException {
+        if (method.getService() != getDescriptor()) {
+          throw new java.lang.IllegalArgumentException(
+            "Service.callBlockingMethod() given method descriptor for " +
+            "wrong service type.");
+        }
+        switch(method.getIndex()) {
+          case 0:
+            return impl.multiGetFeature(controller, (com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest)request);
+          default:
+            throw new java.lang.AssertionError("Can't get here.");
+        }
+      }
+
+      public final com.google.protobuf.Message
+          getRequestPrototype(
+          com.google.protobuf.Descriptors.MethodDescriptor method) {
+        if (method.getService() != getDescriptor()) {
+          throw new java.lang.IllegalArgumentException(
+            "Service.getRequestPrototype() given method " +
+            "descriptor for wrong service type.");
+        }
+        switch(method.getIndex()) {
+          case 0:
+            return com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest.getDefaultInstance();
+          default:
+            throw new java.lang.AssertionError("Can't get here.");
+        }
+      }
+
+      public final com.google.protobuf.Message
+          getResponsePrototype(
+          com.google.protobuf.Descriptors.MethodDescriptor method) {
+        if (method.getService() != getDescriptor()) {
+          throw new java.lang.IllegalArgumentException(
+            "Service.getResponsePrototype() given method " +
+            "descriptor for wrong service type.");
+        }
+        switch(method.getIndex()) {
+          case 0:
+            return com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse.getDefaultInstance();
+          default:
+            throw new java.lang.AssertionError("Can't get here.");
+        }
+      }
+
+    };
+  }
+
+  /**
+   * <code>rpc MultiGetFeature(.MultiGetFeatureRequest) returns (.MultiGetFeatureResponse);</code>
+   */
+  public abstract void multiGetFeature(
+      com.google.protobuf.RpcController controller,
+      com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest request,
+      com.google.protobuf.RpcCallback<com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse> done);
+
+  public static final
+      com.google.protobuf.Descriptors.ServiceDescriptor
+      getDescriptor() {
+    return com.tzld.piaoquan.recommend.feature.model.feature.FeatureV2.getDescriptor().getServices().get(0);
+  }
+  public final com.google.protobuf.Descriptors.ServiceDescriptor
+      getDescriptorForType() {
+    return getDescriptor();
+  }
+
+  public final void callMethod(
+      com.google.protobuf.Descriptors.MethodDescriptor method,
+      com.google.protobuf.RpcController controller,
+      com.google.protobuf.Message request,
+      com.google.protobuf.RpcCallback<
+        com.google.protobuf.Message> done) {
+    if (method.getService() != getDescriptor()) {
+      throw new java.lang.IllegalArgumentException(
+        "Service.callMethod() given method descriptor for wrong " +
+        "service type.");
+    }
+    switch(method.getIndex()) {
+      case 0:
+        this.multiGetFeature(controller, (com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest)request,
+          com.google.protobuf.RpcUtil.<com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse>specializeCallback(
+            done));
+        return;
+      default:
+        throw new java.lang.AssertionError("Can't get here.");
+    }
+  }
+
+  public final com.google.protobuf.Message
+      getRequestPrototype(
+      com.google.protobuf.Descriptors.MethodDescriptor method) {
+    if (method.getService() != getDescriptor()) {
+      throw new java.lang.IllegalArgumentException(
+        "Service.getRequestPrototype() given method " +
+        "descriptor for wrong service type.");
+    }
+    switch(method.getIndex()) {
+      case 0:
+        return com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest.getDefaultInstance();
+      default:
+        throw new java.lang.AssertionError("Can't get here.");
+    }
+  }
+
+  public final com.google.protobuf.Message
+      getResponsePrototype(
+      com.google.protobuf.Descriptors.MethodDescriptor method) {
+    if (method.getService() != getDescriptor()) {
+      throw new java.lang.IllegalArgumentException(
+        "Service.getResponsePrototype() given method " +
+        "descriptor for wrong service type.");
+    }
+    switch(method.getIndex()) {
+      case 0:
+        return com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse.getDefaultInstance();
+      default:
+        throw new java.lang.AssertionError("Can't get here.");
+    }
+  }
+
+  public static Stub newStub(
+      com.google.protobuf.RpcChannel channel) {
+    return new Stub(channel);
+  }
+
+  public static final class Stub extends com.tzld.piaoquan.recommend.feature.model.feature.FeatureV2Service implements Interface {
+    private Stub(com.google.protobuf.RpcChannel channel) {
+      this.channel = channel;
+    }
+
+    private final com.google.protobuf.RpcChannel channel;
+
+    public com.google.protobuf.RpcChannel getChannel() {
+      return channel;
+    }
+
+    public  void multiGetFeature(
+        com.google.protobuf.RpcController controller,
+        com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest request,
+        com.google.protobuf.RpcCallback<com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse> done) {
+      channel.callMethod(
+        getDescriptor().getMethods().get(0),
+        controller,
+        request,
+        com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse.getDefaultInstance(),
+        com.google.protobuf.RpcUtil.generalizeCallback(
+          done,
+          com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse.class,
+          com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse.getDefaultInstance()));
+    }
+  }
+
+  public static BlockingInterface newBlockingStub(
+      com.google.protobuf.BlockingRpcChannel channel) {
+    return new BlockingStub(channel);
+  }
+
+  public interface BlockingInterface {
+    public com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse multiGetFeature(
+        com.google.protobuf.RpcController controller,
+        com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest request)
+        throws com.google.protobuf.ServiceException;
+  }
+
+  private static final class BlockingStub implements BlockingInterface {
+    private BlockingStub(com.google.protobuf.BlockingRpcChannel channel) {
+      this.channel = channel;
+    }
+
+    private final com.google.protobuf.BlockingRpcChannel channel;
+
+    public com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse multiGetFeature(
+        com.google.protobuf.RpcController controller,
+        com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest request)
+        throws com.google.protobuf.ServiceException {
+      return (com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse) channel.callBlockingMethod(
+        getDescriptor().getMethods().get(0),
+        controller,
+        request,
+        com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse.getDefaultInstance());
+    }
+
+  }
+
+  // @@protoc_insertion_point(class_scope:FeatureV2Service)
+}
+

+ 288 - 0
recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/feature/model/feature/FeatureV2ServiceGrpc.java

@@ -0,0 +1,288 @@
+package com.tzld.piaoquan.recommend.feature.model.feature;
+
+import static io.grpc.MethodDescriptor.generateFullMethodName;
+import static io.grpc.stub.ClientCalls.asyncBidiStreamingCall;
+import static io.grpc.stub.ClientCalls.asyncClientStreamingCall;
+import static io.grpc.stub.ClientCalls.asyncServerStreamingCall;
+import static io.grpc.stub.ClientCalls.asyncUnaryCall;
+import static io.grpc.stub.ClientCalls.blockingServerStreamingCall;
+import static io.grpc.stub.ClientCalls.blockingUnaryCall;
+import static io.grpc.stub.ClientCalls.futureUnaryCall;
+import static io.grpc.stub.ServerCalls.asyncBidiStreamingCall;
+import static io.grpc.stub.ServerCalls.asyncClientStreamingCall;
+import static io.grpc.stub.ServerCalls.asyncServerStreamingCall;
+import static io.grpc.stub.ServerCalls.asyncUnaryCall;
+import static io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall;
+import static io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall;
+
+/**
+ */
+@javax.annotation.Generated(
+    value = "by gRPC proto compiler (version 1.34.1)",
+    comments = "Source: com/tzld/piaoquan/recommend/feature/featureV2.proto")
+public final class FeatureV2ServiceGrpc {
+
+  private FeatureV2ServiceGrpc() {}
+
+  public static final String SERVICE_NAME = "FeatureV2Service";
+
+  // Static method descriptors that strictly reflect the proto.
+  private static volatile io.grpc.MethodDescriptor<com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest,
+      com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse> getMultiGetFeatureMethod;
+
+  @io.grpc.stub.annotations.RpcMethod(
+      fullMethodName = SERVICE_NAME + '/' + "MultiGetFeature",
+      requestType = com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest.class,
+      responseType = com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse.class,
+      methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
+  public static io.grpc.MethodDescriptor<com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest,
+      com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse> getMultiGetFeatureMethod() {
+    io.grpc.MethodDescriptor<com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest, com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse> getMultiGetFeatureMethod;
+    if ((getMultiGetFeatureMethod = FeatureV2ServiceGrpc.getMultiGetFeatureMethod) == null) {
+      synchronized (FeatureV2ServiceGrpc.class) {
+        if ((getMultiGetFeatureMethod = FeatureV2ServiceGrpc.getMultiGetFeatureMethod) == null) {
+          FeatureV2ServiceGrpc.getMultiGetFeatureMethod = getMultiGetFeatureMethod =
+              io.grpc.MethodDescriptor.<com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest, com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse>newBuilder()
+              .setType(io.grpc.MethodDescriptor.MethodType.UNARY)
+              .setFullMethodName(generateFullMethodName(SERVICE_NAME, "MultiGetFeature"))
+              .setSampledToLocalTracing(true)
+              .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest.getDefaultInstance()))
+              .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse.getDefaultInstance()))
+              .setSchemaDescriptor(new FeatureV2ServiceMethodDescriptorSupplier("MultiGetFeature"))
+              .build();
+        }
+      }
+    }
+    return getMultiGetFeatureMethod;
+  }
+
+  /**
+   * Creates a new async stub that supports all call types for the service
+   */
+  public static FeatureV2ServiceStub newStub(io.grpc.Channel channel) {
+    io.grpc.stub.AbstractStub.StubFactory<FeatureV2ServiceStub> factory =
+      new io.grpc.stub.AbstractStub.StubFactory<FeatureV2ServiceStub>() {
+        @java.lang.Override
+        public FeatureV2ServiceStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+          return new FeatureV2ServiceStub(channel, callOptions);
+        }
+      };
+    return FeatureV2ServiceStub.newStub(factory, channel);
+  }
+
+  /**
+   * Creates a new blocking-style stub that supports unary and streaming output calls on the service
+   */
+  public static FeatureV2ServiceBlockingStub newBlockingStub(
+      io.grpc.Channel channel) {
+    io.grpc.stub.AbstractStub.StubFactory<FeatureV2ServiceBlockingStub> factory =
+      new io.grpc.stub.AbstractStub.StubFactory<FeatureV2ServiceBlockingStub>() {
+        @java.lang.Override
+        public FeatureV2ServiceBlockingStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+          return new FeatureV2ServiceBlockingStub(channel, callOptions);
+        }
+      };
+    return FeatureV2ServiceBlockingStub.newStub(factory, channel);
+  }
+
+  /**
+   * Creates a new ListenableFuture-style stub that supports unary calls on the service
+   */
+  public static FeatureV2ServiceFutureStub newFutureStub(
+      io.grpc.Channel channel) {
+    io.grpc.stub.AbstractStub.StubFactory<FeatureV2ServiceFutureStub> factory =
+      new io.grpc.stub.AbstractStub.StubFactory<FeatureV2ServiceFutureStub>() {
+        @java.lang.Override
+        public FeatureV2ServiceFutureStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+          return new FeatureV2ServiceFutureStub(channel, callOptions);
+        }
+      };
+    return FeatureV2ServiceFutureStub.newStub(factory, channel);
+  }
+
+  /**
+   */
+  public static abstract class FeatureV2ServiceImplBase implements io.grpc.BindableService {
+
+    /**
+     */
+    public void multiGetFeature(com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest request,
+        io.grpc.stub.StreamObserver<com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse> responseObserver) {
+      asyncUnimplementedUnaryCall(getMultiGetFeatureMethod(), responseObserver);
+    }
+
+    @java.lang.Override public final io.grpc.ServerServiceDefinition bindService() {
+      return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor())
+          .addMethod(
+            getMultiGetFeatureMethod(),
+            asyncUnaryCall(
+              new MethodHandlers<
+                com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest,
+                com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse>(
+                  this, METHODID_MULTI_GET_FEATURE)))
+          .build();
+    }
+  }
+
+  /**
+   */
+  public static final class FeatureV2ServiceStub extends io.grpc.stub.AbstractAsyncStub<FeatureV2ServiceStub> {
+    private FeatureV2ServiceStub(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      super(channel, callOptions);
+    }
+
+    @java.lang.Override
+    protected FeatureV2ServiceStub build(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      return new FeatureV2ServiceStub(channel, callOptions);
+    }
+
+    /**
+     */
+    public void multiGetFeature(com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest request,
+        io.grpc.stub.StreamObserver<com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse> responseObserver) {
+      asyncUnaryCall(
+          getChannel().newCall(getMultiGetFeatureMethod(), getCallOptions()), request, responseObserver);
+    }
+  }
+
+  /**
+   */
+  public static final class FeatureV2ServiceBlockingStub extends io.grpc.stub.AbstractBlockingStub<FeatureV2ServiceBlockingStub> {
+    private FeatureV2ServiceBlockingStub(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      super(channel, callOptions);
+    }
+
+    @java.lang.Override
+    protected FeatureV2ServiceBlockingStub build(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      return new FeatureV2ServiceBlockingStub(channel, callOptions);
+    }
+
+    /**
+     */
+    public com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse multiGetFeature(com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest request) {
+      return blockingUnaryCall(
+          getChannel(), getMultiGetFeatureMethod(), getCallOptions(), request);
+    }
+  }
+
+  /**
+   */
+  public static final class FeatureV2ServiceFutureStub extends io.grpc.stub.AbstractFutureStub<FeatureV2ServiceFutureStub> {
+    private FeatureV2ServiceFutureStub(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      super(channel, callOptions);
+    }
+
+    @java.lang.Override
+    protected FeatureV2ServiceFutureStub build(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      return new FeatureV2ServiceFutureStub(channel, callOptions);
+    }
+
+    /**
+     */
+    public com.google.common.util.concurrent.ListenableFuture<com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse> multiGetFeature(
+        com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest request) {
+      return futureUnaryCall(
+          getChannel().newCall(getMultiGetFeatureMethod(), getCallOptions()), request);
+    }
+  }
+
+  private static final int METHODID_MULTI_GET_FEATURE = 0;
+
+  private static final class MethodHandlers<Req, Resp> implements
+      io.grpc.stub.ServerCalls.UnaryMethod<Req, Resp>,
+      io.grpc.stub.ServerCalls.ServerStreamingMethod<Req, Resp>,
+      io.grpc.stub.ServerCalls.ClientStreamingMethod<Req, Resp>,
+      io.grpc.stub.ServerCalls.BidiStreamingMethod<Req, Resp> {
+    private final FeatureV2ServiceImplBase serviceImpl;
+    private final int methodId;
+
+    MethodHandlers(FeatureV2ServiceImplBase serviceImpl, int methodId) {
+      this.serviceImpl = serviceImpl;
+      this.methodId = methodId;
+    }
+
+    @java.lang.Override
+    @java.lang.SuppressWarnings("unchecked")
+    public void invoke(Req request, io.grpc.stub.StreamObserver<Resp> responseObserver) {
+      switch (methodId) {
+        case METHODID_MULTI_GET_FEATURE:
+          serviceImpl.multiGetFeature((com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest) request,
+              (io.grpc.stub.StreamObserver<com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse>) responseObserver);
+          break;
+        default:
+          throw new AssertionError();
+      }
+    }
+
+    @java.lang.Override
+    @java.lang.SuppressWarnings("unchecked")
+    public io.grpc.stub.StreamObserver<Req> invoke(
+        io.grpc.stub.StreamObserver<Resp> responseObserver) {
+      switch (methodId) {
+        default:
+          throw new AssertionError();
+      }
+    }
+  }
+
+  private static abstract class FeatureV2ServiceBaseDescriptorSupplier
+      implements io.grpc.protobuf.ProtoFileDescriptorSupplier, io.grpc.protobuf.ProtoServiceDescriptorSupplier {
+    FeatureV2ServiceBaseDescriptorSupplier() {}
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.FileDescriptor getFileDescriptor() {
+      return com.tzld.piaoquan.recommend.feature.model.feature.FeatureV2.getDescriptor();
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.ServiceDescriptor getServiceDescriptor() {
+      return getFileDescriptor().findServiceByName("FeatureV2Service");
+    }
+  }
+
+  private static final class FeatureV2ServiceFileDescriptorSupplier
+      extends FeatureV2ServiceBaseDescriptorSupplier {
+    FeatureV2ServiceFileDescriptorSupplier() {}
+  }
+
+  private static final class FeatureV2ServiceMethodDescriptorSupplier
+      extends FeatureV2ServiceBaseDescriptorSupplier
+      implements io.grpc.protobuf.ProtoMethodDescriptorSupplier {
+    private final String methodName;
+
+    FeatureV2ServiceMethodDescriptorSupplier(String methodName) {
+      this.methodName = methodName;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.MethodDescriptor getMethodDescriptor() {
+      return getServiceDescriptor().findMethodByName(methodName);
+    }
+  }
+
+  private static volatile io.grpc.ServiceDescriptor serviceDescriptor;
+
+  public static io.grpc.ServiceDescriptor getServiceDescriptor() {
+    io.grpc.ServiceDescriptor result = serviceDescriptor;
+    if (result == null) {
+      synchronized (FeatureV2ServiceGrpc.class) {
+        result = serviceDescriptor;
+        if (result == null) {
+          serviceDescriptor = result = io.grpc.ServiceDescriptor.newBuilder(SERVICE_NAME)
+              .setSchemaDescriptor(new FeatureV2ServiceFileDescriptorSupplier())
+              .addMethod(getMultiGetFeatureMethod())
+              .build();
+        }
+      }
+    }
+    return result;
+  }
+}

+ 770 - 0
recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/feature/model/feature/MultiGetFeatureRequest.java

@@ -0,0 +1,770 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/feature/featureV2.proto
+
+package com.tzld.piaoquan.recommend.feature.model.feature;
+
+/**
+ * Protobuf type {@code MultiGetFeatureRequest}
+ */
+public final class MultiGetFeatureRequest extends
+    com.google.protobuf.GeneratedMessageV3 implements
+    // @@protoc_insertion_point(message_implements:MultiGetFeatureRequest)
+    MultiGetFeatureRequestOrBuilder {
+private static final long serialVersionUID = 0L;
+  // Use MultiGetFeatureRequest.newBuilder() to construct.
+  private MultiGetFeatureRequest(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    super(builder);
+  }
+  private MultiGetFeatureRequest() {
+    featureKey_ = java.util.Collections.emptyList();
+  }
+
+  @java.lang.Override
+  @SuppressWarnings({"unused"})
+  protected java.lang.Object newInstance(
+      UnusedPrivateParameter unused) {
+    return new MultiGetFeatureRequest();
+  }
+
+  @java.lang.Override
+  public final com.google.protobuf.UnknownFieldSet
+  getUnknownFields() {
+    return this.unknownFields;
+  }
+  private MultiGetFeatureRequest(
+      com.google.protobuf.CodedInputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    this();
+    if (extensionRegistry == null) {
+      throw new java.lang.NullPointerException();
+    }
+    int mutable_bitField0_ = 0;
+    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 10: {
+            if (!((mutable_bitField0_ & 0x00000001) != 0)) {
+              featureKey_ = new java.util.ArrayList<com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto>();
+              mutable_bitField0_ |= 0x00000001;
+            }
+            featureKey_.add(
+                input.readMessage(com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto.parser(), extensionRegistry));
+            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 {
+      if (((mutable_bitField0_ & 0x00000001) != 0)) {
+        featureKey_ = java.util.Collections.unmodifiableList(featureKey_);
+      }
+      this.unknownFields = unknownFields.build();
+      makeExtensionsImmutable();
+    }
+  }
+  public static final com.google.protobuf.Descriptors.Descriptor
+      getDescriptor() {
+    return com.tzld.piaoquan.recommend.feature.model.feature.FeatureV2.internal_static_MultiGetFeatureRequest_descriptor;
+  }
+
+  @java.lang.Override
+  protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internalGetFieldAccessorTable() {
+    return com.tzld.piaoquan.recommend.feature.model.feature.FeatureV2.internal_static_MultiGetFeatureRequest_fieldAccessorTable
+        .ensureFieldAccessorsInitialized(
+            com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest.class, com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest.Builder.class);
+  }
+
+  public static final int FEATURE_KEY_FIELD_NUMBER = 1;
+  private java.util.List<com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto> featureKey_;
+  /**
+   * <code>repeated .FeatureKeyProto feature_key = 1;</code>
+   */
+  @java.lang.Override
+  public java.util.List<com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto> getFeatureKeyList() {
+    return featureKey_;
+  }
+  /**
+   * <code>repeated .FeatureKeyProto feature_key = 1;</code>
+   */
+  @java.lang.Override
+  public java.util.List<? extends com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProtoOrBuilder> 
+      getFeatureKeyOrBuilderList() {
+    return featureKey_;
+  }
+  /**
+   * <code>repeated .FeatureKeyProto feature_key = 1;</code>
+   */
+  @java.lang.Override
+  public int getFeatureKeyCount() {
+    return featureKey_.size();
+  }
+  /**
+   * <code>repeated .FeatureKeyProto feature_key = 1;</code>
+   */
+  @java.lang.Override
+  public com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto getFeatureKey(int index) {
+    return featureKey_.get(index);
+  }
+  /**
+   * <code>repeated .FeatureKeyProto feature_key = 1;</code>
+   */
+  @java.lang.Override
+  public com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProtoOrBuilder getFeatureKeyOrBuilder(
+      int index) {
+    return featureKey_.get(index);
+  }
+
+  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 {
+    for (int i = 0; i < featureKey_.size(); i++) {
+      output.writeMessage(1, featureKey_.get(i));
+    }
+    unknownFields.writeTo(output);
+  }
+
+  @java.lang.Override
+  public int getSerializedSize() {
+    int size = memoizedSize;
+    if (size != -1) return size;
+
+    size = 0;
+    for (int i = 0; i < featureKey_.size(); i++) {
+      size += com.google.protobuf.CodedOutputStream
+        .computeMessageSize(1, featureKey_.get(i));
+    }
+    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.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest)) {
+      return super.equals(obj);
+    }
+    com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest other = (com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest) obj;
+
+    if (!getFeatureKeyList()
+        .equals(other.getFeatureKeyList())) 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();
+    if (getFeatureKeyCount() > 0) {
+      hash = (37 * hash) + FEATURE_KEY_FIELD_NUMBER;
+      hash = (53 * hash) + getFeatureKeyList().hashCode();
+    }
+    hash = (29 * hash) + unknownFields.hashCode();
+    memoizedHashCode = hash;
+    return hash;
+  }
+
+  public static com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest parseFrom(
+      java.nio.ByteBuffer data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest parseFrom(
+      java.nio.ByteBuffer data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest parseFrom(
+      com.google.protobuf.ByteString data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest parseFrom(
+      com.google.protobuf.ByteString data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest parseFrom(byte[] data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest parseFrom(
+      byte[] data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest parseFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest 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.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest parseDelimitedFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest 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.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest parseFrom(
+      com.google.protobuf.CodedInputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest 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.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest 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;
+  }
+  /**
+   * Protobuf type {@code MultiGetFeatureRequest}
+   */
+  public static final class Builder extends
+      com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
+      // @@protoc_insertion_point(builder_implements:MultiGetFeatureRequest)
+      com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequestOrBuilder {
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return com.tzld.piaoquan.recommend.feature.model.feature.FeatureV2.internal_static_MultiGetFeatureRequest_descriptor;
+    }
+
+    @java.lang.Override
+    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return com.tzld.piaoquan.recommend.feature.model.feature.FeatureV2.internal_static_MultiGetFeatureRequest_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest.class, com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest.Builder.class);
+    }
+
+    // Construct using com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest.newBuilder()
+    private Builder() {
+      maybeForceBuilderInitialization();
+    }
+
+    private Builder(
+        com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+      super(parent);
+      maybeForceBuilderInitialization();
+    }
+    private void maybeForceBuilderInitialization() {
+      if (com.google.protobuf.GeneratedMessageV3
+              .alwaysUseFieldBuilders) {
+        getFeatureKeyFieldBuilder();
+      }
+    }
+    @java.lang.Override
+    public Builder clear() {
+      super.clear();
+      if (featureKeyBuilder_ == null) {
+        featureKey_ = java.util.Collections.emptyList();
+        bitField0_ = (bitField0_ & ~0x00000001);
+      } else {
+        featureKeyBuilder_.clear();
+      }
+      return this;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.Descriptor
+        getDescriptorForType() {
+      return com.tzld.piaoquan.recommend.feature.model.feature.FeatureV2.internal_static_MultiGetFeatureRequest_descriptor;
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest getDefaultInstanceForType() {
+      return com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest.getDefaultInstance();
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest build() {
+      com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest result = buildPartial();
+      if (!result.isInitialized()) {
+        throw newUninitializedMessageException(result);
+      }
+      return result;
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest buildPartial() {
+      com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest result = new com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest(this);
+      int from_bitField0_ = bitField0_;
+      if (featureKeyBuilder_ == null) {
+        if (((bitField0_ & 0x00000001) != 0)) {
+          featureKey_ = java.util.Collections.unmodifiableList(featureKey_);
+          bitField0_ = (bitField0_ & ~0x00000001);
+        }
+        result.featureKey_ = featureKey_;
+      } else {
+        result.featureKey_ = featureKeyBuilder_.build();
+      }
+      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.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest) {
+        return mergeFrom((com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest)other);
+      } else {
+        super.mergeFrom(other);
+        return this;
+      }
+    }
+
+    public Builder mergeFrom(com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest other) {
+      if (other == com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest.getDefaultInstance()) return this;
+      if (featureKeyBuilder_ == null) {
+        if (!other.featureKey_.isEmpty()) {
+          if (featureKey_.isEmpty()) {
+            featureKey_ = other.featureKey_;
+            bitField0_ = (bitField0_ & ~0x00000001);
+          } else {
+            ensureFeatureKeyIsMutable();
+            featureKey_.addAll(other.featureKey_);
+          }
+          onChanged();
+        }
+      } else {
+        if (!other.featureKey_.isEmpty()) {
+          if (featureKeyBuilder_.isEmpty()) {
+            featureKeyBuilder_.dispose();
+            featureKeyBuilder_ = null;
+            featureKey_ = other.featureKey_;
+            bitField0_ = (bitField0_ & ~0x00000001);
+            featureKeyBuilder_ = 
+              com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ?
+                 getFeatureKeyFieldBuilder() : null;
+          } else {
+            featureKeyBuilder_.addAllMessages(other.featureKey_);
+          }
+        }
+      }
+      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.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest parsedMessage = null;
+      try {
+        parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        parsedMessage = (com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest) e.getUnfinishedMessage();
+        throw e.unwrapIOException();
+      } finally {
+        if (parsedMessage != null) {
+          mergeFrom(parsedMessage);
+        }
+      }
+      return this;
+    }
+    private int bitField0_;
+
+    private java.util.List<com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto> featureKey_ =
+      java.util.Collections.emptyList();
+    private void ensureFeatureKeyIsMutable() {
+      if (!((bitField0_ & 0x00000001) != 0)) {
+        featureKey_ = new java.util.ArrayList<com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto>(featureKey_);
+        bitField0_ |= 0x00000001;
+       }
+    }
+
+    private com.google.protobuf.RepeatedFieldBuilderV3<
+        com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto, com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto.Builder, com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProtoOrBuilder> featureKeyBuilder_;
+
+    /**
+     * <code>repeated .FeatureKeyProto feature_key = 1;</code>
+     */
+    public java.util.List<com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto> getFeatureKeyList() {
+      if (featureKeyBuilder_ == null) {
+        return java.util.Collections.unmodifiableList(featureKey_);
+      } else {
+        return featureKeyBuilder_.getMessageList();
+      }
+    }
+    /**
+     * <code>repeated .FeatureKeyProto feature_key = 1;</code>
+     */
+    public int getFeatureKeyCount() {
+      if (featureKeyBuilder_ == null) {
+        return featureKey_.size();
+      } else {
+        return featureKeyBuilder_.getCount();
+      }
+    }
+    /**
+     * <code>repeated .FeatureKeyProto feature_key = 1;</code>
+     */
+    public com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto getFeatureKey(int index) {
+      if (featureKeyBuilder_ == null) {
+        return featureKey_.get(index);
+      } else {
+        return featureKeyBuilder_.getMessage(index);
+      }
+    }
+    /**
+     * <code>repeated .FeatureKeyProto feature_key = 1;</code>
+     */
+    public Builder setFeatureKey(
+        int index, com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto value) {
+      if (featureKeyBuilder_ == null) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        ensureFeatureKeyIsMutable();
+        featureKey_.set(index, value);
+        onChanged();
+      } else {
+        featureKeyBuilder_.setMessage(index, value);
+      }
+      return this;
+    }
+    /**
+     * <code>repeated .FeatureKeyProto feature_key = 1;</code>
+     */
+    public Builder setFeatureKey(
+        int index, com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto.Builder builderForValue) {
+      if (featureKeyBuilder_ == null) {
+        ensureFeatureKeyIsMutable();
+        featureKey_.set(index, builderForValue.build());
+        onChanged();
+      } else {
+        featureKeyBuilder_.setMessage(index, builderForValue.build());
+      }
+      return this;
+    }
+    /**
+     * <code>repeated .FeatureKeyProto feature_key = 1;</code>
+     */
+    public Builder addFeatureKey(com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto value) {
+      if (featureKeyBuilder_ == null) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        ensureFeatureKeyIsMutable();
+        featureKey_.add(value);
+        onChanged();
+      } else {
+        featureKeyBuilder_.addMessage(value);
+      }
+      return this;
+    }
+    /**
+     * <code>repeated .FeatureKeyProto feature_key = 1;</code>
+     */
+    public Builder addFeatureKey(
+        int index, com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto value) {
+      if (featureKeyBuilder_ == null) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        ensureFeatureKeyIsMutable();
+        featureKey_.add(index, value);
+        onChanged();
+      } else {
+        featureKeyBuilder_.addMessage(index, value);
+      }
+      return this;
+    }
+    /**
+     * <code>repeated .FeatureKeyProto feature_key = 1;</code>
+     */
+    public Builder addFeatureKey(
+        com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto.Builder builderForValue) {
+      if (featureKeyBuilder_ == null) {
+        ensureFeatureKeyIsMutable();
+        featureKey_.add(builderForValue.build());
+        onChanged();
+      } else {
+        featureKeyBuilder_.addMessage(builderForValue.build());
+      }
+      return this;
+    }
+    /**
+     * <code>repeated .FeatureKeyProto feature_key = 1;</code>
+     */
+    public Builder addFeatureKey(
+        int index, com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto.Builder builderForValue) {
+      if (featureKeyBuilder_ == null) {
+        ensureFeatureKeyIsMutable();
+        featureKey_.add(index, builderForValue.build());
+        onChanged();
+      } else {
+        featureKeyBuilder_.addMessage(index, builderForValue.build());
+      }
+      return this;
+    }
+    /**
+     * <code>repeated .FeatureKeyProto feature_key = 1;</code>
+     */
+    public Builder addAllFeatureKey(
+        java.lang.Iterable<? extends com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto> values) {
+      if (featureKeyBuilder_ == null) {
+        ensureFeatureKeyIsMutable();
+        com.google.protobuf.AbstractMessageLite.Builder.addAll(
+            values, featureKey_);
+        onChanged();
+      } else {
+        featureKeyBuilder_.addAllMessages(values);
+      }
+      return this;
+    }
+    /**
+     * <code>repeated .FeatureKeyProto feature_key = 1;</code>
+     */
+    public Builder clearFeatureKey() {
+      if (featureKeyBuilder_ == null) {
+        featureKey_ = java.util.Collections.emptyList();
+        bitField0_ = (bitField0_ & ~0x00000001);
+        onChanged();
+      } else {
+        featureKeyBuilder_.clear();
+      }
+      return this;
+    }
+    /**
+     * <code>repeated .FeatureKeyProto feature_key = 1;</code>
+     */
+    public Builder removeFeatureKey(int index) {
+      if (featureKeyBuilder_ == null) {
+        ensureFeatureKeyIsMutable();
+        featureKey_.remove(index);
+        onChanged();
+      } else {
+        featureKeyBuilder_.remove(index);
+      }
+      return this;
+    }
+    /**
+     * <code>repeated .FeatureKeyProto feature_key = 1;</code>
+     */
+    public com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto.Builder getFeatureKeyBuilder(
+        int index) {
+      return getFeatureKeyFieldBuilder().getBuilder(index);
+    }
+    /**
+     * <code>repeated .FeatureKeyProto feature_key = 1;</code>
+     */
+    public com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProtoOrBuilder getFeatureKeyOrBuilder(
+        int index) {
+      if (featureKeyBuilder_ == null) {
+        return featureKey_.get(index);  } else {
+        return featureKeyBuilder_.getMessageOrBuilder(index);
+      }
+    }
+    /**
+     * <code>repeated .FeatureKeyProto feature_key = 1;</code>
+     */
+    public java.util.List<? extends com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProtoOrBuilder> 
+         getFeatureKeyOrBuilderList() {
+      if (featureKeyBuilder_ != null) {
+        return featureKeyBuilder_.getMessageOrBuilderList();
+      } else {
+        return java.util.Collections.unmodifiableList(featureKey_);
+      }
+    }
+    /**
+     * <code>repeated .FeatureKeyProto feature_key = 1;</code>
+     */
+    public com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto.Builder addFeatureKeyBuilder() {
+      return getFeatureKeyFieldBuilder().addBuilder(
+          com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto.getDefaultInstance());
+    }
+    /**
+     * <code>repeated .FeatureKeyProto feature_key = 1;</code>
+     */
+    public com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto.Builder addFeatureKeyBuilder(
+        int index) {
+      return getFeatureKeyFieldBuilder().addBuilder(
+          index, com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto.getDefaultInstance());
+    }
+    /**
+     * <code>repeated .FeatureKeyProto feature_key = 1;</code>
+     */
+    public java.util.List<com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto.Builder> 
+         getFeatureKeyBuilderList() {
+      return getFeatureKeyFieldBuilder().getBuilderList();
+    }
+    private com.google.protobuf.RepeatedFieldBuilderV3<
+        com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto, com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto.Builder, com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProtoOrBuilder> 
+        getFeatureKeyFieldBuilder() {
+      if (featureKeyBuilder_ == null) {
+        featureKeyBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3<
+            com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto, com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto.Builder, com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProtoOrBuilder>(
+                featureKey_,
+                ((bitField0_ & 0x00000001) != 0),
+                getParentForChildren(),
+                isClean());
+        featureKey_ = null;
+      }
+      return featureKeyBuilder_;
+    }
+    @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:MultiGetFeatureRequest)
+  }
+
+  // @@protoc_insertion_point(class_scope:MultiGetFeatureRequest)
+  private static final com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest DEFAULT_INSTANCE;
+  static {
+    DEFAULT_INSTANCE = new com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest();
+  }
+
+  public static com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest getDefaultInstance() {
+    return DEFAULT_INSTANCE;
+  }
+
+  private static final com.google.protobuf.Parser<MultiGetFeatureRequest>
+      PARSER = new com.google.protobuf.AbstractParser<MultiGetFeatureRequest>() {
+    @java.lang.Override
+    public MultiGetFeatureRequest parsePartialFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return new MultiGetFeatureRequest(input, extensionRegistry);
+    }
+  };
+
+  public static com.google.protobuf.Parser<MultiGetFeatureRequest> parser() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.google.protobuf.Parser<MultiGetFeatureRequest> getParserForType() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest getDefaultInstanceForType() {
+    return DEFAULT_INSTANCE;
+  }
+
+}
+

+ 33 - 0
recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/feature/model/feature/MultiGetFeatureRequestOrBuilder.java

@@ -0,0 +1,33 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/feature/featureV2.proto
+
+package com.tzld.piaoquan.recommend.feature.model.feature;
+
+public interface MultiGetFeatureRequestOrBuilder extends
+    // @@protoc_insertion_point(interface_extends:MultiGetFeatureRequest)
+    com.google.protobuf.MessageOrBuilder {
+
+  /**
+   * <code>repeated .FeatureKeyProto feature_key = 1;</code>
+   */
+  java.util.List<com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto> 
+      getFeatureKeyList();
+  /**
+   * <code>repeated .FeatureKeyProto feature_key = 1;</code>
+   */
+  com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto getFeatureKey(int index);
+  /**
+   * <code>repeated .FeatureKeyProto feature_key = 1;</code>
+   */
+  int getFeatureKeyCount();
+  /**
+   * <code>repeated .FeatureKeyProto feature_key = 1;</code>
+   */
+  java.util.List<? extends com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProtoOrBuilder> 
+      getFeatureKeyOrBuilderList();
+  /**
+   * <code>repeated .FeatureKeyProto feature_key = 1;</code>
+   */
+  com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProtoOrBuilder getFeatureKeyOrBuilder(
+      int index);
+}

+ 893 - 0
recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/feature/model/feature/MultiGetFeatureResponse.java

@@ -0,0 +1,893 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/feature/featureV2.proto
+
+package com.tzld.piaoquan.recommend.feature.model.feature;
+
+/**
+ * Protobuf type {@code MultiGetFeatureResponse}
+ */
+public final class MultiGetFeatureResponse extends
+    com.google.protobuf.GeneratedMessageV3 implements
+    // @@protoc_insertion_point(message_implements:MultiGetFeatureResponse)
+    MultiGetFeatureResponseOrBuilder {
+private static final long serialVersionUID = 0L;
+  // Use MultiGetFeatureResponse.newBuilder() to construct.
+  private MultiGetFeatureResponse(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    super(builder);
+  }
+  private MultiGetFeatureResponse() {
+  }
+
+  @java.lang.Override
+  @SuppressWarnings({"unused"})
+  protected java.lang.Object newInstance(
+      UnusedPrivateParameter unused) {
+    return new MultiGetFeatureResponse();
+  }
+
+  @java.lang.Override
+  public final com.google.protobuf.UnknownFieldSet
+  getUnknownFields() {
+    return this.unknownFields;
+  }
+  private MultiGetFeatureResponse(
+      com.google.protobuf.CodedInputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    this();
+    if (extensionRegistry == null) {
+      throw new java.lang.NullPointerException();
+    }
+    int mutable_bitField0_ = 0;
+    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 10: {
+            com.tzld.piaoquan.recommend.feature.model.common.Result.Builder subBuilder = null;
+            if (result_ != null) {
+              subBuilder = result_.toBuilder();
+            }
+            result_ = input.readMessage(com.tzld.piaoquan.recommend.feature.model.common.Result.parser(), extensionRegistry);
+            if (subBuilder != null) {
+              subBuilder.mergeFrom(result_);
+              result_ = subBuilder.buildPartial();
+            }
+
+            break;
+          }
+          case 18: {
+            if (!((mutable_bitField0_ & 0x00000001) != 0)) {
+              feature_ = com.google.protobuf.MapField.newMapField(
+                  FeatureDefaultEntryHolder.defaultEntry);
+              mutable_bitField0_ |= 0x00000001;
+            }
+            com.google.protobuf.MapEntry<java.lang.String, java.lang.String>
+            feature__ = input.readMessage(
+                FeatureDefaultEntryHolder.defaultEntry.getParserForType(), extensionRegistry);
+            feature_.getMutableMap().put(
+                feature__.getKey(), feature__.getValue());
+            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.tzld.piaoquan.recommend.feature.model.feature.FeatureV2.internal_static_MultiGetFeatureResponse_descriptor;
+  }
+
+  @SuppressWarnings({"rawtypes"})
+  @java.lang.Override
+  protected com.google.protobuf.MapField internalGetMapField(
+      int number) {
+    switch (number) {
+      case 2:
+        return internalGetFeature();
+      default:
+        throw new RuntimeException(
+            "Invalid map field number: " + number);
+    }
+  }
+  @java.lang.Override
+  protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internalGetFieldAccessorTable() {
+    return com.tzld.piaoquan.recommend.feature.model.feature.FeatureV2.internal_static_MultiGetFeatureResponse_fieldAccessorTable
+        .ensureFieldAccessorsInitialized(
+            com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse.class, com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse.Builder.class);
+  }
+
+  public static final int RESULT_FIELD_NUMBER = 1;
+  private com.tzld.piaoquan.recommend.feature.model.common.Result result_;
+  /**
+   * <code>.Result result = 1;</code>
+   * @return Whether the result field is set.
+   */
+  @java.lang.Override
+  public boolean hasResult() {
+    return result_ != null;
+  }
+  /**
+   * <code>.Result result = 1;</code>
+   * @return The result.
+   */
+  @java.lang.Override
+  public com.tzld.piaoquan.recommend.feature.model.common.Result getResult() {
+    return result_ == null ? com.tzld.piaoquan.recommend.feature.model.common.Result.getDefaultInstance() : result_;
+  }
+  /**
+   * <code>.Result result = 1;</code>
+   */
+  @java.lang.Override
+  public com.tzld.piaoquan.recommend.feature.model.common.ResultOrBuilder getResultOrBuilder() {
+    return getResult();
+  }
+
+  public static final int FEATURE_FIELD_NUMBER = 2;
+  private static final class FeatureDefaultEntryHolder {
+    static final com.google.protobuf.MapEntry<
+        java.lang.String, java.lang.String> defaultEntry =
+            com.google.protobuf.MapEntry
+            .<java.lang.String, java.lang.String>newDefaultInstance(
+                com.tzld.piaoquan.recommend.feature.model.feature.FeatureV2.internal_static_MultiGetFeatureResponse_FeatureEntry_descriptor, 
+                com.google.protobuf.WireFormat.FieldType.STRING,
+                "",
+                com.google.protobuf.WireFormat.FieldType.STRING,
+                "");
+  }
+  private com.google.protobuf.MapField<
+      java.lang.String, java.lang.String> feature_;
+  private com.google.protobuf.MapField<java.lang.String, java.lang.String>
+  internalGetFeature() {
+    if (feature_ == null) {
+      return com.google.protobuf.MapField.emptyMapField(
+          FeatureDefaultEntryHolder.defaultEntry);
+    }
+    return feature_;
+  }
+
+  public int getFeatureCount() {
+    return internalGetFeature().getMap().size();
+  }
+  /**
+   * <code>map&lt;string, string&gt; feature = 2;</code>
+   */
+
+  @java.lang.Override
+  public boolean containsFeature(
+      java.lang.String key) {
+    if (key == null) { throw new java.lang.NullPointerException(); }
+    return internalGetFeature().getMap().containsKey(key);
+  }
+  /**
+   * Use {@link #getFeatureMap()} instead.
+   */
+  @java.lang.Override
+  @java.lang.Deprecated
+  public java.util.Map<java.lang.String, java.lang.String> getFeature() {
+    return getFeatureMap();
+  }
+  /**
+   * <code>map&lt;string, string&gt; feature = 2;</code>
+   */
+  @java.lang.Override
+
+  public java.util.Map<java.lang.String, java.lang.String> getFeatureMap() {
+    return internalGetFeature().getMap();
+  }
+  /**
+   * <code>map&lt;string, string&gt; feature = 2;</code>
+   */
+  @java.lang.Override
+
+  public java.lang.String getFeatureOrDefault(
+      java.lang.String key,
+      java.lang.String defaultValue) {
+    if (key == null) { throw new java.lang.NullPointerException(); }
+    java.util.Map<java.lang.String, java.lang.String> map =
+        internalGetFeature().getMap();
+    return map.containsKey(key) ? map.get(key) : defaultValue;
+  }
+  /**
+   * <code>map&lt;string, string&gt; feature = 2;</code>
+   */
+  @java.lang.Override
+
+  public java.lang.String getFeatureOrThrow(
+      java.lang.String key) {
+    if (key == null) { throw new java.lang.NullPointerException(); }
+    java.util.Map<java.lang.String, java.lang.String> map =
+        internalGetFeature().getMap();
+    if (!map.containsKey(key)) {
+      throw new java.lang.IllegalArgumentException();
+    }
+    return map.get(key);
+  }
+
+  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 (result_ != null) {
+      output.writeMessage(1, getResult());
+    }
+    com.google.protobuf.GeneratedMessageV3
+      .serializeStringMapTo(
+        output,
+        internalGetFeature(),
+        FeatureDefaultEntryHolder.defaultEntry,
+        2);
+    unknownFields.writeTo(output);
+  }
+
+  @java.lang.Override
+  public int getSerializedSize() {
+    int size = memoizedSize;
+    if (size != -1) return size;
+
+    size = 0;
+    if (result_ != null) {
+      size += com.google.protobuf.CodedOutputStream
+        .computeMessageSize(1, getResult());
+    }
+    for (java.util.Map.Entry<java.lang.String, java.lang.String> entry
+         : internalGetFeature().getMap().entrySet()) {
+      com.google.protobuf.MapEntry<java.lang.String, java.lang.String>
+      feature__ = FeatureDefaultEntryHolder.defaultEntry.newBuilderForType()
+          .setKey(entry.getKey())
+          .setValue(entry.getValue())
+          .build();
+      size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(2, feature__);
+    }
+    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.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse)) {
+      return super.equals(obj);
+    }
+    com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse other = (com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse) obj;
+
+    if (hasResult() != other.hasResult()) return false;
+    if (hasResult()) {
+      if (!getResult()
+          .equals(other.getResult())) return false;
+    }
+    if (!internalGetFeature().equals(
+        other.internalGetFeature())) 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();
+    if (hasResult()) {
+      hash = (37 * hash) + RESULT_FIELD_NUMBER;
+      hash = (53 * hash) + getResult().hashCode();
+    }
+    if (!internalGetFeature().getMap().isEmpty()) {
+      hash = (37 * hash) + FEATURE_FIELD_NUMBER;
+      hash = (53 * hash) + internalGetFeature().hashCode();
+    }
+    hash = (29 * hash) + unknownFields.hashCode();
+    memoizedHashCode = hash;
+    return hash;
+  }
+
+  public static com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse parseFrom(
+      java.nio.ByteBuffer data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse parseFrom(
+      java.nio.ByteBuffer data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse parseFrom(
+      com.google.protobuf.ByteString data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse parseFrom(
+      com.google.protobuf.ByteString data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse parseFrom(byte[] data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse parseFrom(
+      byte[] data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse parseFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse 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.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse parseDelimitedFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse 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.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse parseFrom(
+      com.google.protobuf.CodedInputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse 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.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse 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;
+  }
+  /**
+   * Protobuf type {@code MultiGetFeatureResponse}
+   */
+  public static final class Builder extends
+      com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
+      // @@protoc_insertion_point(builder_implements:MultiGetFeatureResponse)
+      com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponseOrBuilder {
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return com.tzld.piaoquan.recommend.feature.model.feature.FeatureV2.internal_static_MultiGetFeatureResponse_descriptor;
+    }
+
+    @SuppressWarnings({"rawtypes"})
+    protected com.google.protobuf.MapField internalGetMapField(
+        int number) {
+      switch (number) {
+        case 2:
+          return internalGetFeature();
+        default:
+          throw new RuntimeException(
+              "Invalid map field number: " + number);
+      }
+    }
+    @SuppressWarnings({"rawtypes"})
+    protected com.google.protobuf.MapField internalGetMutableMapField(
+        int number) {
+      switch (number) {
+        case 2:
+          return internalGetMutableFeature();
+        default:
+          throw new RuntimeException(
+              "Invalid map field number: " + number);
+      }
+    }
+    @java.lang.Override
+    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return com.tzld.piaoquan.recommend.feature.model.feature.FeatureV2.internal_static_MultiGetFeatureResponse_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse.class, com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse.Builder.class);
+    }
+
+    // Construct using com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse.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();
+      if (resultBuilder_ == null) {
+        result_ = null;
+      } else {
+        result_ = null;
+        resultBuilder_ = null;
+      }
+      internalGetMutableFeature().clear();
+      return this;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.Descriptor
+        getDescriptorForType() {
+      return com.tzld.piaoquan.recommend.feature.model.feature.FeatureV2.internal_static_MultiGetFeatureResponse_descriptor;
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse getDefaultInstanceForType() {
+      return com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse.getDefaultInstance();
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse build() {
+      com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse result = buildPartial();
+      if (!result.isInitialized()) {
+        throw newUninitializedMessageException(result);
+      }
+      return result;
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse buildPartial() {
+      com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse result = new com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse(this);
+      int from_bitField0_ = bitField0_;
+      if (resultBuilder_ == null) {
+        result.result_ = result_;
+      } else {
+        result.result_ = resultBuilder_.build();
+      }
+      result.feature_ = internalGetFeature();
+      result.feature_.makeImmutable();
+      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.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse) {
+        return mergeFrom((com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse)other);
+      } else {
+        super.mergeFrom(other);
+        return this;
+      }
+    }
+
+    public Builder mergeFrom(com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse other) {
+      if (other == com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse.getDefaultInstance()) return this;
+      if (other.hasResult()) {
+        mergeResult(other.getResult());
+      }
+      internalGetMutableFeature().mergeFrom(
+          other.internalGetFeature());
+      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.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse parsedMessage = null;
+      try {
+        parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        parsedMessage = (com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse) e.getUnfinishedMessage();
+        throw e.unwrapIOException();
+      } finally {
+        if (parsedMessage != null) {
+          mergeFrom(parsedMessage);
+        }
+      }
+      return this;
+    }
+    private int bitField0_;
+
+    private com.tzld.piaoquan.recommend.feature.model.common.Result result_;
+    private com.google.protobuf.SingleFieldBuilderV3<
+        com.tzld.piaoquan.recommend.feature.model.common.Result, com.tzld.piaoquan.recommend.feature.model.common.Result.Builder, com.tzld.piaoquan.recommend.feature.model.common.ResultOrBuilder> resultBuilder_;
+    /**
+     * <code>.Result result = 1;</code>
+     * @return Whether the result field is set.
+     */
+    public boolean hasResult() {
+      return resultBuilder_ != null || result_ != null;
+    }
+    /**
+     * <code>.Result result = 1;</code>
+     * @return The result.
+     */
+    public com.tzld.piaoquan.recommend.feature.model.common.Result getResult() {
+      if (resultBuilder_ == null) {
+        return result_ == null ? com.tzld.piaoquan.recommend.feature.model.common.Result.getDefaultInstance() : result_;
+      } else {
+        return resultBuilder_.getMessage();
+      }
+    }
+    /**
+     * <code>.Result result = 1;</code>
+     */
+    public Builder setResult(com.tzld.piaoquan.recommend.feature.model.common.Result value) {
+      if (resultBuilder_ == null) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        result_ = value;
+        onChanged();
+      } else {
+        resultBuilder_.setMessage(value);
+      }
+
+      return this;
+    }
+    /**
+     * <code>.Result result = 1;</code>
+     */
+    public Builder setResult(
+        com.tzld.piaoquan.recommend.feature.model.common.Result.Builder builderForValue) {
+      if (resultBuilder_ == null) {
+        result_ = builderForValue.build();
+        onChanged();
+      } else {
+        resultBuilder_.setMessage(builderForValue.build());
+      }
+
+      return this;
+    }
+    /**
+     * <code>.Result result = 1;</code>
+     */
+    public Builder mergeResult(com.tzld.piaoquan.recommend.feature.model.common.Result value) {
+      if (resultBuilder_ == null) {
+        if (result_ != null) {
+          result_ =
+            com.tzld.piaoquan.recommend.feature.model.common.Result.newBuilder(result_).mergeFrom(value).buildPartial();
+        } else {
+          result_ = value;
+        }
+        onChanged();
+      } else {
+        resultBuilder_.mergeFrom(value);
+      }
+
+      return this;
+    }
+    /**
+     * <code>.Result result = 1;</code>
+     */
+    public Builder clearResult() {
+      if (resultBuilder_ == null) {
+        result_ = null;
+        onChanged();
+      } else {
+        result_ = null;
+        resultBuilder_ = null;
+      }
+
+      return this;
+    }
+    /**
+     * <code>.Result result = 1;</code>
+     */
+    public com.tzld.piaoquan.recommend.feature.model.common.Result.Builder getResultBuilder() {
+      
+      onChanged();
+      return getResultFieldBuilder().getBuilder();
+    }
+    /**
+     * <code>.Result result = 1;</code>
+     */
+    public com.tzld.piaoquan.recommend.feature.model.common.ResultOrBuilder getResultOrBuilder() {
+      if (resultBuilder_ != null) {
+        return resultBuilder_.getMessageOrBuilder();
+      } else {
+        return result_ == null ?
+            com.tzld.piaoquan.recommend.feature.model.common.Result.getDefaultInstance() : result_;
+      }
+    }
+    /**
+     * <code>.Result result = 1;</code>
+     */
+    private com.google.protobuf.SingleFieldBuilderV3<
+        com.tzld.piaoquan.recommend.feature.model.common.Result, com.tzld.piaoquan.recommend.feature.model.common.Result.Builder, com.tzld.piaoquan.recommend.feature.model.common.ResultOrBuilder> 
+        getResultFieldBuilder() {
+      if (resultBuilder_ == null) {
+        resultBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
+            com.tzld.piaoquan.recommend.feature.model.common.Result, com.tzld.piaoquan.recommend.feature.model.common.Result.Builder, com.tzld.piaoquan.recommend.feature.model.common.ResultOrBuilder>(
+                getResult(),
+                getParentForChildren(),
+                isClean());
+        result_ = null;
+      }
+      return resultBuilder_;
+    }
+
+    private com.google.protobuf.MapField<
+        java.lang.String, java.lang.String> feature_;
+    private com.google.protobuf.MapField<java.lang.String, java.lang.String>
+    internalGetFeature() {
+      if (feature_ == null) {
+        return com.google.protobuf.MapField.emptyMapField(
+            FeatureDefaultEntryHolder.defaultEntry);
+      }
+      return feature_;
+    }
+    private com.google.protobuf.MapField<java.lang.String, java.lang.String>
+    internalGetMutableFeature() {
+      onChanged();;
+      if (feature_ == null) {
+        feature_ = com.google.protobuf.MapField.newMapField(
+            FeatureDefaultEntryHolder.defaultEntry);
+      }
+      if (!feature_.isMutable()) {
+        feature_ = feature_.copy();
+      }
+      return feature_;
+    }
+
+    public int getFeatureCount() {
+      return internalGetFeature().getMap().size();
+    }
+    /**
+     * <code>map&lt;string, string&gt; feature = 2;</code>
+     */
+
+    @java.lang.Override
+    public boolean containsFeature(
+        java.lang.String key) {
+      if (key == null) { throw new java.lang.NullPointerException(); }
+      return internalGetFeature().getMap().containsKey(key);
+    }
+    /**
+     * Use {@link #getFeatureMap()} instead.
+     */
+    @java.lang.Override
+    @java.lang.Deprecated
+    public java.util.Map<java.lang.String, java.lang.String> getFeature() {
+      return getFeatureMap();
+    }
+    /**
+     * <code>map&lt;string, string&gt; feature = 2;</code>
+     */
+    @java.lang.Override
+
+    public java.util.Map<java.lang.String, java.lang.String> getFeatureMap() {
+      return internalGetFeature().getMap();
+    }
+    /**
+     * <code>map&lt;string, string&gt; feature = 2;</code>
+     */
+    @java.lang.Override
+
+    public java.lang.String getFeatureOrDefault(
+        java.lang.String key,
+        java.lang.String defaultValue) {
+      if (key == null) { throw new java.lang.NullPointerException(); }
+      java.util.Map<java.lang.String, java.lang.String> map =
+          internalGetFeature().getMap();
+      return map.containsKey(key) ? map.get(key) : defaultValue;
+    }
+    /**
+     * <code>map&lt;string, string&gt; feature = 2;</code>
+     */
+    @java.lang.Override
+
+    public java.lang.String getFeatureOrThrow(
+        java.lang.String key) {
+      if (key == null) { throw new java.lang.NullPointerException(); }
+      java.util.Map<java.lang.String, java.lang.String> map =
+          internalGetFeature().getMap();
+      if (!map.containsKey(key)) {
+        throw new java.lang.IllegalArgumentException();
+      }
+      return map.get(key);
+    }
+
+    public Builder clearFeature() {
+      internalGetMutableFeature().getMutableMap()
+          .clear();
+      return this;
+    }
+    /**
+     * <code>map&lt;string, string&gt; feature = 2;</code>
+     */
+
+    public Builder removeFeature(
+        java.lang.String key) {
+      if (key == null) { throw new java.lang.NullPointerException(); }
+      internalGetMutableFeature().getMutableMap()
+          .remove(key);
+      return this;
+    }
+    /**
+     * Use alternate mutation accessors instead.
+     */
+    @java.lang.Deprecated
+    public java.util.Map<java.lang.String, java.lang.String>
+    getMutableFeature() {
+      return internalGetMutableFeature().getMutableMap();
+    }
+    /**
+     * <code>map&lt;string, string&gt; feature = 2;</code>
+     */
+    public Builder putFeature(
+        java.lang.String key,
+        java.lang.String value) {
+      if (key == null) { throw new java.lang.NullPointerException(); }
+      if (value == null) { throw new java.lang.NullPointerException(); }
+      internalGetMutableFeature().getMutableMap()
+          .put(key, value);
+      return this;
+    }
+    /**
+     * <code>map&lt;string, string&gt; feature = 2;</code>
+     */
+
+    public Builder putAllFeature(
+        java.util.Map<java.lang.String, java.lang.String> values) {
+      internalGetMutableFeature().getMutableMap()
+          .putAll(values);
+      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:MultiGetFeatureResponse)
+  }
+
+  // @@protoc_insertion_point(class_scope:MultiGetFeatureResponse)
+  private static final com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse DEFAULT_INSTANCE;
+  static {
+    DEFAULT_INSTANCE = new com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse();
+  }
+
+  public static com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse getDefaultInstance() {
+    return DEFAULT_INSTANCE;
+  }
+
+  private static final com.google.protobuf.Parser<MultiGetFeatureResponse>
+      PARSER = new com.google.protobuf.AbstractParser<MultiGetFeatureResponse>() {
+    @java.lang.Override
+    public MultiGetFeatureResponse parsePartialFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return new MultiGetFeatureResponse(input, extensionRegistry);
+    }
+  };
+
+  public static com.google.protobuf.Parser<MultiGetFeatureResponse> parser() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.google.protobuf.Parser<MultiGetFeatureResponse> getParserForType() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse getDefaultInstanceForType() {
+    return DEFAULT_INSTANCE;
+  }
+
+}
+

+ 58 - 0
recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/feature/model/feature/MultiGetFeatureResponseOrBuilder.java

@@ -0,0 +1,58 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/feature/featureV2.proto
+
+package com.tzld.piaoquan.recommend.feature.model.feature;
+
+public interface MultiGetFeatureResponseOrBuilder extends
+    // @@protoc_insertion_point(interface_extends:MultiGetFeatureResponse)
+    com.google.protobuf.MessageOrBuilder {
+
+  /**
+   * <code>.Result result = 1;</code>
+   * @return Whether the result field is set.
+   */
+  boolean hasResult();
+  /**
+   * <code>.Result result = 1;</code>
+   * @return The result.
+   */
+  com.tzld.piaoquan.recommend.feature.model.common.Result getResult();
+  /**
+   * <code>.Result result = 1;</code>
+   */
+  com.tzld.piaoquan.recommend.feature.model.common.ResultOrBuilder getResultOrBuilder();
+
+  /**
+   * <code>map&lt;string, string&gt; feature = 2;</code>
+   */
+  int getFeatureCount();
+  /**
+   * <code>map&lt;string, string&gt; feature = 2;</code>
+   */
+  boolean containsFeature(
+      java.lang.String key);
+  /**
+   * Use {@link #getFeatureMap()} instead.
+   */
+  @java.lang.Deprecated
+  java.util.Map<java.lang.String, java.lang.String>
+  getFeature();
+  /**
+   * <code>map&lt;string, string&gt; feature = 2;</code>
+   */
+  java.util.Map<java.lang.String, java.lang.String>
+  getFeatureMap();
+  /**
+   * <code>map&lt;string, string&gt; feature = 2;</code>
+   */
+
+  java.lang.String getFeatureOrDefault(
+      java.lang.String key,
+      java.lang.String defaultValue);
+  /**
+   * <code>map&lt;string, string&gt; feature = 2;</code>
+   */
+
+  java.lang.String getFeatureOrThrow(
+      java.lang.String key);
+}

+ 28 - 0
recommend-feature-client/src/main/proto/com/tzld/piaoquan/recommend/feature/featureV2.proto

@@ -0,0 +1,28 @@
+syntax = "proto3";
+
+import "google/protobuf/any.proto";
+import "com/tzld/piaoquan/recommend/feature/common.proto";
+
+option java_multiple_files = true;
+option java_package = "com.tzld.piaoquan.recommend.feature.model.feature";
+option java_generic_services = true;
+
+message MultiGetFeatureRequest {
+  repeated FeatureKeyProto feature_key = 1;
+}
+
+message FeatureKeyProto {
+  string unique_key = 1;
+  string table_name = 2;
+  map<string, string> field_value = 3;
+}
+
+message MultiGetFeatureResponse {
+  Result result = 1;
+  map<string, string> feature = 2;
+}
+
+
+service FeatureV2Service {
+  rpc MultiGetFeature (MultiGetFeatureRequest) returns (MultiGetFeatureResponse);
+}

+ 8 - 8
recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/common/ThreadPoolFactory.java

@@ -12,26 +12,26 @@ import java.util.concurrent.TimeUnit;
  */
 public final class ThreadPoolFactory {
     private final static ExecutorService DEFAULT = new CommonThreadPoolExecutor(
-            32,
-            32,
+            128,
+            128,
             0L, TimeUnit.SECONDS,
             new LinkedBlockingQueue<>(1000),
             new ThreadFactoryBuilder().setNameFormat("DEFAULT-%d").build(),
             new ThreadPoolExecutor.AbortPolicy());
-    public final static ExecutorService RECALL = new CommonThreadPoolExecutor(
-            128,
-            128,
+    private final static ExecutorService MULTI_GET_FEATURE = new CommonThreadPoolExecutor(
+            256,
+            256,
             0L, TimeUnit.SECONDS,
             new LinkedBlockingQueue<>(1000),
-            new ThreadFactoryBuilder().setNameFormat("RecallService-%d").build(),
+            new ThreadFactoryBuilder().setNameFormat("MultiGetFeaturePool-%d").build(),
             new ThreadPoolExecutor.AbortPolicy());
 
     public static ExecutorService defaultPool() {
         return DEFAULT;
     }
 
-    public static ExecutorService recallPool() {
-        return RECALL;
+    public static ExecutorService multiGetFeaturePool() {
+        return MULTI_GET_FEATURE;
     }
 
 }

+ 33 - 0
recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/grpcservice/FeatureV2GrpcService.java

@@ -0,0 +1,33 @@
+package com.tzld.piaoquan.recommend.feature.grpcservice;
+
+import com.tzld.piaoquan.recommend.feature.client.ProtobufUtils;
+import com.tzld.piaoquan.recommend.feature.model.feature.FeatureV2ServiceGrpc;
+import com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest;
+import com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse;
+import com.tzld.piaoquan.recommend.feature.service.FeatureV2Service;
+import io.grpc.stub.StreamObserver;
+import lombok.extern.slf4j.Slf4j;
+import net.devh.boot.grpc.server.service.GrpcService;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * @author dyp
+ */
+@GrpcService
+@Slf4j
+public class FeatureV2GrpcService extends FeatureV2ServiceGrpc.FeatureV2ServiceImplBase {
+
+    @Autowired
+    private FeatureV2Service featureV2Service;
+
+    @Override
+    public void multiGetFeature(MultiGetFeatureRequest request,
+                                StreamObserver<MultiGetFeatureResponse> responseObserver) {
+        log.info("FeatureV2GrpcService multiGetFeature request={}", ProtobufUtils.toJson(request));
+        MultiGetFeatureResponse response = featureV2Service.multiGetFeature(request);
+        log.info("FeatureV2GrpcService multiGetFeature response={}", ProtobufUtils.toJson(response));
+        responseObserver.onNext(response);
+        responseObserver.onCompleted();
+    }
+
+}

+ 70 - 0
recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/service/FeatureV2Service.java

@@ -0,0 +1,70 @@
+package com.tzld.piaoquan.recommend.feature.service;
+
+import com.ctrip.framework.apollo.spring.annotation.ApolloJsonValue;
+import com.google.common.base.Strings;
+import com.tzld.piaoquan.recommend.feature.model.common.Result;
+import com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto;
+import com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureRequest;
+import com.tzld.piaoquan.recommend.feature.model.feature.MultiGetFeatureResponse;
+import com.tzld.piaoquan.recommend.feature.util.CommonCollectionUtils;
+import com.tzld.piaoquan.recommend.feature.util.JSONUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author dyp
+ */
+@Service
+@Slf4j
+public class FeatureV2Service {
+    @Qualifier("redisTemplate")
+    @Autowired
+    private RedisTemplate<String, String> redisTemplate;
+
+    @ApolloJsonValue("${dts.config:}")
+    private Map<String, List<String>> tableFieldConfig;
+
+    public MultiGetFeatureResponse multiGetFeature(MultiGetFeatureRequest request) {
+        if (request.getFeatureKeyCount() == 0) {
+            return MultiGetFeatureResponse.newBuilder()
+                    .setResult(Result.newBuilder().setCode(1))
+                    .build();
+        }
+        // 目前都在一个Redis,所以放在一个list简化处理
+        List<String> redisKeys = CommonCollectionUtils.toList(request.getFeatureKeyList(), fk -> redisKey(fk));
+        List<String> values = redisTemplate.opsForValue().multiGet(redisKeys);
+
+        log.info("feature key {} value {}", JSONUtils.toJson(redisKeys), JSONUtils.toJson(values));
+
+        MultiGetFeatureResponse.Builder builder = MultiGetFeatureResponse.newBuilder();
+        builder.setResult(Result.newBuilder().setCode(1));
+        for (int i = 0; i < request.getFeatureKeyCount(); i++) {
+            builder.putFeature(request.getFeatureKeyList().get(i).getUniqueKey(),
+                    Strings.nullToEmpty(values.get(i)));
+        }
+        return builder.build();
+    }
+
+    // Note:写入和读取的key生成规则应保持一致
+    private String redisKey(FeatureKeyProto fk) {
+        List<String> fields = tableFieldConfig.get(fk.getTableName());
+        if (CollectionUtils.isEmpty(fields)) {
+            return fk.getTableName();
+        }
+        StringBuilder sb = new StringBuilder(fk.getTableName());
+        for (String field : fields) {
+            sb.append(":");
+            sb.append(fk.getFieldValueMap().get(field));
+        }
+        return sb.toString();
+    }
+
+}

+ 1 - 1
recommend-feature-service/src/main/resources/application-dev.yml

@@ -18,7 +18,7 @@ eureka:
 
 spring:
   redis:
-    hostName: r-bp1pi8wyv6lzvgjy5z.redis.rds.aliyuncs.com
+    hostName: r-bp1wwqqkjqwwkxgbup.redis.rds.aliyuncs.com
     port: 6379
     password: Wqsd@2019
     timeout: 1000

+ 1 - 1
recommend-feature-service/src/main/resources/application-test.yml

@@ -14,7 +14,7 @@ eureka:
 
 spring:
   redis:
-    hostName: r-bp1pi8wyv6lzvgjy5z.redis.rds.aliyuncs.com
+    hostName: r-bp1wwqqkjqwwkxgbup.redis.rds.aliyuncs.com
     port: 6379
     password: Wqsd@2019
     timeout: 1000