Browse Source

model service

丁云鹏 5 months ago
parent
commit
8101f6c1b0
36 changed files with 4552 additions and 0 deletions
  1. 2 0
      pom.xml
  2. 33 0
      recommend-model-client/.gitignore
  3. 104 0
      recommend-model-client/pom.xml
  4. 41 0
      recommend-model-client/src/main/java/com/tzld/piaoquan/recommend/model/grpc/client/PredictClient.java
  5. 30 0
      recommend-model-client/src/main/java/com/tzld/piaoquan/recommend/model/grpc/client/ProtobufUtils.java
  6. 69 0
      recommend-model-client/src/main/java/com/tzld/piaoquan/recommend/model/grpc/model/Predict.java
  7. 695 0
      recommend-model-client/src/main/java/com/tzld/piaoquan/recommend/model/grpc/model/PredictRequest.java
  8. 33 0
      recommend-model-client/src/main/java/com/tzld/piaoquan/recommend/model/grpc/model/PredictRequestOrBuilder.java
  9. 745 0
      recommend-model-client/src/main/java/com/tzld/piaoquan/recommend/model/grpc/model/PredictResponse.java
  10. 36 0
      recommend-model-client/src/main/java/com/tzld/piaoquan/recommend/model/grpc/model/PredictResponseOrBuilder.java
  11. 237 0
      recommend-model-client/src/main/java/com/tzld/piaoquan/recommend/model/grpc/model/PredictService.java
  12. 288 0
      recommend-model-client/src/main/java/com/tzld/piaoquan/recommend/model/grpc/model/PredictServiceGrpc.java
  13. 49 0
      recommend-model-client/src/main/java/com/tzld/piaoquan/recommend/model/grpc/model/common/Common.java
  14. 637 0
      recommend-model-client/src/main/java/com/tzld/piaoquan/recommend/model/grpc/model/common/Result.java
  15. 31 0
      recommend-model-client/src/main/java/com/tzld/piaoquan/recommend/model/grpc/model/common/ResultOrBuilder.java
  16. 9 0
      recommend-model-client/src/main/proto/com/tzld/piaoquan/recommend/model/common.proto
  17. 23 0
      recommend-model-client/src/main/proto/com/tzld/piaoquan/recommend/model/predict.proto
  18. 130 0
      recommend-model-service/pom.xml
  19. 27 0
      recommend-model-service/src/main/java/com/tzld/piaoquan/recommend/model/Application.java
  20. 40 0
      recommend-model-service/src/main/java/com/tzld/piaoquan/recommend/model/common/CommonThreadPoolExecutor.java
  21. 358 0
      recommend-model-service/src/main/java/com/tzld/piaoquan/recommend/model/common/RecommendLoghubAppender.java
  22. 37 0
      recommend-model-service/src/main/java/com/tzld/piaoquan/recommend/model/common/RecommendLoghubAppenderCallback.java
  23. 37 0
      recommend-model-service/src/main/java/com/tzld/piaoquan/recommend/model/common/ThreadPoolFactory.java
  24. 41 0
      recommend-model-service/src/main/java/com/tzld/piaoquan/recommend/model/util/CommonCollectionUtils.java
  25. 30 0
      recommend-model-service/src/main/java/com/tzld/piaoquan/recommend/model/util/DateUtils.java
  26. 96 0
      recommend-model-service/src/main/java/com/tzld/piaoquan/recommend/model/util/HttpClientFactory.java
  27. 38 0
      recommend-model-service/src/main/java/com/tzld/piaoquan/recommend/model/util/JSONUtils.java
  28. 48 0
      recommend-model-service/src/main/java/com/tzld/piaoquan/recommend/model/util/TraceUtils.java
  29. 18 0
      recommend-model-service/src/main/java/com/tzld/piaoquan/recommend/model/web/HealthCheckController.java
  30. 55 0
      recommend-model-service/src/main/resources/application-dev.yml
  31. 47 0
      recommend-model-service/src/main/resources/application-pre.yml
  32. 47 0
      recommend-model-service/src/main/resources/application-prod.yml
  33. 47 0
      recommend-model-service/src/main/resources/application-test.yml
  34. 16 0
      recommend-model-service/src/main/resources/application.yml
  35. 324 0
      recommend-model-service/src/main/resources/logback-spring.xml
  36. 54 0
      recommend-model-service/src/main/resources/mybatis-generator-config.xml

+ 2 - 0
pom.xml

@@ -16,7 +16,9 @@
     <description>recommend-model</description>
 
     <modules>
+        <module>recommend-model-client</module>
         <module>recommend-model-produce</module>
+        <module>recommend-model-service</module>
     </modules>
 
     <dependencies>

+ 33 - 0
recommend-model-client/.gitignore

@@ -0,0 +1,33 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/

+ 104 - 0
recommend-model-client/pom.xml

@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>recommend-model</artifactId>
+        <groupId>com.tzld.piaoquan</groupId>
+        <version>1.0.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>recommend-model-client</artifactId>
+    <version>1.0.0</version>
+
+    <dependencies>
+        <dependency>
+            <groupId>it.unimi.dsi</groupId>
+            <artifactId>fastutil</artifactId>
+            <version>7.0.12</version>
+        </dependency>
+        <dependency>
+            <groupId>net.devh</groupId>
+            <artifactId>grpc-client-spring-boot-starter</artifactId>
+            <version>2.9.0.RELEASE</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.protobuf</groupId>
+            <artifactId>protobuf-java</artifactId>
+            <version>3.12.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>1.16.8</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.protobuf</groupId>
+            <artifactId>protobuf-java-util</artifactId>
+            <version>3.6.0</version>
+            <exclusions>
+                <exclusion>
+                    <artifactId>protobuf-java</artifactId>
+                    <groupId>com.google.protobuf</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+    </dependencies>
+    <build>
+        <extensions>
+            <extension>
+                <groupId>kr.motd.maven</groupId>
+                <artifactId>os-maven-plugin</artifactId>
+                <version>1.6.0</version>
+            </extension>
+        </extensions>
+        <plugins>
+            <plugin>
+                <groupId>org.xolstice.maven.plugins</groupId>
+                <artifactId>protobuf-maven-plugin</artifactId>
+                <version>0.6.1</version>
+                <configuration>
+                    <protocArtifact>com.google.protobuf:protoc:3.12.0:exe:${os.detected.classifier}</protocArtifact>
+                    <pluginId>grpc-java</pluginId>
+                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.34.1:exe:${os.detected.classifier}</pluginArtifact>
+                    <!--设置grpc生成代码到指定路径-->
+                    <outputDirectory>${project.basedir}/src/main/java</outputDirectory>
+                    <!--生成代码前是否清空目录-->
+                    <clearOutputDirectory>false</clearOutputDirectory>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>compile</goal>
+                            <goal>compile-custom</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <!-- 设置多个源文件夹 -->
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <version>3.0.0</version>
+                <executions>
+                    <!-- 添加主源码目录 -->
+                    <execution>
+                        <id>add-source</id>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>add-source</goal>
+                        </goals>
+                        <configuration>
+                            <sources>
+                                <source>${project.basedir}/src/main/java</source>
+                            </sources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
+        </plugins>
+    </build>
+</project>

+ 41 - 0
recommend-model-client/src/main/java/com/tzld/piaoquan/recommend/model/grpc/client/PredictClient.java

@@ -0,0 +1,41 @@
+package com.tzld.piaoquan.recommend.model.grpc.client;
+
+import com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest;
+import com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse;
+import com.tzld.piaoquan.recommend.model.grpc.model.PredictServiceGrpc;
+import lombok.extern.slf4j.Slf4j;
+import net.devh.boot.grpc.client.inject.GrpcClient;
+import org.springframework.stereotype.Component;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author dyp
+ */
+@Component
+@Slf4j
+public class PredictClient {
+    @GrpcClient("recommend-model")
+    private PredictServiceGrpc.PredictServiceBlockingStub client;
+
+
+    public String predict(String modelName, String param) {
+        PredictRequest request = PredictRequest.newBuilder()
+                .setModelName("")
+                .setParam("")
+                .build();
+        PredictResponse response = client.predict(request);
+        if (response == null || !response.hasResult()) {
+            log.info("predict grpc error");
+            return null;
+        }
+        if (response.getResult().getCode() != 1) {
+            log.info("predict grpc code={}, msg={}", response.getResult().getCode(),
+                    response.getResult().getMessage());
+            return null;
+        }
+        return response.getData();
+    }
+
+}

+ 30 - 0
recommend-model-client/src/main/java/com/tzld/piaoquan/recommend/model/grpc/client/ProtobufUtils.java

@@ -0,0 +1,30 @@
+package com.tzld.piaoquan.recommend.model.grpc.client;
+
+import com.google.protobuf.InvalidProtocolBufferException;
+import com.google.protobuf.Message;
+import com.google.protobuf.MessageOrBuilder;
+import com.google.protobuf.util.JsonFormat;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * @author dyp
+ */
+@Slf4j
+public class ProtobufUtils {
+    public static String toJson(MessageOrBuilder message) {
+        if (message == null) {
+            return "";
+        }
+        try {
+            return JsonFormat.printer().includingDefaultValueFields().print(message);
+        } catch (InvalidProtocolBufferException e) {
+            log.error("ProtobufUtils toJson error", e);
+        }
+        return "";
+    }
+
+    public static void fromJson(String json, Message.Builder builder) throws InvalidProtocolBufferException {
+        JsonFormat.parser().merge(json, builder);
+    }
+
+}

+ 69 - 0
recommend-model-client/src/main/java/com/tzld/piaoquan/recommend/model/grpc/model/Predict.java

@@ -0,0 +1,69 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/model/predict.proto
+
+package com.tzld.piaoquan.recommend.model.grpc.model;
+
+public final class Predict {
+  private Predict() {}
+  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_PredictRequest_descriptor;
+  static final 
+    com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internal_static_PredictRequest_fieldAccessorTable;
+  static final com.google.protobuf.Descriptors.Descriptor
+    internal_static_PredictResponse_descriptor;
+  static final 
+    com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internal_static_PredictResponse_fieldAccessorTable;
+
+  public static com.google.protobuf.Descriptors.FileDescriptor
+      getDescriptor() {
+    return descriptor;
+  }
+  private static  com.google.protobuf.Descriptors.FileDescriptor
+      descriptor;
+  static {
+    java.lang.String[] descriptorData = {
+      "\n/com/tzld/piaoquan/recommend/model/pred" +
+      "ict.proto\032\031google/protobuf/any.proto\032.co" +
+      "m/tzld/piaoquan/recommend/model/common.p" +
+      "roto\"3\n\016PredictRequest\022\r\n\005param\030\001 \001(\t\022\022\n" +
+      "\nmodel_name\030\002 \001(\t\"8\n\017PredictResponse\022\027\n\006" +
+      "result\030\001 \001(\0132\007.Result\022\014\n\004data\030\002 \001(\t2>\n\016P" +
+      "redictService\022,\n\007predict\022\017.PredictReques" +
+      "t\032\020.PredictResponseB3\n,com.tzld.piaoquan" +
+      ".recommend.model.grpc.modelP\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.model.grpc.model.common.Common.getDescriptor(),
+        });
+    internal_static_PredictRequest_descriptor =
+      getDescriptor().getMessageTypes().get(0);
+    internal_static_PredictRequest_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_PredictRequest_descriptor,
+        new java.lang.String[] { "Param", "ModelName", });
+    internal_static_PredictResponse_descriptor =
+      getDescriptor().getMessageTypes().get(1);
+    internal_static_PredictResponse_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_PredictResponse_descriptor,
+        new java.lang.String[] { "Result", "Data", });
+    com.google.protobuf.AnyProto.getDescriptor();
+    com.tzld.piaoquan.recommend.model.grpc.model.common.Common.getDescriptor();
+  }
+
+  // @@protoc_insertion_point(outer_class_scope)
+}

+ 695 - 0
recommend-model-client/src/main/java/com/tzld/piaoquan/recommend/model/grpc/model/PredictRequest.java

