grpc接口對接

日萌社

人工智能AI:Keras PyTorch MXNet TensorFlow PaddlePaddle 深度學習實戰(不定時更新)


5.2 grpc接口對接

學習目標

  • 目標
  • 應用

5.2.1 頭條推薦接口對接

  • 請求參數:

    • feed流推薦:用戶ID,頻道ID,推薦文章數量,請求推薦時間戳
    • 相似文章獲取:文章ID,推薦文章數量
  • 返回參數:

    • feed流推薦:曝光參數,每篇文章的所有行爲參數,上一條時間戳

    • # 埋點參數參考:
      # {
      #     "param": '{"action": "exposure", "userId": 1, "articleId": [1,2,3,4],  "algorithmCombine": "c1"}',
      #     "recommends": [
      #         {"article_id": 1, "param": {"click": "{"action": "click", "userId": "1", "articleId": 1, "algorithmCombine": 'c1'}", "collect": "", "share": "","read":""}},
      #         {"article_id": 2, "param": {"click": "", "collect": "", "share": "", "read":""}},
      #         {"article_id": 3, "param": {"click": "", "collect": "", "share": "", "read":""}},
      #         {"article_id": 4, "param": {"click": "", "collect": "", "share": "", "read":""}}
      #     ]
      #     "timestamp": 1546391572
      # }
      
    • 相似文章獲取:文章ID列表

5.2.2 簡介

  • gRPC是由Google公司開源的高性能RPC框架。

  • gRPC支持多語言

    gRPC原生使用C、Java、Go進行了三種實現,而C語言實現的版本進行封裝後又支持C++、C#、Node、ObjC、 Python、Ruby、PHP等開發語言

  • gRPC支持多平臺

    支持的平臺包括:Linux、Android、iOS、MacOS、Windows

  • gRPC的消息協議使用Google自家開源的Protocol Buffers協議機制(proto3) 序列化

  • gRPC的傳輸使用HTTP/2標準,支持雙向流和連接多路複用

使用方法

  1. 使用Protocol Buffers(proto3)的IDL接口定義語言定義接口服務,編寫在文本文件(以.proto爲後綴名)中。
  2. 使用protobuf編譯器生成服務器和客戶端使用的stub代碼

在gRPC中推薦使用proto3版本。

5.2.3 代碼結構

Protocol Buffers版本

Protocol Buffers文檔的第一行非註釋行,爲版本申明,不填寫的話默認爲版本2。

syntax = "proto3";
或者
syntax = "proto2";
  • 消息類型

Protocol Buffers使用message定義消息數據。在Protocol Buffers中使用的數據都是通過message消息數據封裝基本類型數據或其他消息數據,對應Python中的類。

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}
  • 字段編號

消息定義中的每個字段都有唯一的編號。這些字段編號用於以消息二進制格式標識字段,並且在使用消息類型後不應更改。 請注意,1到15範圍內的字段編號需要一個字節進行編碼,包括字段編號和字段類型16到2047範圍內的字段編號佔用兩個字節。因此,您應該爲非常頻繁出現的消息元素保留數字1到15。請記住爲將來可能添加的常用元素留出一些空間。

最小的標識號可以從1開始,最大到2^29 - 1,或 536,870,911。不可以使用其中的[19000-19999]的標識號, Protobuf協議實現中對這些進行了預留。如果非要在.proto文件中使用這些預留標識號,編譯時就會報警。同樣你也不能使用早期保留的標識號。

  • 指定字段規則

消息字段可以是以下之一:

  • singular:格式良好的消息可以包含該字段中的零個或一個(但不超過一個)。

  • repeated:此字段可以在格式良好的消息中重複任意次數(包括零)。將保留重複值的順序。對應Python的列表。

    message Result {
      string url = 1;
      string title = 2;
      repeated string snippets = 3;
    }
    
  • 添加更多消息類型

可以在單個.proto文件中定義多個消息類型。

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}

message SearchResponse {
 ...
}
  • 安裝protobuf編譯器和grpc庫
pip install grpcio-tools
  • 編譯生成代碼