@@ -0,0 +1,695 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/model/predict.proto
+
+package com.tzld.piaoquan.recommend.model.grpc.model;
+
+/**
+ * Protobuf type {@code PredictRequest}
+ */
+public final class PredictRequest extends
+    com.google.protobuf.GeneratedMessageV3 implements
+    // @@protoc_insertion_point(message_implements:PredictRequest)
+    PredictRequestOrBuilder {
+private static final long serialVersionUID = 0L;
+  // Use PredictRequest.newBuilder() to construct.
+  private PredictRequest(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    super(builder);
+  }
+  private PredictRequest() {
+    param_ = "";
+    modelName_ = "";
+  }
+
+  @java.lang.Override
+  @SuppressWarnings({"unused"})
+  protected java.lang.Object newInstance(
+      UnusedPrivateParameter unused) {
+    return new PredictRequest();
+  }
+
+  @java.lang.Override
+  public final com.google.protobuf.UnknownFieldSet
+  getUnknownFields() {
+    return this.unknownFields;
+  }
+  private PredictRequest(
+      com.google.protobuf.CodedInputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    this();
+    if (extensionRegistry == null) {
+      throw new java.lang.NullPointerException();
+    }
+    com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+        com.google.protobuf.UnknownFieldSet.newBuilder();
+    try {
+      boolean done = false;
+      while (!done) {
+        int tag = input.readTag();
+        switch (tag) {
+          case 0:
+            done = true;
+            break;
+          case 10: {
+            java.lang.String s = input.readStringRequireUtf8();
+
+            param_ = s;
+            break;
+          }
+          case 18: {
+            java.lang.String s = input.readStringRequireUtf8();
+
+            modelName_ = s;
+            break;
+          }
+          default: {
+            if (!parseUnknownField(
+                input, unknownFields, extensionRegistry, tag)) {
+              done = true;
+            }
+            break;
+          }
+        }
+      }
+    } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+      throw e.setUnfinishedMessage(this);
+    } catch (java.io.IOException e) {
+      throw new com.google.protobuf.InvalidProtocolBufferException(
+          e).setUnfinishedMessage(this);
+    } finally {
+      this.unknownFields = unknownFields.build();
+      makeExtensionsImmutable();
+    }
+  }
+  public static final com.google.protobuf.Descriptors.Descriptor
+      getDescriptor() {
+    return com.tzld.piaoquan.recommend.model.grpc.model.Predict.internal_static_PredictRequest_descriptor;
+  }
+
+  @java.lang.Override
+  protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internalGetFieldAccessorTable() {
+    return com.tzld.piaoquan.recommend.model.grpc.model.Predict.internal_static_PredictRequest_fieldAccessorTable
+        .ensureFieldAccessorsInitialized(
+            com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest.class, com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest.Builder.class);
+  }
+
+  public static final int PARAM_FIELD_NUMBER = 1;
+  private volatile java.lang.Object param_;
+  /**
+   * <code>string param = 1;</code>
+   * @return The param.
+   */
+  @java.lang.Override
+  public java.lang.String getParam() {
+    java.lang.Object ref = param_;
+    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();
+      param_ = s;
+      return s;
+    }
+  }
+  /**
+   * <code>string param = 1;</code>
+   * @return The bytes for param.
+   */
+  @java.lang.Override
+  public com.google.protobuf.ByteString
+      getParamBytes() {
+    java.lang.Object ref = param_;
+    if (ref instanceof java.lang.String) {
+      com.google.protobuf.ByteString b = 
+          com.google.protobuf.ByteString.copyFromUtf8(
+              (java.lang.String) ref);
+      param_ = b;
+      return b;
+    } else {
+      return (com.google.protobuf.ByteString) ref;
+    }
+  }
+
+  public static final int MODEL_NAME_FIELD_NUMBER = 2;
+  private volatile java.lang.Object modelName_;
+  /**
+   * <code>string model_name = 2;</code>
+   * @return The modelName.
+   */
+  @java.lang.Override
+  public java.lang.String getModelName() {
+    java.lang.Object ref = modelName_;
+    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();
+      modelName_ = s;
+      return s;
+    }
+  }
+  /**
+   * <code>string model_name = 2;</code>
+   * @return The bytes for modelName.
+   */
+  @java.lang.Override
+  public com.google.protobuf.ByteString
+      getModelNameBytes() {
+    java.lang.Object ref = modelName_;
+    if (ref instanceof java.lang.String) {
+      com.google.protobuf.ByteString b = 
+          com.google.protobuf.ByteString.copyFromUtf8(
+              (java.lang.String) ref);
+      modelName_ = b;
+      return b;
+    } else {
+      return (com.google.protobuf.ByteString) ref;
+    }
+  }
+
+  private byte memoizedIsInitialized = -1;
+  @java.lang.Override
+  public final boolean isInitialized() {
+    byte isInitialized = memoizedIsInitialized;
+    if (isInitialized == 1) return true;
+    if (isInitialized == 0) return false;
+
+    memoizedIsInitialized = 1;
+    return true;
+  }
+
+  @java.lang.Override
+  public void writeTo(com.google.protobuf.CodedOutputStream output)
+                      throws java.io.IOException {
+    if (!getParamBytes().isEmpty()) {
+      com.google.protobuf.GeneratedMessageV3.writeString(output, 1, param_);
+    }
+    if (!getModelNameBytes().isEmpty()) {
+      com.google.protobuf.GeneratedMessageV3.writeString(output, 2, modelName_);
+    }
+    unknownFields.writeTo(output);
+  }
+
+  @java.lang.Override
+  public int getSerializedSize() {
+    int size = memoizedSize;
+    if (size != -1) return size;
+
+    size = 0;
+    if (!getParamBytes().isEmpty()) {
+      size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, param_);
+    }
+    if (!getModelNameBytes().isEmpty()) {
+      size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, modelName_);
+    }
+    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.model.grpc.model.PredictRequest)) {
+      return super.equals(obj);
+    }
+    com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest other = (com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest) obj;
+
+    if (!getParam()
+        .equals(other.getParam())) return false;
+    if (!getModelName()
+        .equals(other.getModelName())) 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) + PARAM_FIELD_NUMBER;
+    hash = (53 * hash) + getParam().hashCode();
+    hash = (37 * hash) + MODEL_NAME_FIELD_NUMBER;
+    hash = (53 * hash) + getModelName().hashCode();
+    hash = (29 * hash) + unknownFields.hashCode();
+    memoizedHashCode = hash;
+    return hash;
+  }
+
+  public static com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest parseFrom(
+      java.nio.ByteBuffer data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest 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.model.grpc.model.PredictRequest parseFrom(
+      com.google.protobuf.ByteString data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest 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.model.grpc.model.PredictRequest parseFrom(byte[] data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest parseFrom(
+      byte[] data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest parseFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest 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.model.grpc.model.PredictRequest parseDelimitedFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest 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.model.grpc.model.PredictRequest parseFrom(
+      com.google.protobuf.CodedInputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest 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.model.grpc.model.PredictRequest 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 PredictRequest}
+   */
+  public static final class Builder extends
+      com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
+      // @@protoc_insertion_point(builder_implements:PredictRequest)
+      com.tzld.piaoquan.recommend.model.grpc.model.PredictRequestOrBuilder {
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return com.tzld.piaoquan.recommend.model.grpc.model.Predict.internal_static_PredictRequest_descriptor;
+    }
+
+    @java.lang.Override
+    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return com.tzld.piaoquan.recommend.model.grpc.model.Predict.internal_static_PredictRequest_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest.class, com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest.Builder.class);
+    }
+
+    // Construct using com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest.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();
+      param_ = "";
+
+      modelName_ = "";
+
+      return this;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.Descriptor
+        getDescriptorForType() {
+      return com.tzld.piaoquan.recommend.model.grpc.model.Predict.internal_static_PredictRequest_descriptor;
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest getDefaultInstanceForType() {
+      return com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest.getDefaultInstance();
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest build() {
+      com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest result = buildPartial();
+      if (!result.isInitialized()) {
+        throw newUninitializedMessageException(result);
+      }
+      return result;
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest buildPartial() {
+      com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest result = new com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest(this);
+      result.param_ = param_;
+      result.modelName_ = modelName_;
+      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.model.grpc.model.PredictRequest) {
+        return mergeFrom((com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest)other);
+      } else {
+        super.mergeFrom(other);
+        return this;
+      }
+    }
+
+    public Builder mergeFrom(com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest other) {
+      if (other == com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest.getDefaultInstance()) return this;
+      if (!other.getParam().isEmpty()) {
+        param_ = other.param_;
+        onChanged();
+      }
+      if (!other.getModelName().isEmpty()) {
+        modelName_ = other.modelName_;
+        onChanged();
+      }
+      this.mergeUnknownFields(other.unknownFields);
+      onChanged();
+      return this;
+    }
+
+    @java.lang.Override
+    public final boolean isInitialized() {
+      return true;
+    }
+
+    @java.lang.Override
+    public Builder mergeFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest parsedMessage = null;
+      try {
+        parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        parsedMessage = (com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest) e.getUnfinishedMessage();
+        throw e.unwrapIOException();
+      } finally {
+        if (parsedMessage != null) {
+          mergeFrom(parsedMessage);
+        }
+      }
+      return this;
+    }
+
+    private java.lang.Object param_ = "";
+    /**
+     * <code>string param = 1;</code>
+     * @return The param.
+     */
+    public java.lang.String getParam() {
+      java.lang.Object ref = param_;
+      if (!(ref instanceof java.lang.String)) {
+        com.google.protobuf.ByteString bs =
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        param_ = s;
+        return s;
+      } else {
+        return (java.lang.String) ref;
+      }
+    }
+    /**
+     * <code>string param = 1;</code>
+     * @return The bytes for param.
+     */
+    public com.google.protobuf.ByteString
+        getParamBytes() {
+      java.lang.Object ref = param_;
+      if (ref instanceof String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        param_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+    /**
+     * <code>string param = 1;</code>
+     * @param value The param to set.
+     * @return This builder for chaining.
+     */
+    public Builder setParam(
+        java.lang.String value) {
+      if (value == null) {
+    throw new NullPointerException();
+  }
+  
+      param_ = value;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string param = 1;</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearParam() {
+      
+      param_ = getDefaultInstance().getParam();
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string param = 1;</code>
+     * @param value The bytes for param to set.
+     * @return This builder for chaining.
+     */
+    public Builder setParamBytes(
+        com.google.protobuf.ByteString value) {
+      if (value == null) {
+    throw new NullPointerException();
+  }
+  checkByteStringIsUtf8(value);
+      
+      param_ = value;
+      onChanged();
+      return this;
+    }
+
+    private java.lang.Object modelName_ = "";
+    /**
+     * <code>string model_name = 2;</code>
+     * @return The modelName.
+     */
+    public java.lang.String getModelName() {
+      java.lang.Object ref = modelName_;
+      if (!(ref instanceof java.lang.String)) {
+        com.google.protobuf.ByteString bs =
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        modelName_ = s;
+        return s;
+      } else {
+        return (java.lang.String) ref;
+      }
+    }
+    /**
+     * <code>string model_name = 2;</code>
+     * @return The bytes for modelName.
+     */
+    public com.google.protobuf.ByteString
+        getModelNameBytes() {
+      java.lang.Object ref = modelName_;
+      if (ref instanceof String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        modelName_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+    /**
+     * <code>string model_name = 2;</code>
+     * @param value The modelName to set.
+     * @return This builder for chaining.
+     */
+    public Builder setModelName(
+        java.lang.String value) {
+      if (value == null) {
+    throw new NullPointerException();
+  }
+  
+      modelName_ = value;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string model_name = 2;</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearModelName() {
+      
+      modelName_ = getDefaultInstance().getModelName();
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string model_name = 2;</code>
+     * @param value The bytes for modelName to set.
+     * @return This builder for chaining.
+     */
+    public Builder setModelNameBytes(
+        com.google.protobuf.ByteString value) {
+      if (value == null) {
+    throw new NullPointerException();
+  }
+  checkByteStringIsUtf8(value);
+      
+      modelName_ = value;
+      onChanged();
+      return this;
+    }
+    @java.lang.Override
+    public final Builder setUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.setUnknownFields(unknownFields);
+    }
+
+    @java.lang.Override
+    public final Builder mergeUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.mergeUnknownFields(unknownFields);
+    }
+
+
+    // @@protoc_insertion_point(builder_scope:PredictRequest)
+  }
+
+  // @@protoc_insertion_point(class_scope:PredictRequest)
+  private static final com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest DEFAULT_INSTANCE;
+  static {
+    DEFAULT_INSTANCE = new com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest();
+  }
+
+  public static com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest getDefaultInstance() {
+    return DEFAULT_INSTANCE;
+  }
+
+  private static final com.google.protobuf.Parser<PredictRequest>
+      PARSER = new com.google.protobuf.AbstractParser<PredictRequest>() {
+    @java.lang.Override
+    public PredictRequest parsePartialFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return new PredictRequest(input, extensionRegistry);
+    }
+  };
+
+  public static com.google.protobuf.Parser<PredictRequest> parser() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.google.protobuf.Parser<PredictRequest> getParserForType() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest getDefaultInstanceForType() {
+    return DEFAULT_INSTANCE;
+  }
+
+}
+

+ 33 - 0
recommend-model-client/src/main/java/com/tzld/piaoquan/recommend/model/grpc/model/PredictRequestOrBuilder.java

@@ -0,0 +1,33 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/model/predict.proto
+
+package com.tzld.piaoquan.recommend.model.grpc.model;
+
+public interface PredictRequestOrBuilder extends
+    // @@protoc_insertion_point(interface_extends:PredictRequest)
+    com.google.protobuf.MessageOrBuilder {
+
+  /**
+   * <code>string param = 1;</code>
+   * @return The param.
+   */
+  java.lang.String getParam();
+  /**
+   * <code>string param = 1;</code>
+   * @return The bytes for param.
+   */
+  com.google.protobuf.ByteString
+      getParamBytes();
+
+  /**
+   * <code>string model_name = 2;</code>
+   * @return The modelName.
+   */
+  java.lang.String getModelName();
+  /**
+   * <code>string model_name = 2;</code>
+   * @return The bytes for modelName.
+   */
+  com.google.protobuf.ByteString
+      getModelNameBytes();
+}

+ 745 - 0
recommend-model-client/src/main/java/com/tzld/piaoquan/recommend/model/grpc/model/PredictResponse.java

@@ -0,0 +1,745 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/model/predict.proto
+
+package com.tzld.piaoquan.recommend.model.grpc.model;
+
+/**
+ * Protobuf type {@code PredictResponse}
+ */
+public final class PredictResponse extends
+    com.google.protobuf.GeneratedMessageV3 implements
+    // @@protoc_insertion_point(message_implements:PredictResponse)
+    PredictResponseOrBuilder {
+private static final long serialVersionUID = 0L;
+  // Use PredictResponse.newBuilder() to construct.
+  private PredictResponse(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    super(builder);
+  }
+  private PredictResponse() {
+    data_ = "";
+  }
+
+  @java.lang.Override
+  @SuppressWarnings({"unused"})
+  protected java.lang.Object newInstance(
+      UnusedPrivateParameter unused) {
+    return new PredictResponse();
+  }
+
+  @java.lang.Override
+  public final com.google.protobuf.UnknownFieldSet
+  getUnknownFields() {
+    return this.unknownFields;
+  }
+  private PredictResponse(
+      com.google.protobuf.CodedInputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    this();
+    if (extensionRegistry == null) {
+      throw new java.lang.NullPointerException();
+    }
+    com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+        com.google.protobuf.UnknownFieldSet.newBuilder();
+    try {
+      boolean done = false;
+      while (!done) {
+        int tag = input.readTag();
+        switch (tag) {
+          case 0:
+            done = true;
+            break;
+          case 10: {
+            com.tzld.piaoquan.recommend.model.grpc.model.common.Result.Builder subBuilder = null;
+            if (result_ != null) {
+              subBuilder = result_.toBuilder();
+            }
+            result_ = input.readMessage(com.tzld.piaoquan.recommend.model.grpc.model.common.Result.parser(), extensionRegistry);
+            if (subBuilder != null) {
+              subBuilder.mergeFrom(result_);
+              result_ = subBuilder.buildPartial();
+            }
+
+            break;
+          }
+          case 18: {
+            java.lang.String s = input.readStringRequireUtf8();
+
+            data_ = s;
+            break;
+          }
+          default: {
+            if (!parseUnknownField(
+                input, unknownFields, extensionRegistry, tag)) {
+              done = true;
+            }
+            break;
+          }
+        }
+      }
+    } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+      throw e.setUnfinishedMessage(this);
+    } catch (java.io.IOException e) {
+      throw new com.google.protobuf.InvalidProtocolBufferException(
+          e).setUnfinishedMessage(this);
+    } finally {
+      this.unknownFields = unknownFields.build();
+      makeExtensionsImmutable();
+    }
+  }
+  public static final com.google.protobuf.Descriptors.Descriptor
+      getDescriptor() {
+    return com.tzld.piaoquan.recommend.model.grpc.model.Predict.internal_static_PredictResponse_descriptor;
+  }
+
+  @java.lang.Override
+  protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internalGetFieldAccessorTable() {
+    return com.tzld.piaoquan.recommend.model.grpc.model.Predict.internal_static_PredictResponse_fieldAccessorTable
+        .ensureFieldAccessorsInitialized(
+            com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse.class, com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse.Builder.class);
+  }
+
+  public static final int RESULT_FIELD_NUMBER = 1;
+  private com.tzld.piaoquan.recommend.model.grpc.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.model.grpc.model.common.Result getResult() {
+    return result_ == null ? com.tzld.piaoquan.recommend.model.grpc.model.common.Result.getDefaultInstance() : result_;
+  }
+  /**
+   * <code>.Result result = 1;</code>
+   */
+  @java.lang.Override
+  public com.tzld.piaoquan.recommend.model.grpc.model.common.ResultOrBuilder getResultOrBuilder() {
+    return getResult();
+  }
+
+  public static final int DATA_FIELD_NUMBER = 2;
+  private volatile java.lang.Object data_;
+  /**
+   * <code>string data = 2;</code>
+   * @return The data.
+   */
+  @java.lang.Override
+  public java.lang.String getData() {
+    java.lang.Object ref = data_;
+    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();
+      data_ = s;
+      return s;
+    }
+  }
+  /**
+   * <code>string data = 2;</code>
+   * @return The bytes for data.
+   */
+  @java.lang.Override
+  public com.google.protobuf.ByteString
+      getDataBytes() {
+    java.lang.Object ref = data_;
+    if (ref instanceof java.lang.String) {
+      com.google.protobuf.ByteString b = 
+          com.google.protobuf.ByteString.copyFromUtf8(
+              (java.lang.String) ref);
+      data_ = b;
+      return b;
+    } else {
+      return (com.google.protobuf.ByteString) ref;
+    }
+  }
+
+  private byte memoizedIsInitialized = -1;
+  @java.lang.Override
+  public final boolean isInitialized() {
+    byte isInitialized = memoizedIsInitialized;
+    if (isInitialized == 1) return true;
+    if (isInitialized == 0) return false;
+
+    memoizedIsInitialized = 1;
+    return true;
+  }
+
+  @java.lang.Override
+  public void writeTo(com.google.protobuf.CodedOutputStream output)
+                      throws java.io.IOException {
+    if (result_ != null) {
+      output.writeMessage(1, getResult());
+    }
+    if (!getDataBytes().isEmpty()) {
+      com.google.protobuf.GeneratedMessageV3.writeString(output, 2, data_);
+    }
+    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());
+    }
+    if (!getDataBytes().isEmpty()) {
+      size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, data_);
+    }
+    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.model.grpc.model.PredictResponse)) {
+      return super.equals(obj);
+    }
+    com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse other = (com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse) obj;
+
+    if (hasResult() != other.hasResult()) return false;
+    if (hasResult()) {
+      if (!getResult()
+          .equals(other.getResult())) return false;
+    }
+    if (!getData()
+        .equals(other.getData())) 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();
+    }
+    hash = (37 * hash) + DATA_FIELD_NUMBER;
+    hash = (53 * hash) + getData().hashCode();
+    hash = (29 * hash) + unknownFields.hashCode();
+    memoizedHashCode = hash;
+    return hash;
+  }
+
+  public static com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse parseFrom(
+      java.nio.ByteBuffer data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse 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.model.grpc.model.PredictResponse parseFrom(
+      com.google.protobuf.ByteString data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse 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.model.grpc.model.PredictResponse parseFrom(byte[] data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse parseFrom(
+      byte[] data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse parseFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse 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.model.grpc.model.PredictResponse parseDelimitedFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse 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.model.grpc.model.PredictResponse parseFrom(
+      com.google.protobuf.CodedInputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse 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.model.grpc.model.PredictResponse 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 PredictResponse}
+   */
+  public static final class Builder extends
+      com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
+      // @@protoc_insertion_point(builder_implements:PredictResponse)
+      com.tzld.piaoquan.recommend.model.grpc.model.PredictResponseOrBuilder {
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return com.tzld.piaoquan.recommend.model.grpc.model.Predict.internal_static_PredictResponse_descriptor;
+    }
+
+    @java.lang.Override
+    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return com.tzld.piaoquan.recommend.model.grpc.model.Predict.internal_static_PredictResponse_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse.class, com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse.Builder.class);
+    }
+
+    // Construct using com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse.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;
+      }
+      data_ = "";
+
+      return this;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.Descriptor
+        getDescriptorForType() {
+      return com.tzld.piaoquan.recommend.model.grpc.model.Predict.internal_static_PredictResponse_descriptor;
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse getDefaultInstanceForType() {
+      return com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse.getDefaultInstance();
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse build() {
+      com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse result = buildPartial();
+      if (!result.isInitialized()) {
+        throw newUninitializedMessageException(result);
+      }
+      return result;
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse buildPartial() {
+      com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse result = new com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse(this);
+      if (resultBuilder_ == null) {
+        result.result_ = result_;
+      } else {
+        result.result_ = resultBuilder_.build();
+      }
+      result.data_ = data_;
+      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.model.grpc.model.PredictResponse) {
+        return mergeFrom((com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse)other);
+      } else {
+        super.mergeFrom(other);
+        return this;
+      }
+    }
+
+    public Builder mergeFrom(com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse other) {
+      if (other == com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse.getDefaultInstance()) return this;
+      if (other.hasResult()) {
+        mergeResult(other.getResult());
+      }
+      if (!other.getData().isEmpty()) {
+        data_ = other.data_;
+        onChanged();
+      }
+      this.mergeUnknownFields(other.unknownFields);
+      onChanged();
+      return this;
+    }
+
+    @java.lang.Override
+    public final boolean isInitialized() {
+      return true;
+    }
+
+    @java.lang.Override
+    public Builder mergeFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse parsedMessage = null;
+      try {
+        parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        parsedMessage = (com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse) e.getUnfinishedMessage();
+        throw e.unwrapIOException();
+      } finally {
+        if (parsedMessage != null) {
+          mergeFrom(parsedMessage);
+        }
+      }
+      return this;
+    }
+
+    private com.tzld.piaoquan.recommend.model.grpc.model.common.Result result_;
+    private com.google.protobuf.SingleFieldBuilderV3<
+        com.tzld.piaoquan.recommend.model.grpc.model.common.Result, com.tzld.piaoquan.recommend.model.grpc.model.common.Result.Builder, com.tzld.piaoquan.recommend.model.grpc.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.model.grpc.model.common.Result getResult() {
+      if (resultBuilder_ == null) {
+        return result_ == null ? com.tzld.piaoquan.recommend.model.grpc.model.common.Result.getDefaultInstance() : result_;
+      } else {
+        return resultBuilder_.getMessage();
+      }
+    }
+    /**
+     * <code>.Result result = 1;</code>
+     */
+    public Builder setResult(com.tzld.piaoquan.recommend.model.grpc.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.model.grpc.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.model.grpc.model.common.Result value) {
+      if (resultBuilder_ == null) {
+        if (result_ != null) {
+          result_ =
+            com.tzld.piaoquan.recommend.model.grpc.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.model.grpc.model.common.Result.Builder getResultBuilder() {
+      
+      onChanged();
+      return getResultFieldBuilder().getBuilder();
+    }
+    /**
+     * <code>.Result result = 1;</code>
+     */
+    public com.tzld.piaoquan.recommend.model.grpc.model.common.ResultOrBuilder getResultOrBuilder() {
+      if (resultBuilder_ != null) {
+        return resultBuilder_.getMessageOrBuilder();
+      } else {
+        return result_ == null ?
+            com.tzld.piaoquan.recommend.model.grpc.model.common.Result.getDefaultInstance() : result_;
+      }
+    }
+    /**
+     * <code>.Result result = 1;</code>
+     */
+    private com.google.protobuf.SingleFieldBuilderV3<
+        com.tzld.piaoquan.recommend.model.grpc.model.common.Result, com.tzld.piaoquan.recommend.model.grpc.model.common.Result.Builder, com.tzld.piaoquan.recommend.model.grpc.model.common.ResultOrBuilder> 
+        getResultFieldBuilder() {
+      if (resultBuilder_ == null) {
+        resultBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
+            com.tzld.piaoquan.recommend.model.grpc.model.common.Result, com.tzld.piaoquan.recommend.model.grpc.model.common.Result.Builder, com.tzld.piaoquan.recommend.model.grpc.model.common.ResultOrBuilder>(
+                getResult(),
+                getParentForChildren(),
+                isClean());
+        result_ = null;
+      }
+      return resultBuilder_;
+    }
+
+    private java.lang.Object data_ = "";
+    /**
+     * <code>string data = 2;</code>
+     * @return The data.
+     */
+    public java.lang.String getData() {
+      java.lang.Object ref = data_;
+      if (!(ref instanceof java.lang.String)) {
+        com.google.protobuf.ByteString bs =
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        data_ = s;
+        return s;
+      } else {
+        return (java.lang.String) ref;
+      }
+    }
+    /**
+     * <code>string data = 2;</code>
+     * @return The bytes for data.
+     */
+    public com.google.protobuf.ByteString
+        getDataBytes() {
+      java.lang.Object ref = data_;
+      if (ref instanceof String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        data_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+    /**
+     * <code>string data = 2;</code>
+     * @param value The data to set.
+     * @return This builder for chaining.
+     */
+    public Builder setData(
+        java.lang.String value) {
+      if (value == null) {
+    throw new NullPointerException();
+  }
+  
+      data_ = value;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string data = 2;</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearData() {
+      
+      data_ = getDefaultInstance().getData();
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string data = 2;</code>
+     * @param value The bytes for data to set.
+     * @return This builder for chaining.
+     */
+    public Builder setDataBytes(
+        com.google.protobuf.ByteString value) {
+      if (value == null) {
+    throw new NullPointerException();
+  }
+  checkByteStringIsUtf8(value);
+      
+      data_ = value;
+      onChanged();
+      return this;
+    }
+    @java.lang.Override
+    public final Builder setUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.setUnknownFields(unknownFields);
+    }
+
+    @java.lang.Override
+    public final Builder mergeUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.mergeUnknownFields(unknownFields);
+    }
+
+
+    // @@protoc_insertion_point(builder_scope:PredictResponse)
+  }
+
+  // @@protoc_insertion_point(class_scope:PredictResponse)
+  private static final com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse DEFAULT_INSTANCE;
+  static {
+    DEFAULT_INSTANCE = new com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse();
+  }
+
+  public static com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse getDefaultInstance() {
+    return DEFAULT_INSTANCE;
+  }
+
+  private static final com.google.protobuf.Parser<PredictResponse>
+      PARSER = new com.google.protobuf.AbstractParser<PredictResponse>() {
+    @java.lang.Override
+    public PredictResponse parsePartialFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return new PredictResponse(input, extensionRegistry);
+    }
+  };
+
+  public static com.google.protobuf.Parser<PredictResponse> parser() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.google.protobuf.Parser<PredictResponse> getParserForType() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse getDefaultInstanceForType() {
+    return DEFAULT_INSTANCE;
+  }
+
+}
+

+ 36 - 0
recommend-model-client/src/main/java/com/tzld/piaoquan/recommend/model/grpc/model/PredictResponseOrBuilder.java

@@ -0,0 +1,36 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/model/predict.proto
+
+package com.tzld.piaoquan.recommend.model.grpc.model;
+
+public interface PredictResponseOrBuilder extends
+    // @@protoc_insertion_point(interface_extends:PredictResponse)
+    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.model.grpc.model.common.Result getResult();
+  /**
+   * <code>.Result result = 1;</code>
+   */
+  com.tzld.piaoquan.recommend.model.grpc.model.common.ResultOrBuilder getResultOrBuilder();
+
+  /**
+   * <code>string data = 2;</code>
+   * @return The data.
+   */
+  java.lang.String getData();
+  /**
+   * <code>string data = 2;</code>
+   * @return The bytes for data.
+   */
+  com.google.protobuf.ByteString
+      getDataBytes();
+}

+ 237 - 0
recommend-model-client/src/main/java/com/tzld/piaoquan/recommend/model/grpc/model/PredictService.java

@@ -0,0 +1,237 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/model/predict.proto
+
+package com.tzld.piaoquan.recommend.model.grpc.model;
+
+/**
+ * Protobuf service {@code PredictService}
+ */
+public  abstract class PredictService
+    implements com.google.protobuf.Service {
+  protected PredictService() {}
+
+  public interface Interface {
+    /**
+     * <code>rpc predict(.PredictRequest) returns (.PredictResponse);</code>
+     */
+    public abstract void predict(
+        com.google.protobuf.RpcController controller,
+        com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest request,
+        com.google.protobuf.RpcCallback<com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse> done);
+
+  }
+
+  public static com.google.protobuf.Service newReflectiveService(
+      final Interface impl) {
+    return new PredictService() {
+      @java.lang.Override
+      public  void predict(
+          com.google.protobuf.RpcController controller,
+          com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest request,
+          com.google.protobuf.RpcCallback<com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse> done) {
+        impl.predict(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.predict(controller, (com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest)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.model.grpc.model.PredictRequest.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.model.grpc.model.PredictResponse.getDefaultInstance();
+          default:
+            throw new java.lang.AssertionError("Can't get here.");
+        }
+      }
+
+    };
+  }
+
+  /**
+   * <code>rpc predict(.PredictRequest) returns (.PredictResponse);</code>
+   */
+  public abstract void predict(
+      com.google.protobuf.RpcController controller,
+      com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest request,
+      com.google.protobuf.RpcCallback<com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse> done);
+
+  public static final
+      com.google.protobuf.Descriptors.ServiceDescriptor
+      getDescriptor() {
+    return com.tzld.piaoquan.recommend.model.grpc.model.Predict.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.predict(controller, (com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest)request,
+          com.google.protobuf.RpcUtil.<com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse>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.model.grpc.model.PredictRequest.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.model.grpc.model.PredictResponse.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.model.grpc.model.PredictService 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 predict(
+        com.google.protobuf.RpcController controller,
+        com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest request,
+        com.google.protobuf.RpcCallback<com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse> done) {
+      channel.callMethod(
+        getDescriptor().getMethods().get(0),
+        controller,
+        request,
+        com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse.getDefaultInstance(),
+        com.google.protobuf.RpcUtil.generalizeCallback(
+          done,
+          com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse.class,
+          com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse.getDefaultInstance()));
+    }
+  }
+
+  public static BlockingInterface newBlockingStub(
+      com.google.protobuf.BlockingRpcChannel channel) {
+    return new BlockingStub(channel);
+  }
+
+  public interface BlockingInterface {
+    public com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse predict(
+        com.google.protobuf.RpcController controller,
+        com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest 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.model.grpc.model.PredictResponse predict(
+        com.google.protobuf.RpcController controller,
+        com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest request)
+        throws com.google.protobuf.ServiceException {
+      return (com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse) channel.callBlockingMethod(
+        getDescriptor().getMethods().get(0),
+        controller,
+        request,
+        com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse.getDefaultInstance());
+    }
+
+  }
+
+  // @@protoc_insertion_point(class_scope:PredictService)
+}
+

+ 288 - 0
recommend-model-client/src/main/java/com/tzld/piaoquan/recommend/model/grpc/model/PredictServiceGrpc.java

@@ -0,0 +1,288 @@
+package com.tzld.piaoquan.recommend.model.grpc.model;
+
+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/model/predict.proto")
+public final class PredictServiceGrpc {
+
+  private PredictServiceGrpc() {}
+
+  public static final String SERVICE_NAME = "PredictService";
+
+  // Static method descriptors that strictly reflect the proto.
+  private static volatile io.grpc.MethodDescriptor<com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest,
+      com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse> getPredictMethod;
+
+  @io.grpc.stub.annotations.RpcMethod(
+      fullMethodName = SERVICE_NAME + '/' + "predict",
+      requestType = com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest.class,
+      responseType = com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse.class,
+      methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
+  public static io.grpc.MethodDescriptor<com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest,
+      com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse> getPredictMethod() {
+    io.grpc.MethodDescriptor<com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest, com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse> getPredictMethod;
+    if ((getPredictMethod = PredictServiceGrpc.getPredictMethod) == null) {
+      synchronized (PredictServiceGrpc.class) {
+        if ((getPredictMethod = PredictServiceGrpc.getPredictMethod) == null) {
+          PredictServiceGrpc.getPredictMethod = getPredictMethod =
+              io.grpc.MethodDescriptor.<com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest, com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse>newBuilder()
+              .setType(io.grpc.MethodDescriptor.MethodType.UNARY)
+              .setFullMethodName(generateFullMethodName(SERVICE_NAME, "predict"))
+              .setSampledToLocalTracing(true)
+              .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest.getDefaultInstance()))
+              .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse.getDefaultInstance()))
+              .setSchemaDescriptor(new PredictServiceMethodDescriptorSupplier("predict"))
+              .build();
+        }
+      }
+    }
+    return getPredictMethod;
+  }
+
+  /**
+   * Creates a new async stub that supports all call types for the service
+   */
+  public static PredictServiceStub newStub(io.grpc.Channel channel) {
+    io.grpc.stub.AbstractStub.StubFactory<PredictServiceStub> factory =
+      new io.grpc.stub.AbstractStub.StubFactory<PredictServiceStub>() {
+        @java.lang.Override
+        public PredictServiceStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+          return new PredictServiceStub(channel, callOptions);
+        }
+      };
+    return PredictServiceStub.newStub(factory, channel);
+  }
+
+  /**
+   * Creates a new blocking-style stub that supports unary and streaming output calls on the service
+   */
+  public static PredictServiceBlockingStub newBlockingStub(
+      io.grpc.Channel channel) {
+    io.grpc.stub.AbstractStub.StubFactory<PredictServiceBlockingStub> factory =
+      new io.grpc.stub.AbstractStub.StubFactory<PredictServiceBlockingStub>() {
+        @java.lang.Override
+        public PredictServiceBlockingStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+          return new PredictServiceBlockingStub(channel, callOptions);
+        }
+      };
+    return PredictServiceBlockingStub.newStub(factory, channel);
+  }
+
+  /**
+   * Creates a new ListenableFuture-style stub that supports unary calls on the service
+   */
+  public static PredictServiceFutureStub newFutureStub(
+      io.grpc.Channel channel) {
+    io.grpc.stub.AbstractStub.StubFactory<PredictServiceFutureStub> factory =
+      new io.grpc.stub.AbstractStub.StubFactory<PredictServiceFutureStub>() {
+        @java.lang.Override
+        public PredictServiceFutureStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+          return new PredictServiceFutureStub(channel, callOptions);
+        }
+      };
+    return PredictServiceFutureStub.newStub(factory, channel);
+  }
+
+  /**
+   */
+  public static abstract class PredictServiceImplBase implements io.grpc.BindableService {
+
+    /**
+     */
+    public void predict(com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest request,
+        io.grpc.stub.StreamObserver<com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse> responseObserver) {
+      asyncUnimplementedUnaryCall(getPredictMethod(), responseObserver);
+    }
+
+    @java.lang.Override public final io.grpc.ServerServiceDefinition bindService() {
+      return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor())
+          .addMethod(
+            getPredictMethod(),
+            asyncUnaryCall(
+              new MethodHandlers<
+                com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest,
+                com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse>(
+                  this, METHODID_PREDICT)))
+          .build();
+    }
+  }
+
+  /**
+   */
+  public static final class PredictServiceStub extends io.grpc.stub.AbstractAsyncStub<PredictServiceStub> {
+    private PredictServiceStub(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      super(channel, callOptions);
+    }
+
+    @java.lang.Override
+    protected PredictServiceStub build(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      return new PredictServiceStub(channel, callOptions);
+    }
+
+    /**
+     */
+    public void predict(com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest request,
+        io.grpc.stub.StreamObserver<com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse> responseObserver) {
+      asyncUnaryCall(
+          getChannel().newCall(getPredictMethod(), getCallOptions()), request, responseObserver);
+    }
+  }
+
+  /**
+   */
+  public static final class PredictServiceBlockingStub extends io.grpc.stub.AbstractBlockingStub<PredictServiceBlockingStub> {
+    private PredictServiceBlockingStub(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      super(channel, callOptions);
+    }
+
+    @java.lang.Override
+    protected PredictServiceBlockingStub build(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      return new PredictServiceBlockingStub(channel, callOptions);
+    }
+
+    /**
+     */
+    public com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse predict(com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest request) {
+      return blockingUnaryCall(
+          getChannel(), getPredictMethod(), getCallOptions(), request);
+    }
+  }
+
+  /**
+   */
+  public static final class PredictServiceFutureStub extends io.grpc.stub.AbstractFutureStub<PredictServiceFutureStub> {
+    private PredictServiceFutureStub(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      super(channel, callOptions);
+    }
+
+    @java.lang.Override
+    protected PredictServiceFutureStub build(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      return new PredictServiceFutureStub(channel, callOptions);
+    }
+
+    /**
+     */
+    public com.google.common.util.concurrent.ListenableFuture<com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse> predict(
+        com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest request) {
+      return futureUnaryCall(
+          getChannel().newCall(getPredictMethod(), getCallOptions()), request);
+    }
+  }
+
+  private static final int METHODID_PREDICT = 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 PredictServiceImplBase serviceImpl;
+    private final int methodId;
+
+    MethodHandlers(PredictServiceImplBase 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_PREDICT:
+          serviceImpl.predict((com.tzld.piaoquan.recommend.model.grpc.model.PredictRequest) request,
+              (io.grpc.stub.StreamObserver<com.tzld.piaoquan.recommend.model.grpc.model.PredictResponse>) 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 PredictServiceBaseDescriptorSupplier
+      implements io.grpc.protobuf.ProtoFileDescriptorSupplier, io.grpc.protobuf.ProtoServiceDescriptorSupplier {
+    PredictServiceBaseDescriptorSupplier() {}
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.FileDescriptor getFileDescriptor() {
+      return com.tzld.piaoquan.recommend.model.grpc.model.Predict.getDescriptor();
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.ServiceDescriptor getServiceDescriptor() {
+      return getFileDescriptor().findServiceByName("PredictService");
+    }
+  }
+
+  private static final class PredictServiceFileDescriptorSupplier
+      extends PredictServiceBaseDescriptorSupplier {
+    PredictServiceFileDescriptorSupplier() {}
+  }
+
+  private static final class PredictServiceMethodDescriptorSupplier
+      extends PredictServiceBaseDescriptorSupplier
+      implements io.grpc.protobuf.ProtoMethodDescriptorSupplier {
+    private final String methodName;
+
+    PredictServiceMethodDescriptorSupplier(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 (PredictServiceGrpc.class) {
+        result = serviceDescriptor;
+        if (result == null) {
+          serviceDescriptor = result = io.grpc.ServiceDescriptor.newBuilder(SERVICE_NAME)
+              .setSchemaDescriptor(new PredictServiceFileDescriptorSupplier())
+              .addMethod(getPredictMethod())
+              .build();
+        }
+      }
+    }
+    return result;
+  }
+}

+ 49 - 0
recommend-model-client/src/main/java/com/tzld/piaoquan/recommend/model/grpc/model/common/Common.java

@@ -0,0 +1,49 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/model/common.proto
+
+package com.tzld.piaoquan.recommend.model.grpc.model.common;
+
+public final class Common {
+  private Common() {}
+  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_Result_descriptor;
+  static final 
+    com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internal_static_Result_fieldAccessorTable;
+
+  public static com.google.protobuf.Descriptors.FileDescriptor
+      getDescriptor() {
+    return descriptor;
+  }
+  private static  com.google.protobuf.Descriptors.FileDescriptor
+      descriptor;
+  static {
+    java.lang.String[] descriptorData = {
+      "\n.com/tzld/piaoquan/recommend/model/comm" +
+      "on.proto\"\'\n\006Result\022\014\n\004code\030\001 \001(\005\022\017\n\007mess" +
+      "age\030\002 \001(\tB7\n3com.tzld.piaoquan.recommend" +
+      ".model.grpc.model.commonP\001b\006proto3"
+    };
+    descriptor = com.google.protobuf.Descriptors.FileDescriptor
+      .internalBuildGeneratedFileFrom(descriptorData,
+        new com.google.protobuf.Descriptors.FileDescriptor[] {
+        });
+    internal_static_Result_descriptor =
+      getDescriptor().getMessageTypes().get(0);
+    internal_static_Result_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_Result_descriptor,
+        new java.lang.String[] { "Code", "Message", });
+  }
+
+  // @@protoc_insertion_point(outer_class_scope)
+}

+ 637 - 0
recommend-model-client/src/main/java/com/tzld/piaoquan/recommend/model/grpc/model/common/Result.java

@@ -0,0 +1,637 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/model/common.proto
+
+package com.tzld.piaoquan.recommend.model.grpc.model.common;
+
+/**
+ * Protobuf type {@code Result}
+ */
+public final class Result extends
+    com.google.protobuf.GeneratedMessageV3 implements
+    // @@protoc_insertion_point(message_implements:Result)
+    ResultOrBuilder {
+private static final long serialVersionUID = 0L;
+  // Use Result.newBuilder() to construct.
+  private Result(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    super(builder);
+  }
+  private Result() {
+    message_ = "";
+  }
+
+  @java.lang.Override
+  @SuppressWarnings({"unused"})
+  protected java.lang.Object newInstance(
+      UnusedPrivateParameter unused) {
+    return new Result();
+  }
+
+  @java.lang.Override
+  public final com.google.protobuf.UnknownFieldSet
+  getUnknownFields() {
+    return this.unknownFields;
+  }
+  private Result(
+      com.google.protobuf.CodedInputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    this();
+    if (extensionRegistry == null) {
+      throw new java.lang.NullPointerException();
+    }
+    com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+        com.google.protobuf.UnknownFieldSet.newBuilder();
+    try {
+      boolean done = false;
+      while (!done) {
+        int tag = input.readTag();
+        switch (tag) {
+          case 0:
+            done = true;
+            break;
+          case 8: {
+
+            code_ = input.readInt32();
+            break;
+          }
+          case 18: {
+            java.lang.String s = input.readStringRequireUtf8();
+
+            message_ = s;
+            break;
+          }
+          default: {
+            if (!parseUnknownField(
+                input, unknownFields, extensionRegistry, tag)) {
+              done = true;
+            }
+            break;
+          }
+        }
+      }
+    } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+      throw e.setUnfinishedMessage(this);
+    } catch (java.io.IOException e) {
+      throw new com.google.protobuf.InvalidProtocolBufferException(
+          e).setUnfinishedMessage(this);
+    } finally {
+      this.unknownFields = unknownFields.build();
+      makeExtensionsImmutable();
+    }
+  }
+  public static final com.google.protobuf.Descriptors.Descriptor
+      getDescriptor() {
+    return com.tzld.piaoquan.recommend.model.grpc.model.common.Common.internal_static_Result_descriptor;
+  }
+
+  @java.lang.Override
+  protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internalGetFieldAccessorTable() {
+    return com.tzld.piaoquan.recommend.model.grpc.model.common.Common.internal_static_Result_fieldAccessorTable
+        .ensureFieldAccessorsInitialized(
+            com.tzld.piaoquan.recommend.model.grpc.model.common.Result.class, com.tzld.piaoquan.recommend.model.grpc.model.common.Result.Builder.class);
+  }
+
+  public static final int CODE_FIELD_NUMBER = 1;
+  private int code_;
+  /**
+   * <pre>
+   * 1 成功
+   * </pre>
+   *
+   * <code>int32 code = 1;</code>
+   * @return The code.
+   */
+  @java.lang.Override
+  public int getCode() {
+    return code_;
+  }
+
+  public static final int MESSAGE_FIELD_NUMBER = 2;
+  private volatile java.lang.Object message_;
+  /**
+   * <code>string message = 2;</code>
+   * @return The message.
+   */
+  @java.lang.Override
+  public java.lang.String getMessage() {
+    java.lang.Object ref = message_;
+    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();
+      message_ = s;
+      return s;
+    }
+  }
+  /**
+   * <code>string message = 2;</code>
+   * @return The bytes for message.
+   */
+  @java.lang.Override
+  public com.google.protobuf.ByteString
+      getMessageBytes() {
+    java.lang.Object ref = message_;
+    if (ref instanceof java.lang.String) {
+      com.google.protobuf.ByteString b = 
+          com.google.protobuf.ByteString.copyFromUtf8(
+              (java.lang.String) ref);
+      message_ = b;
+      return b;
+    } else {
+      return (com.google.protobuf.ByteString) ref;
+    }
+  }
+
+  private byte memoizedIsInitialized = -1;
+  @java.lang.Override
+  public final boolean isInitialized() {
+    byte isInitialized = memoizedIsInitialized;
+    if (isInitialized == 1) return true;
+    if (isInitialized == 0) return false;
+
+    memoizedIsInitialized = 1;
+    return true;
+  }
+
+  @java.lang.Override
+  public void writeTo(com.google.protobuf.CodedOutputStream output)
+                      throws java.io.IOException {
+    if (code_ != 0) {
+      output.writeInt32(1, code_);
+    }
+    if (!getMessageBytes().isEmpty()) {
+      com.google.protobuf.GeneratedMessageV3.writeString(output, 2, message_);
+    }
+    unknownFields.writeTo(output);
+  }
+
+  @java.lang.Override
+  public int getSerializedSize() {
+    int size = memoizedSize;
+    if (size != -1) return size;
+
+    size = 0;
+    if (code_ != 0) {
+      size += com.google.protobuf.CodedOutputStream
+        .computeInt32Size(1, code_);
+    }
+    if (!getMessageBytes().isEmpty()) {
+      size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, message_);
+    }
+    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.model.grpc.model.common.Result)) {
+      return super.equals(obj);
+    }
+    com.tzld.piaoquan.recommend.model.grpc.model.common.Result other = (com.tzld.piaoquan.recommend.model.grpc.model.common.Result) obj;
+
+    if (getCode()
+        != other.getCode()) return false;
+    if (!getMessage()
+        .equals(other.getMessage())) 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) + CODE_FIELD_NUMBER;
+    hash = (53 * hash) + getCode();
+    hash = (37 * hash) + MESSAGE_FIELD_NUMBER;
+    hash = (53 * hash) + getMessage().hashCode();
+    hash = (29 * hash) + unknownFields.hashCode();
+    memoizedHashCode = hash;
+    return hash;
+  }
+
+  public static com.tzld.piaoquan.recommend.model.grpc.model.common.Result parseFrom(
+      java.nio.ByteBuffer data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.model.grpc.model.common.Result 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.model.grpc.model.common.Result parseFrom(
+      com.google.protobuf.ByteString data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.model.grpc.model.common.Result 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.model.grpc.model.common.Result parseFrom(byte[] data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.model.grpc.model.common.Result parseFrom(
+      byte[] data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.model.grpc.model.common.Result parseFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.model.grpc.model.common.Result 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.model.grpc.model.common.Result parseDelimitedFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.model.grpc.model.common.Result 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.model.grpc.model.common.Result parseFrom(
+      com.google.protobuf.CodedInputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.model.grpc.model.common.Result 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.model.grpc.model.common.Result 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 Result}
+   */
+  public static final class Builder extends
+      com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
+      // @@protoc_insertion_point(builder_implements:Result)
+      com.tzld.piaoquan.recommend.model.grpc.model.common.ResultOrBuilder {
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return com.tzld.piaoquan.recommend.model.grpc.model.common.Common.internal_static_Result_descriptor;
+    }
+
+    @java.lang.Override
+    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return com.tzld.piaoquan.recommend.model.grpc.model.common.Common.internal_static_Result_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              com.tzld.piaoquan.recommend.model.grpc.model.common.Result.class, com.tzld.piaoquan.recommend.model.grpc.model.common.Result.Builder.class);
+    }
+
+    // Construct using com.tzld.piaoquan.recommend.model.grpc.model.common.Result.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();
+      code_ = 0;
+
+      message_ = "";
+
+      return this;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.Descriptor
+        getDescriptorForType() {
+      return com.tzld.piaoquan.recommend.model.grpc.model.common.Common.internal_static_Result_descriptor;
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.model.grpc.model.common.Result getDefaultInstanceForType() {
+      return com.tzld.piaoquan.recommend.model.grpc.model.common.Result.getDefaultInstance();
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.model.grpc.model.common.Result build() {
+      com.tzld.piaoquan.recommend.model.grpc.model.common.Result result = buildPartial();
+      if (!result.isInitialized()) {
+        throw newUninitializedMessageException(result);
+      }
+      return result;
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.model.grpc.model.common.Result buildPartial() {
+      com.tzld.piaoquan.recommend.model.grpc.model.common.Result result = new com.tzld.piaoquan.recommend.model.grpc.model.common.Result(this);
+      result.code_ = code_;
+      result.message_ = message_;
+      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.model.grpc.model.common.Result) {
+        return mergeFrom((com.tzld.piaoquan.recommend.model.grpc.model.common.Result)other);
+      } else {
+        super.mergeFrom(other);
+        return this;
+      }
+    }
+
+    public Builder mergeFrom(com.tzld.piaoquan.recommend.model.grpc.model.common.Result other) {
+      if (other == com.tzld.piaoquan.recommend.model.grpc.model.common.Result.getDefaultInstance()) return this;
+      if (other.getCode() != 0) {
+        setCode(other.getCode());
+      }
+      if (!other.getMessage().isEmpty()) {
+        message_ = other.message_;
+        onChanged();
+      }
+      this.mergeUnknownFields(other.unknownFields);
+      onChanged();
+      return this;
+    }
+
+    @java.lang.Override
+    public final boolean isInitialized() {
+      return true;
+    }
+
+    @java.lang.Override
+    public Builder mergeFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      com.tzld.piaoquan.recommend.model.grpc.model.common.Result parsedMessage = null;
+      try {
+        parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        parsedMessage = (com.tzld.piaoquan.recommend.model.grpc.model.common.Result) e.getUnfinishedMessage();
+        throw e.unwrapIOException();
+      } finally {
+        if (parsedMessage != null) {
+          mergeFrom(parsedMessage);
+        }
+      }
+      return this;
+    }
+
+    private int code_ ;
+    /**
+     * <pre>
+     * 1 成功
+     * </pre>
+     *
+     * <code>int32 code = 1;</code>
+     * @return The code.
+     */
+    @java.lang.Override
+    public int getCode() {
+      return code_;
+    }
+    /**
+     * <pre>
+     * 1 成功
+     * </pre>
+     *
+     * <code>int32 code = 1;</code>
+     * @param value The code to set.
+     * @return This builder for chaining.
+     */
+    public Builder setCode(int value) {
+      
+      code_ = value;
+      onChanged();
+      return this;
+    }
+    /**
+     * <pre>
+     * 1 成功
+     * </pre>
+     *
+     * <code>int32 code = 1;</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearCode() {
+      
+      code_ = 0;
+      onChanged();
+      return this;
+    }
+
+    private java.lang.Object message_ = "";
+    /**
+     * <code>string message = 2;</code>
+     * @return The message.
+     */
+    public java.lang.String getMessage() {
+      java.lang.Object ref = message_;
+      if (!(ref instanceof java.lang.String)) {
+        com.google.protobuf.ByteString bs =
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        message_ = s;
+        return s;
+      } else {
+        return (java.lang.String) ref;
+      }
+    }
+    /**
+     * <code>string message = 2;</code>
+     * @return The bytes for message.
+     */
+    public com.google.protobuf.ByteString
+        getMessageBytes() {
+      java.lang.Object ref = message_;
+      if (ref instanceof String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        message_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+    /**
+     * <code>string message = 2;</code>
+     * @param value The message to set.
+     * @return This builder for chaining.
+     */
+    public Builder setMessage(
+        java.lang.String value) {
+      if (value == null) {
+    throw new NullPointerException();
+  }
+  
+      message_ = value;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string message = 2;</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearMessage() {
+      
+      message_ = getDefaultInstance().getMessage();
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string message = 2;</code>
+     * @param value The bytes for message to set.
+     * @return This builder for chaining.
+     */
+    public Builder setMessageBytes(
+        com.google.protobuf.ByteString value) {
+      if (value == null) {
+    throw new NullPointerException();
+  }
+  checkByteStringIsUtf8(value);
+      
+      message_ = value;
+      onChanged();
+      return this;
+    }
+    @java.lang.Override
+    public final Builder setUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.setUnknownFields(unknownFields);
+    }
+
+    @java.lang.Override
+    public final Builder mergeUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.mergeUnknownFields(unknownFields);
+    }
+
+
+    // @@protoc_insertion_point(builder_scope:Result)
+  }
+
+  // @@protoc_insertion_point(class_scope:Result)
+  private static final com.tzld.piaoquan.recommend.model.grpc.model.common.Result DEFAULT_INSTANCE;
+  static {
+    DEFAULT_INSTANCE = new com.tzld.piaoquan.recommend.model.grpc.model.common.Result();
+  }
+
+  public static com.tzld.piaoquan.recommend.model.grpc.model.common.Result getDefaultInstance() {
+    return DEFAULT_INSTANCE;
+  }
+
+  private static final com.google.protobuf.Parser<Result>
+      PARSER = new com.google.protobuf.AbstractParser<Result>() {
+    @java.lang.Override
+    public Result parsePartialFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return new Result(input, extensionRegistry);
+    }
+  };
+
+  public static com.google.protobuf.Parser<Result> parser() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.google.protobuf.Parser<Result> getParserForType() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.tzld.piaoquan.recommend.model.grpc.model.common.Result getDefaultInstanceForType() {
+    return DEFAULT_INSTANCE;
+  }
+
+}
+

+ 31 - 0
recommend-model-client/src/main/java/com/tzld/piaoquan/recommend/model/grpc/model/common/ResultOrBuilder.java

@@ -0,0 +1,31 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/model/common.proto
+
+package com.tzld.piaoquan.recommend.model.grpc.model.common;
+
+public interface ResultOrBuilder extends
+    // @@protoc_insertion_point(interface_extends:Result)
+    com.google.protobuf.MessageOrBuilder {
+
+  /**
+   * <pre>
+   * 1 成功
+   * </pre>
+   *
+   * <code>int32 code = 1;</code>
+   * @return The code.
+   */
+  int getCode();
+
+  /**
+   * <code>string message = 2;</code>
+   * @return The message.
+   */
+  java.lang.String getMessage();
+  /**
+   * <code>string message = 2;</code>
+   * @return The bytes for message.
+   */
+  com.google.protobuf.ByteString
+      getMessageBytes();
+}

+ 9 - 0
recommend-model-client/src/main/proto/com/tzld/piaoquan/recommend/model/common.proto

@@ -0,0 +1,9 @@
+syntax = "proto3";
+
+option java_multiple_files = true;
+option java_package = "com.tzld.piaoquan.recommend.model.grpc.model.common";
+
+message Result {
+  int32 code = 1; // 1 成功
+  string message = 2;
+}

+ 23 - 0
recommend-model-client/src/main/proto/com/tzld/piaoquan/recommend/model/predict.proto

@@ -0,0 +1,23 @@
+syntax = "proto3";
+
+import "google/protobuf/any.proto";
+import "com/tzld/piaoquan/recommend/model/common.proto";
+
+option java_multiple_files = true;
+option java_package = "com.tzld.piaoquan.recommend.model.grpc.model";
+option java_generic_services = true;
+
+message PredictRequest {
+  string param = 1;
+  string model_name = 2;
+}
+
+message PredictResponse {
+  Result result = 1;
+  string data = 2;
+}
+
+
+service PredictService {
+  rpc predict (PredictRequest) returns (PredictResponse);
+}

+ 130 - 0
recommend-model-service/pom.xml

@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.tzld.piaoquan</groupId>
+        <artifactId>recommend-model</artifactId>
+        <version>1.0.0</version>
+    </parent>
+    <artifactId>recommend-model-service</artifactId>
+    <name>recommend-model-service</name>
+    <description>recommend-model-service</description>
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-commons</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-pool2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.junit.vintage</groupId>
+                    <artifactId>junit-vintage-engine</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.ctrip.framework.apollo</groupId>
+            <artifactId>apollo-client</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.csp</groupId>
+            <artifactId>sentinel-datasource-apollo</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.aliyun.openservices</groupId>
+            <artifactId>aliyun-log-logback-appender</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>net.devh</groupId>
+            <artifactId>grpc-server-spring-boot-starter</artifactId>
+            <version>2.9.0.RELEASE</version>
+        </dependency>
+        <dependency>
+            <groupId>com.tzld.piaoquan</groupId>
+            <artifactId>recommend-model-client</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.protobuf</groupId>
+            <artifactId>protobuf-java</artifactId>
+            <version>3.12.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>1.18.16</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-client</artifactId>
+            <version>1.9.1</version>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-core</artifactId>
+            <version>1.9.1</version>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.jersey.contribs</groupId>
+            <artifactId>jersey-apache-client4</artifactId>
+            <version>1.9.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-collections4</artifactId>
+            <version>4.1</version>
+        </dependency>
+    </dependencies>
+    <build>
+        <finalName>recommend-model-service</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>com.tzld.piaoquan.recommend.model.Application</mainClass>
+                    <layout>ZIP</layout>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+
+    </build>
+
+</project>

+ 27 - 0
recommend-model-service/src/main/java/com/tzld/piaoquan/recommend/model/Application.java

@@ -0,0 +1,27 @@
+package com.tzld.piaoquan.recommend.feature;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration;
+import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+
+/**
+ * https://github.com/grpc-swagger/grpc-swagger/blob/master/README_CN.md
+ */
+@SpringBootApplication(exclude = {
+        RedisReactiveAutoConfiguration.class
+})
+@ComponentScan({
+        "com.tzld.piaoquan.recommend.model.service",
+        "com.tzld.piaoquan.recommend.model.grpcservice",
+        "com.tzld.piaoquan.recommend.model.web"
+})
+@EnableEurekaClient
+@EnableAspectJAutoProxy
+public class Application {
+    public static void main(String[] args) {
+        SpringApplication.run(Application.class, args);
+    }
+}

+ 40 - 0
recommend-model-service/src/main/java/com/tzld/piaoquan/recommend/model/common/CommonThreadPoolExecutor.java

@@ -0,0 +1,40 @@
+package com.tzld.piaoquan.recommend.feature.common;
+
+import org.slf4j.MDC;
+
+import java.util.Map;
+import java.util.concurrent.*;
+
+/**
+ * @author dyp
+ */
+public class CommonThreadPoolExecutor extends ThreadPoolExecutor {
+    public CommonThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
+        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
+    }
+
+    public CommonThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
+        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
+    }
+
+    public CommonThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
+        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
+    }
+
+    public CommonThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
+        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
+    }
+
+    @Override
+    public void execute(Runnable command) {
+        Map<String, String> mdcMap = MDC.getCopyOfContextMap();
+        super.execute(() -> {
+            if (mdcMap != null) {
+                MDC.setContextMap(mdcMap);
+            }
+            command.run();
+            MDC.clear();
+        });
+    }
+
+}

+ 358 - 0
recommend-model-service/src/main/java/com/tzld/piaoquan/recommend/model/common/RecommendLoghubAppender.java

@@ -0,0 +1,358 @@
+package com.tzld.piaoquan.recommend.feature.common;
+
+import ch.qos.logback.classic.spi.IThrowableProxy;
+import ch.qos.logback.classic.spi.LoggingEvent;
+import ch.qos.logback.classic.spi.StackTraceElementProxy;
+import ch.qos.logback.classic.spi.ThrowableProxyUtil;
+import ch.qos.logback.core.CoreConstants;
+import ch.qos.logback.core.UnsynchronizedAppenderBase;
+import ch.qos.logback.core.encoder.Encoder;
+import com.aliyun.openservices.aliyun.log.producer.LogProducer;
+import com.aliyun.openservices.aliyun.log.producer.Producer;
+import com.aliyun.openservices.aliyun.log.producer.ProducerConfig;
+import com.aliyun.openservices.aliyun.log.producer.ProjectConfig;
+import com.aliyun.openservices.aliyun.log.producer.errors.ProducerException;
+import com.aliyun.openservices.log.common.LogItem;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+
+import java.time.Instant;
+import java.time.ZoneId;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * @author dyp
+ */
+public class RecommendLoghubAppender<E> extends UnsynchronizedAppenderBase<E> {
+    private String project;
+    private String endpoint;
+    private String accessKeyId;
+    private String accessKeySecret;
+    private String userAgent = "logback";
+    protected Encoder<E> encoder;
+    protected ProducerConfig producerConfig = new ProducerConfig();
+    protected ProjectConfig projectConfig;
+    protected Producer producer;
+    protected String logStore;
+    protected String topic = "";
+    protected String source = "";
+    protected String timeZone = "UTC";
+    protected String timeFormat = "yyyy-MM-dd'T'HH:mmZ";
+    protected DateTimeFormatter formatter;
+    protected java.time.format.DateTimeFormatter formatter1;
+    private String mdcFields;
+
+    public RecommendLoghubAppender() {
+    }
+
+    public void start() {
+        try {
+            this.doStart();
+        } catch (Exception var2) {
+            this.addError("Failed to start LoghubAppender.", var2);
+        }
+
+    }
+
+    private void doStart() {
+        try {
+            this.formatter = DateTimeFormat.forPattern(this.timeFormat).withZone(DateTimeZone.forID(this.timeZone));
+        } catch (Exception var2) {
+            this.formatter1 = java.time.format.DateTimeFormatter.ofPattern(this.timeFormat).withZone(ZoneId.of(this.timeZone));
+        }
+
+        this.producer = this.createProducer();
+        super.start();
+    }
+
+    public Producer createProducer() {
+        this.projectConfig = this.buildProjectConfig();
+        Producer producer = new LogProducer(this.producerConfig);
+        producer.putProjectConfig(this.projectConfig);
+        return producer;
+    }
+
+    private ProjectConfig buildProjectConfig() {
+        return new ProjectConfig(this.project, this.endpoint, this.accessKeyId, this.accessKeySecret, (String) null, this.userAgent);
+    }
+
+    public void stop() {
+        try {
+            this.doStop();
+        } catch (Exception var2) {
+            this.addError("Failed to stop LoghubAppender.", var2);
+        }
+
+    }
+
+    private void doStop() throws InterruptedException, ProducerException {
+        if (this.isStarted()) {
+            super.stop();
+            this.producer.close();
+        }
+    }
+
+    public void append(E eventObject) {
+        try {
+            this.appendEvent(eventObject);
+        } catch (Exception var3) {
+            this.addError("Failed to append event.", var3);
+        }
+
+    }
+
+    private void appendEvent(E eventObject) {
+        if (eventObject instanceof LoggingEvent) {
+            LoggingEvent event = (LoggingEvent) eventObject;
+            List<LogItem> logItems = new ArrayList();
+            LogItem item = new LogItem();
+            logItems.add(item);
+            item.SetTime((int) (event.getTimeStamp() / 1000L));
+            if (this.formatter != null) {
+                DateTime dateTime = new DateTime(event.getTimeStamp());
+                item.PushBack("time", dateTime.toString(this.formatter));
+            } else {
+                Instant instant = Instant.ofEpochMilli(event.getTimeStamp());
+                item.PushBack("time", this.formatter1.format(instant));
+            }
+
+            item.PushBack("level", event.getLevel().toString());
+            item.PushBack("thread", event.getThreadName());
+            StackTraceElement[] caller = event.getCallerData();
+            if (caller != null && caller.length > 0) {
+                item.PushBack("location", caller[0].toString());
+            }
+
+            String message = event.getFormattedMessage();
+            item.PushBack("message", message);
+            IThrowableProxy iThrowableProxy = event.getThrowableProxy();
+            if (iThrowableProxy != null) {
+                String throwable = this.getExceptionInfo(iThrowableProxy);
+                throwable = throwable + this.fullDump(event.getThrowableProxy().getStackTraceElementProxyArray());
+                item.PushBack("throwable", throwable);
+            }
+
+            if (this.encoder != null) {
+                item.PushBack("log", new String(this.encoder.encode(eventObject)));
+            }
+
+            Optional.ofNullable(this.mdcFields).ifPresent((f) -> {
+                event.getMDCPropertyMap().entrySet().stream().filter((v) -> {
+                    return Arrays.stream(f.split(",")).anyMatch((i) -> {
+                        return i.equals(v.getKey());
+                    });
+                }).forEach((map) -> {
+                    item.PushBack((String) map.getKey(), (String) map.getValue());
+                });
+            });
+
+            try {
+                this.producer.send(this.projectConfig.getProject(), this.logStore, this.topic, this.source, logItems,
+                        new RecommendLoghubAppenderCallback(this, this.projectConfig.getProject(), this.logStore,
+                                this.topic, this.source, logItems));
+            } catch (Exception var9) {
+                this.addError("Failed to send log, project=" + this.project + ", logStore=" + this.logStore + ", topic=" + this.topic + ", source=" + this.source + ", logItem=" + logItems, var9);
+            }
+
+        }
+    }
+
+    public String getTimeFormat() {
+        return this.timeFormat;
+    }
+
+    public void setTimeFormat(String timeFormat) {
+        this.timeFormat = timeFormat;
+    }
+
+    private String getExceptionInfo(IThrowableProxy iThrowableProxy) {
+        String s = iThrowableProxy.getClassName();
+        String message = iThrowableProxy.getMessage();
+        return message != null ? s + ": " + message : s;
+    }
+
+    private String fullDump(StackTraceElementProxy[] stackTraceElementProxyArray) {
+        StringBuilder builder = new StringBuilder();
+        StackTraceElementProxy[] var3 = stackTraceElementProxyArray;
+        int var4 = stackTraceElementProxyArray.length;
+
+        for (int var5 = 0; var5 < var4; ++var5) {
+            StackTraceElementProxy step = var3[var5];
+            builder.append(CoreConstants.LINE_SEPARATOR);
+            String string = step.toString();
+            builder.append('\t').append(string);
+            ThrowableProxyUtil.subjoinPackagingData(builder, step);
+        }
+
+        return builder.toString();
+    }
+
+    public String getLogStore() {
+        return this.logStore;
+    }
+
+    public void setLogStore(String logStore) {
+        this.logStore = logStore;
+    }
+
+    public String getTopic() {
+        return this.topic;
+    }
+
+    public void setTopic(String topic) {
+        this.topic = topic;
+    }
+
+    public String getSource() {
+        return this.source;
+    }
+
+    public void setSource(String source) {
+        this.source = source;
+    }
+
+    public String getTimeZone() {
+        return this.timeZone;
+    }
+
+    public void setTimeZone(String timeZone) {
+        this.timeZone = timeZone;
+    }
+
+    public String getProject() {
+        return this.project;
+    }
+
+    public void setProject(String project) {
+        this.project = project;
+    }
+
+    public String getEndpoint() {
+        return this.endpoint;
+    }
+
+    public void setEndpoint(String endpoint) {
+        this.endpoint = endpoint;
+    }
+
+    public String getAccessKeyId() {
+        return this.accessKeyId;
+    }
+
+    public void setAccessKeyId(String accessKeyId) {
+        this.accessKeyId = accessKeyId;
+    }
+
+    public String getAccessKeySecret() {
+        return this.accessKeySecret;
+    }
+
+    public void setAccessKeySecret(String accessKeySecret) {
+        this.accessKeySecret = accessKeySecret;
+    }
+
+    public String getUserAgent() {
+        return this.userAgent;
+    }
+
+    public void setUserAgent(String userAgent) {
+        this.userAgent = userAgent;
+    }
+
+    public int getTotalSizeInBytes() {
+        return this.producerConfig.getTotalSizeInBytes();
+    }
+
+    public void setTotalSizeInBytes(int totalSizeInBytes) {
+        this.producerConfig.setTotalSizeInBytes(totalSizeInBytes);
+    }
+
+    public long getMaxBlockMs() {
+        return this.producerConfig.getMaxBlockMs();
+    }
+
+    public void setMaxBlockMs(long maxBlockMs) {
+        this.producerConfig.setMaxBlockMs(maxBlockMs);
+    }
+
+    public int getIoThreadCount() {
+        return this.producerConfig.getIoThreadCount();
+    }
+
+    public void setIoThreadCount(int ioThreadCount) {
+        this.producerConfig.setIoThreadCount(ioThreadCount);
+    }
+
+    public int getBatchSizeThresholdInBytes() {
+        return this.producerConfig.getBatchSizeThresholdInBytes();
+    }
+
+    public void setBatchSizeThresholdInBytes(int batchSizeThresholdInBytes) {
+        this.producerConfig.setBatchSizeThresholdInBytes(batchSizeThresholdInBytes);
+    }
+
+    public int getBatchCountThreshold() {
+        return this.producerConfig.getBatchCountThreshold();
+    }
+
+    public void setBatchCountThreshold(int batchCountThreshold) {
+        this.producerConfig.setBatchCountThreshold(batchCountThreshold);
+    }
+
+    public int getLingerMs() {
+        return this.producerConfig.getLingerMs();
+    }
+
+    public void setLingerMs(int lingerMs) {
+        this.producerConfig.setLingerMs(lingerMs);
+    }
+
+    public int getRetries() {
+        return this.producerConfig.getRetries();
+    }
+
+    public void setRetries(int retries) {
+        this.producerConfig.setRetries(retries);
+    }
+
+    public int getMaxReservedAttempts() {
+        return this.producerConfig.getMaxReservedAttempts();
+    }
+
+    public void setMaxReservedAttempts(int maxReservedAttempts) {
+        this.producerConfig.setMaxReservedAttempts(maxReservedAttempts);
+    }
+
+    public long getBaseRetryBackoffMs() {
+        return this.producerConfig.getBaseRetryBackoffMs();
+    }
+
+    public void setBaseRetryBackoffMs(long baseRetryBackoffMs) {
+        this.producerConfig.setBaseRetryBackoffMs(baseRetryBackoffMs);
+    }
+
+    public long getMaxRetryBackoffMs() {
+        return this.producerConfig.getMaxRetryBackoffMs();
+    }
+
+    public void setMaxRetryBackoffMs(long maxRetryBackoffMs) {
+        this.producerConfig.setMaxRetryBackoffMs(maxRetryBackoffMs);
+    }
+
+    public Encoder<E> getEncoder() {
+        return this.encoder;
+    }
+
+    public void setEncoder(Encoder<E> encoder) {
+        this.encoder = encoder;
+    }
+
+    public void setMdcFields(String mdcFields) {
+        this.mdcFields = mdcFields;
+    }
+}