python -m grpc_tools.protoc -I. --python_out=.. --grpc_python_out=.. itcast.proto
  • -I表示搜索proto文件中被導入文件的目錄
  • --python_out表示保存生成Python文件的目錄,生成的文件中包含接口定義中的數據類型
  • --grpc_python_out表示保存生成Python文件的目錄,生成的文件中包含接口定義中的服務類型

5.2.4 頭條推薦接口protoco協議定義

創建abtest目錄,將相關接口代碼放入user_reco.proto協議文件

  • 用戶刷新feed流接口
    • user_recommend(User) returns (Track)
  • 文章相似(猜你喜歡)接口
    • article_recommend(Article) returns(Similar)
syntax = "proto3";

message User {

    string user_id = 1;
    int32 channel_id = 2;
    int32 article_num = 3;
    int64 time_stamp = 4;
}
// int32 ---> int64 article_id
message Article {

    int64 article_id = 1;
    int32 article_num = 2;

}

message param2 {
    string click = 1;
    string collect = 2;
    string share = 3;
    string read = 4;
}

message param1 {
    int64 article_id = 1;
    param2 params = 2;
}

message Track {
    string exposure = 1;
    repeated param1 recommends = 2;
    int64 time_stamp = 3;
}

message Similar {
    repeated int64 article_id = 1;
}

service UserRecommend {
    // feed recommend
    rpc user_recommend(User) returns (Track) {}
    rpc article_recommend(Article) returns(Similar) {}
}

通過命令生成

python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. user_reco.proto

5.2.4 頭條grpc服務端編寫

創建routing.py文件,填寫服務端代碼:

相關包

import os
import sys

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(BASE_DIR))
from concurrent import futures
from abtest import user_reco_pb2
from abtest import user_reco_pb2_grpc
from setting.default import DefaultConfig
import grpc
import time
import json

完整程序代碼

需要添加grpc服務配置:

# rpc
RPC_SERVER = '192.168.19.137:9999'

完整代碼:

# 基於用戶推薦的rpc服務推薦
# 定義指定的rpc服務輸入輸出參數格式proto
class UserRecommendServicer(user_reco_pb2_grpc.UserRecommendServicer):
    """
    對用戶進行技術文章推薦
    """
    def user_recommend(self, request, context):
        """
        用戶feed流推薦
        :param request:
        :param context:
        :return:
        """
        # 選擇C4組合
        user_id = request.user_id
        channel_id = request.channel_id
        article_num = request.article_num
        time_stamp = request.time_stamp

        # 解析參數,並進行推薦中心推薦(暫時使用假數據替代)
        class Temp(object):
            user_id = -10
            algo = 'test'
            time_stamp = -10

        tp = Temp()
        tp.user_id = user_id
        tp.time_stamp = time_stamp
        _track = add_track([], tp)

        # 解析返回參數到rpc結果參數
        # 參數如下
        # [       {"article_id": 1, "param": {"click": "", "collect": "", "share": "", 'detentionTime':''}},
        #         {"article_id": 2, "param": {"click": "", "collect": "", "share": "", 'detentionTime':''}},
        #         {"article_id": 3, "param": {"click": "", "collect": "", "share": "", 'detentionTime':''}},
        #         {"article_id": 4, "param": {"click": "", "collect": "", "share": "", 'detentionTime':''}}
        #     ]
        # 第二個rpc參數
        _param1 = []
        for _ in _track['recommends']:
            # param的封裝
            _params = user_reco_pb2.param2(click=_['param']['click'],
                                           collect=_['param']['collect'],
                                           share=_['param']['share'],
                                           read=_['param']['read'])
            _p2 = user_reco_pb2.param1(article_id=_['article_id'], params=_params)
            _param1.append(_p2)
        # param
        return user_reco_pb2.Track(exposure=_track['param'], recommends=_param1, time_stamp=_track['timestamp'])

#    def article_recommend(self, request, context):
#        """
#       文章相似推薦
#       :param request:
#       :param context:
#       :return:
#       """
#       # 獲取web參數
#       article_id = request.article_id
#       article_num = request.article_num
#
#        # 進行文章相似推薦,調用推薦中心的文章相似
#       _article_list = article_reco_list(article_id, article_num, 105)
#
#       # rpc參數封裝
#       return user_reco_pb2.Similar(article_id=_article_list)