+ 37 - 0
recommend-model-service/src/main/java/com/tzld/piaoquan/recommend/model/common/RecommendLoghubAppenderCallback.java

@@ -0,0 +1,37 @@
+package com.tzld.piaoquan.recommend.feature.common;
+
+
+import com.aliyun.openservices.aliyun.log.producer.Callback;
+import com.aliyun.openservices.aliyun.log.producer.Result;
+import com.aliyun.openservices.log.common.LogItem;
+
+import java.util.List;
+
+/**
+ * @author dyp
+ */
+public class RecommendLoghubAppenderCallback<E> implements Callback {
+    protected RecommendLoghubAppender<E> loghubAppender;
+    protected String project;
+    protected String logstore;
+    protected String topic;
+    protected String source;
+    protected List<LogItem> logItems;
+
+    public RecommendLoghubAppenderCallback(RecommendLoghubAppender<E> loghubAppender, String project, String logstore,
+                                           String topic, String source, List<LogItem> logItems) {
+        this.loghubAppender = loghubAppender;
+        this.project = project;
+        this.logstore = logstore;
+        this.topic = topic;
+        this.source = source;
+        this.logItems = logItems;
+    }
+
+    public void onCompletion(Result result) {
+        if (!result.isSuccessful()) {
+            this.loghubAppender.addError("Failed to send log, project=" + this.project + ", logStore=" + this.logstore + ", topic=" + this.topic + ", source=" + this.source + ", logItem=" + this.logItems + ", errorCode=" + result.getErrorCode() + ", errorMessage=" + result.getErrorMessage());
+        }
+
+    }
+}