def serve():

    # 多線程服務器
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    # 註冊本地服務
    user_reco_pb2_grpc.add_UserRecommendServicer_to_server(UserRecommendServicer(), server)
    # 監聽端口
    server.add_insecure_port(DefaultConfig.RPC_SERVER)

    # 開始接收請求進行服務
    server.start()
    # 使用 ctrl+c 可以退出服務
    _ONE_DAY_IN_SECONDS = 60 * 60 * 24
    try:
        while True:
            time.sleep(_ONE_DAY_IN_SECONDS)
    except KeyboardInterrupt:
        server.stop(0)


if __name__ == '__main__':
    # 測試grpc服務
    serve()

埋點參數的接口封裝:

其中:

class Temp(object):
    user_id = '1115629498121846784'
    algo = 'test'
    time_stamp = int(time.time() * 1000)
_track = add_track([], Temp())

web後臺請求傳入的時間戳是time.time(),Out[3]: int(1558128143.8735564) * 1000的大小

def add_track(res, temp):
    """
    封裝埋點參數
    :param res: 推薦文章id列表
    :param cb: 合併參數
    :param rpc_param: rpc參數
    :return: 埋點參數
        文章列表參數
        單文章參數
    """
    # 添加埋點參數
    track = {}

    # 準備曝光參數
    # 全部字符串形式提供,在hive端不會解析問題
    _exposure = {"action": "exposure", "userId": temp.user_id, "articleId": json.dumps(res),
                 "algorithmCombine": temp.algo}

    track['param'] = json.dumps(_exposure)
    track['recommends'] = []

    # 準備其它點擊參數
    for _id in res:
        # 構造字典
        _dic = {}
        _dic['article_id'] = _id
        _dic['param'] = {}

        # 準備click參數
        _p = {"action": "click", "userId": temp.user_id, "articleId": str(_id),
              "algorithmCombine": temp.algo}

        _dic['param']['click'] = json.dumps(_p)
        # 準備collect參數
        _p["action"] = 'collect'
        _dic['param']['collect'] = json.dumps(_p)
        # 準備share參數
        _p["action"] = 'share'
        _dic['param']['share'] = json.dumps(_p)
        # 準備detentionTime參數
        _p["action"] = 'read'
        _dic['param']['read'] = json.dumps(_p)

        track['recommends'].append(_dic)

    track['timestamp'] = temp.time_stamp
    return track

提供客戶端測試代碼:

  • 測試客戶端
import os
import sys

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(BASE_DIR))
from abtest import user_reco_pb2_grpc
from abtest import user_reco_pb2
import grpc
from setting.default import DefaultConfig
import time


def test():
    article_dict = {}
    # 構造傳入數據

    req_article = user_reco_pb2.User()
    req_article.user_id = '1115629498121846784'
    req_article.channel_id = 18
    req_article.article_num = 10
    req_article.time_stamp = int(time.time() * 1000)
    # req_article.time_stamp = 1555573069870

    with grpc.insecure_channel(DefaultConfig.RPC_SERVER) as rpc_cli:
        print('''''')
        try:
            stub = user_reco_pb2_grpc.UserRecommendStub(rpc_cli)
            resp = stub.user_recommend(req_article)
        except Exception as e:
            print(e)
            article_dict['param'] = []
        else:

            # 解析返回結果參數
            article_dict['exposure_param'] = resp.exposure

            reco_arts = resp.recommends

            reco_art_param = []
            reco_list = []
            for art in reco_arts:
                reco_art_param.append({
                    'artcle_id': art.article_id,
                    'params': {
                        'click': art.params.click,
                        'collect': art.params.collect,
                        'share': art.params.share,
                        'read': art.params.read
                    }
                })

                reco_list.append(art.article_id)
            article_dict['param'] = reco_art_param

            # 文章列表以及參數(曝光參數 以及 每篇文章的點擊等參數)
            print(reco_list, article_dict)

if __name__ == '__main__':
    test()

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章