+ 37 - 0
recommend-model-service/src/main/java/com/tzld/piaoquan/recommend/model/common/ThreadPoolFactory.java

@@ -0,0 +1,37 @@
+package com.tzld.piaoquan.recommend.feature.common;
+
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author dyp
+ */
+public final class ThreadPoolFactory {
+    private final static ExecutorService DEFAULT = new CommonThreadPoolExecutor(
+            128,
+            128,
+            0L, TimeUnit.SECONDS,
+            new LinkedBlockingQueue<>(1000),
+            new ThreadFactoryBuilder().setNameFormat("DEFAULT-%d").build(),
+            new ThreadPoolExecutor.AbortPolicy());
+    private final static ExecutorService MULTI_GET_FEATURE = new CommonThreadPoolExecutor(
+            256,
+            256,
+            0L, TimeUnit.SECONDS,
+            new LinkedBlockingQueue<>(1000),
+            new ThreadFactoryBuilder().setNameFormat("MultiGetFeaturePool-%d").build(),
+            new ThreadPoolExecutor.AbortPolicy());
+
+    public static ExecutorService defaultPool() {
+        return DEFAULT;
+    }
+
+    public static ExecutorService multiGetFeaturePool() {
+        return MULTI_GET_FEATURE;
+    }
+
+}

+ 41 - 0
recommend-model-service/src/main/java/com/tzld/piaoquan/recommend/model/util/CommonCollectionUtils.java

@@ -0,0 +1,41 @@
+package com.tzld.piaoquan.recommend.feature.util;
+
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.MapUtils;
+
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * @author dyp
+ */
+public class CommonCollectionUtils {
+    public static <T, R> List<R> toList(Collection<T> list, Function<T, R> map) {
+        if (CollectionUtils.isEmpty(list)) {
+            return Collections.emptyList();
+        }
+        return list.stream().map(map).collect(Collectors.toList());
+    }
+
+    public static <T, K, V> Map<K, V> toMap(List<T> list, Function<T, K> keyFunc, Function<T, V> valueFunc) {
+        if (CollectionUtils.isEmpty(list)) {
+            return Collections.emptyMap();
+        }
+        return list.stream().collect(Collectors.toMap(keyFunc::apply, valueFunc::apply));
+    }
+
+    public static <K, V> Map<K, V> merge(Map<K, V> map1, Map<K, V> map2) {
+        if (MapUtils.isEmpty(map1)) {
+            return map2;
+        }
+        if (MapUtils.isEmpty(map2)) {
+            return map1;
+        }
+
+        Map<K, V> map = new HashMap<>();
+        map.putAll(map1);
+        map.putAll(map2);
+        return map;
+    }
+}

+ 30 - 0
recommend-model-service/src/main/java/com/tzld/piaoquan/recommend/model/util/DateUtils.java

@@ -0,0 +1,30 @@
+package com.tzld.piaoquan.recommend.feature.util;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * @author dyp
+ */
+public final class DateUtils {
+    public static String getCurrentDateStr(String format) {
+        SimpleDateFormat dateFormat = new SimpleDateFormat(format);
+        Date currentDate = new Date();
+        return dateFormat.format(currentDate);
+    }
+
+    public static int getCurrentHour() {
+        Calendar calendar = Calendar.getInstance();
+        return calendar.get(Calendar.HOUR_OF_DAY);
+    }
+
+    public static String getBeforeDaysDateStr(String format, int d) {
+        SimpleDateFormat dateFormat = new SimpleDateFormat(format);
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(new Date());
+        calendar.add(Calendar.DAY_OF_MONTH, -d);
+        Date previousDate = calendar.getTime();
+        return dateFormat.format(previousDate);
+    }
+}

+ 96 - 0
recommend-model-service/src/main/java/com/tzld/piaoquan/recommend/model/util/HttpClientFactory.java

@@ -0,0 +1,96 @@
+package com.tzld.piaoquan.recommend.feature.util;
+
+import com.google.common.collect.Lists;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.concurrent.BasicThreadFactory;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.config.Registry;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.HttpClientConnectionManager;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
+import org.apache.http.conn.socket.PlainConnectionSocketFactory;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.ssl.SSLContexts;
+import org.slf4j.MDC;
+
+import javax.net.ssl.SSLContext;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.util.List;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+@Slf4j
+public class HttpClientFactory {
+
+    private static final ScheduledExecutorService SCHEDULED_CLOSED_EXECUTOR = new ScheduledThreadPoolExecutor(1,
+            new BasicThreadFactory.Builder().namingPattern("http conn-closed-thread-%s").priority(Thread.NORM_PRIORITY).daemon(false).build(), (r, e) -> log.error(" monitor push reject task error={}", e.toString()));
+
+    private static final List<HttpClientConnectionManager> HTTP_CLIENT_CONNECTION_MANAGERS = Lists.newArrayList();
+
+    static {
+        SCHEDULED_CLOSED_EXECUTOR.schedule(() -> HTTP_CLIENT_CONNECTION_MANAGERS.forEach(HttpClientConnectionManager::closeExpiredConnections), 5, TimeUnit.SECONDS);
+    }
+
+    private static HttpRequestInterceptor getInterceptor() {
+        HttpRequestInterceptor requestInterceptor = (request, context) -> {
+            try {
+                String missSpanId = MDC.get("missSpanId");
+                String missTraceId = MDC.get("request-id");
+                if (missTraceId != null && !"".equals(missTraceId.trim())) {
+                    request.setHeader("request-id", missTraceId);
+                }
+                if (missSpanId != null && !"".equals(missSpanId.trim())) {
+                    request.setHeader("missSpanId", missSpanId);
+                }
+            } catch (Exception e) {
+                log.error(e.getMessage(), e);
+            }
+        };
+        return requestInterceptor;
+    }
+
+    /**
+     * @param connectTimeout 连接超时时间 ms
+     * @param socketTimeout  读超时时间(等待数据超时时间)ms
+     * @param maxPerRoute    每个路由的最大连接数
+     * @param maxTotal       最大连接数
+     * @param retryCount     重试次数
+     * @return httpclient instance
+     */
+    public static CloseableHttpClient create(int connectTimeout, int socketTimeout, int maxPerRoute, int maxTotal,
+                                             int retryCount, int connectionWaitTimeout) {
+        try {
+            RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(connectTimeout).setSocketTimeout(socketTimeout).setConnectionRequestTimeout(connectionWaitTimeout).build();
+            CloseableHttpClient client = HttpClientBuilder.create()
+                    .setDefaultRequestConfig(requestConfig)
+                    .setConnectionManager(createConnectionManager(maxPerRoute, maxTotal))
+                    .setRetryHandler(new DefaultHttpRequestRetryHandler(retryCount, false)).addInterceptorFirst(getInterceptor()).build();
+            return client;
+        } catch (Throwable e) {
+            log.error("create HttpPoolClient exception", e);
+            throw new RuntimeException("create HttpPoolClient exception");
+        }
+    }
+
+    private static PoolingHttpClientConnectionManager createConnectionManager(int maxPerRoute, int maxTotal) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
+        SSLContext sslContext = SSLContexts.custom().loadTrustMaterial((chain, authType) -> true).build();
+        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
+                .register("http", PlainConnectionSocketFactory.getSocketFactory())
+                .register("https", new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE)).build();
+        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
+        cm.setDefaultMaxPerRoute(maxPerRoute);
+        cm.setMaxTotal(maxTotal);
+        HTTP_CLIENT_CONNECTION_MANAGERS.add(cm);
+        return cm;
+    }
+
+}

+ 38 - 0
recommend-model-service/src/main/java/com/tzld/piaoquan/recommend/model/util/JSONUtils.java

@@ -0,0 +1,38 @@
+package com.tzld.piaoquan.recommend.feature.util;
+
+import com.alibaba.fastjson.JSONObject;
+import com.google.common.reflect.TypeToken;
+import com.google.gson.Gson;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+@Slf4j
+public class JSONUtils {
+
+
+    public static String toJson(Object obj) {
+        if (obj == null) {
+            return "";
+        }
+        try {
+            return new Gson().toJson(obj);
+        } catch (Exception e) {
+            log.error("toJson exception", e);
+            return "";
+        }
+    }
+
+    public static <T> T fromJson(String value, TypeToken<T> typeToken, T defaultValue) {
+
+        if (StringUtils.isBlank(value)) {
+            return defaultValue;
+        }
+        try {
+            return JSONObject.parseObject(value, typeToken.getType());
+        } catch (Exception e) {
+            log.error("parseObject error! value=[{}]", value, e);
+        }
+        return defaultValue;
+    }
+
+}

+ 48 - 0
recommend-model-service/src/main/java/com/tzld/piaoquan/recommend/model/util/TraceUtils.java

@@ -0,0 +1,48 @@
+package com.tzld.piaoquan.recommend.feature.util;
+
+import org.apache.commons.lang.RandomStringUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.MDC;
+
+import java.net.InetAddress;
+
+/**
+ * @author dyp
+ */
+public class TraceUtils {
+    public static final String TRACE_ID_KEY = "traceId";
+    public static final String DEFAULT_TRACE_ID = "default_trace_id";
+
+    public static void setMDC() {
+        MDC.put(TraceUtils.TRACE_ID_KEY, genTraceId());
+    }
+
+    public static void removeMDC() {
+        MDC.remove(TraceUtils.TRACE_ID_KEY);
+    }
+
+    public static String genTraceId() {
+        StringBuilder traceIdBuilder = new StringBuilder();
+        InetAddress address = InetAddress.getLoopbackAddress();
+        if (address == null) {
+            return DEFAULT_TRACE_ID;
+        }
+        String addressStr = address.getHostAddress();
+        if (StringUtils.isBlank(addressStr)) {
+            return DEFAULT_TRACE_ID;
+        }
+        String[] addressArr = addressStr.split("\\.");
+        if (addressArr.length != 4) {
+            return DEFAULT_TRACE_ID;
+        }
+
+        traceIdBuilder.append(StringUtils.leftPad(Integer.toHexString(Integer.valueOf(addressArr[0])), 2, '0'))
+                .append(StringUtils.leftPad(Integer.toHexString(Integer.valueOf(addressArr[1])), 2, '0'))
+                .append(StringUtils.leftPad(Integer.toHexString(Integer.valueOf(addressArr[2])), 2, '0'))
+                .append(StringUtils.leftPad(Integer.toHexString(Integer.valueOf(addressArr[3])), 2, '0'))
+                .append(System.currentTimeMillis())
+                .append(RandomStringUtils.randomAlphabetic(4))
+                .append(Thread.currentThread().getId());
+        return traceIdBuilder.toString();
+    }
+}

+ 18 - 0
recommend-model-service/src/main/java/com/tzld/piaoquan/recommend/model/web/HealthCheckController.java

@@ -0,0 +1,18 @@
+package com.tzld.piaoquan.recommend.feature.web;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author dyp
+ */
+@RestController
+@Slf4j
+public class HealthCheckController {
+
+    @GetMapping("/ok")
+    public String ok() {
+        return "ok";
+    }
+}

+ 55 - 0
recommend-model-service/src/main/resources/application-dev.yml

@@ -0,0 +1,55 @@
+server:
+  port: 8000
+grpc:
+  server:
+    port: 9000
+    reflection-service-enabled: true
+
+eureka:
+  instance:
+    prefer-ip-address: true #是否优先使用IP地址作为主机名的标识,默认false
+    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port} #注册到eureka上的唯一实例ID
+    lease-renewal-interval-in-seconds: 10 #表示eureka client发送心跳给server端的频率,默认30
+    lease-expiration-duration-in-seconds: 30 #表示eureka server至上一次收到client的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则将移除该instance,默认90
+  client:
+    registry-fetch-interval-seconds: 5  #定时从Eureka Server拉取服务注册信息的间隔时间
+    serviceUrl:
+      defaultZone: http://deveureka-internal.piaoquantv.com/eureka/
+
+spring:
+  redis:
+    hostName: r-bp1pi8wyv6lzvgjy5z.redis.rds.aliyuncs.com
+    port: 6379
+    password: Wqsd@2019
+    timeout: 1000
+    lettuce:
+      pool:
+        max-active: 8
+        max-wait: -1
+        max-idle: 8
+        min-idle: 0
+  tair:
+    hostName: r-bp1pi8wyv6lzvgjy5z.redis.rds.aliyuncs.com
+    port: 6379
+    password: Wqsd@2019
+    timeout: 1000
+    lettuce:
+      pool:
+        max-active: 8
+        max-wait: -1
+        max-idle: 8
+        min-idle: 0
+
+apollo:
+  meta: http://devapolloconfig-internal.piaoquantv.com
+
+aliyun:
+  log:
+    endpoint: cn-hangzhou.log.aliyuncs.com
+    accessKeyId: LTAIP6x1l3DXfSxm
+    accessKeySecret: KbTaM9ars4OX3PMS6Xm7rtxGr1FLon
+    project: recommend-feature-test
+
+logging:
+  file:
+    path: ./${spring.application.name}/logs/

+ 47 - 0
recommend-model-service/src/main/resources/application-pre.yml

@@ -0,0 +1,47 @@
+server:
+  port: 8080
+
+eureka:
+  instance:
+    prefer-ip-address: true #是否优先使用IP地址作为主机名的标识,默认false
+    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port} #注册到eureka上的唯一实例ID
+    lease-renewal-interval-in-seconds: 10 #表示eureka client发送心跳给server端的频率,默认30
+    lease-expiration-duration-in-seconds: 30 #表示eureka server至上一次收到client的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则将移除该instance,默认90
+  client:
+    registry-fetch-interval-seconds: 5  #定时从Eureka Server拉取服务注册信息的间隔时间
+    serviceUrl:
+      defaultZone: http://preeureka-internal.piaoquantv.com/eureka/
+
+spring:
+  redis:
+    hostName: r-bp1pi8wyv6lzvgjy5z.redis.rds.aliyuncs.com
+    port: 6379
+    password: Wqsd@2019
+    timeout: 1000
+    lettuce:
+      pool:
+        max-active: 8
+        max-wait: -1
+        max-idle: 8
+        min-idle: 0
+  tair:
+    hostName: r-bp1pi8wyv6lzvgjy5z.redis.rds.aliyuncs.com
+    port: 6379
+    password: Wqsd@2019
+    timeout: 1000
+    lettuce:
+      pool:
+        max-active: 8
+        max-wait: -1
+        max-idle: 8
+        min-idle: 0
+
+apollo:
+  meta: http://preapolloconfig-internal.piaoquantv.com
+
+aliyun:
+  log:
+    endpoint: cn-hangzhou-intranet.log.aliyuncs.com
+    accessKeyId: LTAIP6x1l3DXfSxm
+    accessKeySecret: KbTaM9ars4OX3PMS6Xm7rtxGr1FLon
+    project: recommend-feature

+ 47 - 0
recommend-model-service/src/main/resources/application-prod.yml

@@ -0,0 +1,47 @@
+server:
+  port: 8080
+
+eureka:
+  instance:
+    prefer-ip-address: true #是否优先使用IP地址作为主机名的标识,默认false
+    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port} #注册到eureka上的唯一实例ID
+    lease-renewal-interval-in-seconds: 10 #表示eureka client发送心跳给server端的频率,默认30
+    lease-expiration-duration-in-seconds: 30 #表示eureka server至上一次收到client的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则将移除该instance,默认90
+  client:
+    registry-fetch-interval-seconds: 5  #定时从Eureka Server拉取服务注册信息的间隔时间
+    serviceUrl:
+      defaultZone: http://eureka-internal.piaoquantv.com/eureka/
+
+spring:
+  redis:
+    hostName: r-bp1pi8wyv6lzvgjy5z.redis.rds.aliyuncs.com
+    port: 6379
+    password: Wqsd@2019
+    timeout: 1000
+    lettuce:
+      pool:
+        max-active: 8
+        max-wait: -1
+        max-idle: 8
+        min-idle: 0
+  tair:
+    hostName: r-bp1pi8wyv6lzvgjy5z.redis.rds.aliyuncs.com
+    port: 6379
+    password: Wqsd@2019
+    timeout: 1000
+    lettuce:
+      pool:
+        max-active: 8
+        max-wait: -1
+        max-idle: 8
+        min-idle: 0
+
+apollo:
+  meta: http://apolloconfig-internal.piaoquantv.com
+
+aliyun:
+  log:
+    endpoint: cn-hangzhou-intranet.log.aliyuncs.com
+    accessKeyId: LTAIP6x1l3DXfSxm
+    accessKeySecret: KbTaM9ars4OX3PMS6Xm7rtxGr1FLon
+    project: recommend-feature

+ 47 - 0
recommend-model-service/src/main/resources/application-test.yml

@@ -0,0 +1,47 @@
+server:
+  port: 8080
+
+eureka:
+  instance:
+    prefer-ip-address: true #是否优先使用IP地址作为主机名的标识,默认false
+    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port} #注册到eureka上的唯一实例ID
+    lease-renewal-interval-in-seconds: 10 #表示eureka client发送心跳给server端的频率,默认30
+    lease-expiration-duration-in-seconds: 30 #表示eureka server至上一次收到client的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则将移除该instance,默认90
+  client:
+    registry-fetch-interval-seconds: 5  #定时从Eureka Server拉取服务注册信息的间隔时间
+    serviceUrl:
+      defaultZone: http://testeureka-internal.piaoquantv.com/eureka/
+
+spring:
+  redis:
+    hostName: r-bp1wwqqkjqwwkxgbup.redis.rds.aliyuncs.com
+    port: 6379
+    password: Wqsd@2019
+    timeout: 1000
+    lettuce:
+      pool:
+        max-active: 8
+        max-wait: -1
+        max-idle: 8
+        min-idle: 0
+  tair:
+    hostName: r-bp1wwqqkjqwwkxgbup.redis.rds.aliyuncs.com
+    port: 6379
+    password: Wqsd@2019
+    timeout: 1000
+    lettuce:
+      pool:
+        max-active: 8
+        max-wait: -1
+        max-idle: 8
+        min-idle: 0
+
+apollo:
+  meta: http://testapolloconfig-internal.piaoquantv.com
+
+aliyun:
+  log:
+    endpoint: cn-hangzhou-intranet.log.aliyuncs.com
+    accessKeyId: LTAIP6x1l3DXfSxm
+    accessKeySecret: KbTaM9ars4OX3PMS6Xm7rtxGr1FLon
+    project: recommend-feature-test

+ 16 - 0
recommend-model-service/src/main/resources/application.yml

@@ -0,0 +1,16 @@
+spring:
+  profiles:
+    active: dev
+  application:
+    name: recommend-feature
+logging:
+  file:
+    path: /datalog/weblog/${spring.application.name}/
+
+app:
+  id: recommend-feature
+apollo:
+  bootstrap:
+    enabled: true
+    namespaces: application
+  cacheDir: /datalog/apollo-cache-dir

+ 324 - 0
recommend-model-service/src/main/resources/logback-spring.xml

@@ -0,0 +1,324 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
+<!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true -->
+<!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
+<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
+<configuration scan="true" scanPeriod="10 seconds">
+
+    <!--为了防止进程退出时,内存中的数据丢失,请加上此选项-->
+    <shutdownHook class="ch.qos.logback.core.hook.DelayingShutdownHook"/>
+
+    <!-- 日志上下文名称 -->
+    <contextName>logback</contextName>
+
+    <!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。 -->
+    <!-- 日志输出格式 -->
+    <property name="LOG_PATTERN"
+              value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}\\(%L\\) - %msg%n"/>
+    <property name="mdcFields" value="traceId,appType"></property>
+
+    <!-- spring property使用方式 -->
+    <springProperty name="LOG_PATH" source="logging.file.path"/>
+    <springProperty name="aliyun_log_endpoint" source="aliyun.log.endpoint"/>
+    <springProperty name="aliyun_log_accessKeyId" source="aliyun.log.accessKeyId"/>
+    <springProperty name="aliyun_log_accessKeySecret" source="aliyun.log.accessKeySecret"/>
+    <springProperty name="aliyun_log_project" source="aliyun.log.project"/>
+
+    <!-- 彩色日志 -->
+    <!-- 彩色日志依赖的渲染类 -->
+    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
+    <conversionRule conversionWord="wex"
+                    converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
+    <conversionRule conversionWord="wEx"
+                    converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
+    <!-- 彩色日志格式 -->
+    <property name="CONSOLE_LOG_PATTERN"
+              value="${CONSOLE_LOG_PATTERN:-%clr{traceId} %clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}\\(%L\\)){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
+
+    <!--输出到控制台-->
+    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+        <!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>info</level>
+        </filter>
+        <encoder>
+            <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
+            <!-- 设置字符集 -->
+            <charset>UTF-8</charset>
+        </encoder>
+    </appender>
+
+    <!--输出到文件-->
+    <!-- 时间滚动输出 level为 DEBUG 日志 -->
+    <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <!-- 正在记录的日志文件的路径及文件名 -->
+        <file>${LOG_PATH}/debug.log</file>
+        <!--日志文件输出格式-->
+        <encoder>
+            <pattern>${LOG_PATTERN}</pattern>
+            <charset>UTF-8</charset> <!-- 设置字符集 -->
+        </encoder>
+        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志归档 -->
+            <fileNamePattern>${LOG_PATH}/debug/log-debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>100MB</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+            <!--日志文件保留天数-->
+            <maxHistory>15</maxHistory>
+        </rollingPolicy>
+        <!-- 此日志文件只记录debug级别的 -->
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>debug</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!-- 时间滚动输出 level为 INFO 日志 -->
+    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <!-- 正在记录的日志文件的路径及文件名 -->
+        <file>${LOG_PATH}/info.log</file>
+        <!--日志文件输出格式-->
+        <encoder>
+            <pattern>${LOG_PATTERN}</pattern>
+            <charset>UTF-8</charset>
+        </encoder>
+        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 每天日志归档路径以及格式 -->
+            <fileNamePattern>${LOG_PATH}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>100MB</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+            <!--日志文件保留天数-->
+            <maxHistory>15</maxHistory>
+        </rollingPolicy>
+        <!-- 此日志文件只记录info级别的 -->
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>info</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!-- 时间滚动输出 level为 WARN 日志 -->
+    <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <!-- 正在记录的日志文件的路径及文件名 -->
+        <file>${LOG_PATH}/warn.log</file>
+        <!--日志文件输出格式-->
+        <encoder>
+            <pattern>${LOG_PATTERN}</pattern>
+            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
+        </encoder>
+        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${LOG_PATH}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>100MB</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+            <!--日志文件保留天数-->
+            <maxHistory>15</maxHistory>
+        </rollingPolicy>
+        <!-- 此日志文件只记录warn级别的 -->
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>warn</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+
+    <!-- 时间滚动输出 level为 ERROR 日志 -->
+    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <!-- 正在记录的日志文件的路径及文件名 -->
+        <file>${LOG_PATH}/error.log</file>
+        <!--日志文件输出格式-->
+        <encoder>
+            <pattern>${LOG_PATTERN}</pattern>
+            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
+        </encoder>
+        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${LOG_PATH}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>100MB</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+            <!--日志文件保留天数-->
+            <maxHistory>15</maxHistory>
+        </rollingPolicy>
+        <!-- 此日志文件只记录ERROR级别的 -->
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>ERROR</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <appender name="loghubAppenderInfo" class="com.tzld.piaoquan.recommend.feature.common.RecommendLoghubAppender">
+        <!--必选项-->
+        <!-- 账号及网络配置 -->
+        <endpoint>${aliyun_log_endpoint}</endpoint>
+        <accessKeyId>${aliyun_log_accessKeyId}</accessKeyId>
+        <accessKeySecret>${aliyun_log_accessKeySecret}</accessKeySecret>
+        <!-- sls 项目配置 -->
+        <project>${aliyun_log_project}</project>
+        <logStore>info-log</logStore>
+        <!--必选项 (end)-->
+        <!-- 可选项 -->
+        <topic></topic>
+        <source></source>
+        <!-- 可选项 详见 '参数说明'-->
+        <totalSizeInBytes>104857600</totalSizeInBytes>
+        <maxBlockMs>0</maxBlockMs>
+        <ioThreadCount>16</ioThreadCount>
+        <batchSizeThresholdInBytes>524288</batchSizeThresholdInBytes>
+        <batchCountThreshold>4096</batchCountThreshold>
+        <lingerMs>2000</lingerMs>
+        <retries>10</retries>
+        <baseRetryBackoffMs>100</baseRetryBackoffMs>
+        <maxRetryBackoffMs>50000</maxRetryBackoffMs>
+        <!-- 可选项 通过配置 encoder 的 pattern 自定义 log 的格式 -->
+        <!--日志文件输出格式-->
+        <encoder>
+            <pattern>${LOG_PATTERN}</pattern>
+            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>INFO</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+        <mdcFields>
+            ${mdcFields}
+        </mdcFields>
+    </appender>
+
+    <appender name="loghubAppenderWarn" class="com.tzld.piaoquan.recommend.feature.common.RecommendLoghubAppender">
+        <!--必选项-->
+        <!-- 账号及网络配置 -->
+        <endpoint>${aliyun_log_endpoint}</endpoint>
+        <accessKeyId>${aliyun_log_accessKeyId}</accessKeyId>
+        <accessKeySecret>${aliyun_log_accessKeySecret}</accessKeySecret>
+        <!-- sls 项目配置 -->
+        <project>${aliyun_log_project}</project>
+        <logStore>warn-log</logStore>
+        <!--必选项 (end)-->
+        <!-- 可选项 -->
+        <topic></topic>
+        <source></source>
+        <!-- 可选项 详见 '参数说明'-->
+        <totalSizeInBytes>104857600</totalSizeInBytes>
+        <maxBlockMs>0</maxBlockMs>
+        <ioThreadCount>16</ioThreadCount>
+        <batchSizeThresholdInBytes>524288</batchSizeThresholdInBytes>
+        <batchCountThreshold>4096</batchCountThreshold>
+        <lingerMs>2000</lingerMs>
+        <retries>10</retries>
+        <baseRetryBackoffMs>100</baseRetryBackoffMs>
+        <maxRetryBackoffMs>50000</maxRetryBackoffMs>
+        <!-- 可选项 通过配置 encoder 的 pattern 自定义 log 的格式 -->
+        <!--日志文件输出格式-->
+        <encoder>
+            <pattern>${LOG_PATTERN}</pattern>
+            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>WARN</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+        <mdcFields>
+            ${mdcFields}
+        </mdcFields>
+    </appender>
+
+    <appender name="loghubAppenderError" class="com.tzld.piaoquan.recommend.feature.common.RecommendLoghubAppender">
+        <!--必选项-->
+        <!-- 账号及网络配置 -->
+        <endpoint>${aliyun_log_endpoint}</endpoint>
+        <accessKeyId>${aliyun_log_accessKeyId}</accessKeyId>
+        <accessKeySecret>${aliyun_log_accessKeySecret}</accessKeySecret>
+        <!-- sls 项目配置 -->
+        <project>${aliyun_log_project}</project>
+        <logStore>error-log</logStore>
+        <!--必选项 (end)-->
+        <!-- 可选项 -->
+        <topic></topic>
+        <source></source>
+        <!-- 可选项 详见 '参数说明'-->
+        <totalSizeInBytes>104857600</totalSizeInBytes>
+        <maxBlockMs>0</maxBlockMs>
+        <ioThreadCount>16</ioThreadCount>
+        <batchSizeThresholdInBytes>524288</batchSizeThresholdInBytes>
+        <batchCountThreshold>4096</batchCountThreshold>
+        <lingerMs>2000</lingerMs>
+        <retries>10</retries>
+        <baseRetryBackoffMs>100</baseRetryBackoffMs>
+        <maxRetryBackoffMs>50000</maxRetryBackoffMs>
+        <!-- 可选项 通过配置 encoder 的 pattern 自定义 log 的格式 -->
+        <!--日志文件输出格式-->
+        <encoder>
+            <pattern>${LOG_PATTERN}</pattern>
+            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>ERROR</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+        <mdcFields>
+            ${mdcFields}
+        </mdcFields>
+    </appender>
+
+    <!--
+        <logger>用来设置某一个包或者具体的某一个类的日志打印级别、
+        以及指定<appender>。<logger>仅有一个name属性,
+        一个可选的level和一个可选的addtivity属性。
+        name:用来指定受此logger约束的某一个包或者具体的某一个类。
+        level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
+              还有一个特俗值INHERITED或者同义词NULL,代表强制执行上级的级别。
+              如果未设置此属性,那么当前logger将会继承上级的级别。
+        addtivity:是否向上级logger传递打印信息。默认是true。
+    -->
+    <!--<logger name="org.springframework.web" level="info"/>-->
+    <!--<logger name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor" level="INFO"/>-->
+    <!--
+        使用mybatis的时候,sql语句是debug下才会打印,而这里我们只配置了info,所以想要查看sql语句的话,有以下两种操作:
+        第一种把<root level="info">改成<root level="DEBUG">这样就会打印sql,不过这样日志那边会出现很多其他消息
+        第二种就是单独给dao下目录配置debug模式,代码如下,这样配置sql语句会打印,其他还是正常info级别:
+     -->
+
+    <!--
+        root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性
+        level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
+        不能设置为INHERITED或者同义词NULL。默认是DEBUG
+        可以包含零个或多个元素,标识这个appender将会添加到这个logger。
+    -->
+
+    <springProfile name="dev">
+        <logger name="com.tzld.piaoquan.recommend.server" level="info"/>
+    </springProfile>
+    <springProfile name="test">
+        <logger name="com.tzld.piaoquan.recommend.server" level="info"/>
+    </springProfile>
+    <springProfile name="pre">
+        <logger name="com.tzld.piaoquan.recommend.server" level="info"/>
+    </springProfile>
+    <springProfile name="stress">
+        <logger name="com.tzld.piaoquan.recommend.server" level="info"/>
+    </springProfile>
+    <springProfile name="prod">
+        <logger name="com.tzld.piaoquan.recommend.server" level="info"/>
+    </springProfile>
+
+    <root level="info">
+        <appender-ref ref="CONSOLE"/>
+        <appender-ref ref="loghubAppenderInfo"/>
+        <appender-ref ref="loghubAppenderWarn"/>
+        <appender-ref ref="loghubAppenderError"/>
+    </root>
+
+</configuration>

+ 54 - 0
recommend-model-service/src/main/resources/mybatis-generator-config.xml

@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE generatorConfiguration
+        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
+        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
+<!-- 配置生成器 -->
+<generatorConfiguration>
+    <context id="mysql" defaultModelType="flat">
+        <property name="autoDelimitKeywords" value="true"/>
+        <!-- 生成的Java文件的编码 -->
+        <property name="javaFileEncoding" value="UTF-8"/>
+        <!-- 格式化java代码 -->
+        <property name="javaFormatter" value="org.mybatis.generator.api.dom.DefaultJavaFormatter"/>
+        <!-- 格式化XML代码 -->
+        <property name="xmlFormatter" value="org.mybatis.generator.api.dom.DefaultXmlFormatter"/>
+        <!-- beginningDelimiter和endingDelimiter:指明数据库的用于标记数据库对象名的符号,比如ORACLE就是双引号,MYSQL默认是`反引号; -->
+        <property name="beginningDelimiter" value="`"/>
+        <property name="endingDelimiter" value="`"/>
+
+        <plugin type="org.mybatis.generator.plugins.ToStringPlugin"></plugin>
+        <plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin" />
+
+        <commentGenerator>
+            <property name="addRemarkComments" value="true"/>
+        </commentGenerator>
+
+        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
+                        connectionURL="jdbc:mysql://rm-bp1k5853td1r25g3n690.mysql.rds.aliyuncs.com:3306/longvideo?useUnicode=true&amp;characterEncoding=utf-8&amp;zeroDateTimeBehavior=convertToNull&amp;useSSL=false"
+                        userId="wx2016_longvideo" password="wx2016_longvideoP@assword1234">
+        </jdbcConnection>
+
+        <javaTypeResolver type="org.mybatis.generator.internal.types.JavaTypeResolverDefaultImpl">
+            <property name="forceBigDecimals" value="false"/>
+        </javaTypeResolver>
+
+
+
+        <javaModelGenerator targetPackage="com.tzld.piaoquan.recommend.server.model.po" targetProject="src/main/java">
+            <property name="constructorBased" value="false"/>
+            <property name="enableSubPackages" value="true"/>
+            <property name="immutable" value="false"/>
+        </javaModelGenerator>
+
+        <sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources">
+            <property name="enableSubPackages" value="true"/>
+        </sqlMapGenerator>
+
+        <javaClientGenerator targetPackage="com.tzld.piaoquan.recommend.server.dao.mapper" type="XMLMAPPER" targetProject="src/main/java">
+            <property name="enableSubPackages" value="true"/>
+        </javaClientGenerator>
+
+        <table tableName="wx_ab_laboratory_white" domainObjectName="WxAbLaboratoryWhite" alias=""/>
+    </context>
+
+</generatorConfiguration>