【Grpc】grpcを使用してポートを介してアクセスするpythonサービスを構成する

19640 ワード

0 x 00はじめに
最近興味をそそられた技術調査はますます少なくなった.(TTSは1つですが)主にプロジェクトやタスクのために検討しているものです.現在の状況では、ディスプレイメモリを節約するために、大きなモデルでは、4つのworkerを用いて繰り返しディスプレイメモリを占有するよりも、1つのディスプレイメモリのみを占有するようにしていますが、サービスフローを開いたり、異なるプロジェクトのニーズをトリガ的に処理したりすることができます.そこで@caoyixuan 93先輩がGRPCを推薦してくれました.@hongfengと@phchangの助けを経て、ついに小さなカスタムdemoを実現することに成功し、その後、モデルをマウントするのに時間がかかりました.
0 x 01 GRPC紹介
GRPC: A high performance, open source, general-purpose RPC framework
簡単に言えば、オープンソースの「サービス・エンド・クライアント」フレームワークです.モデルの予測関数などのサービスをマウントし、ポートを介して送信された入力データをいつでも受信し、計算後に出力を返します.複数のアクセスがある場合は、キューまたはストリームの形式で1つずつ処理します.
0 x 02環境構成
Python Quick Startを参照して、簡単なgrpcサービスについて、どのようなものが順番に必要なのかを構築し、構成が必要な環境に対応してみましょう.
  • Python >= 3.4 grpcには多くのバージョンがありますが、ここでは便利なpython
  • を選択しました.
  • grpcio>=1.28.1 grpcを使う以上自然にgrpcを入れなければならない.ここのgrpcioはgrpcのpythonバージョンを指す.また、他の言語のバージョンはあまり研究されていない.直接pip install grpcio
  • でよい.
  • grpcio-toolsこのツールの役割は、.proto形式のプロファイルを読み取ることによって、直接呼び出すために2つのpythonコードを生成し、直接pip install grpcio-tools
  • を生成することである.
  • protobuf==3.6.0前のステップのpip installには3.11.xが付属してインストールされています.その結果、バージョンが高すぎてエラーが発生しやすくなります.ここでは、上記の3.6.0形式のファイルを読み取り、サービスを行う変数名構成
  • を選択します.
    0 x 03ソースコードおよび分析
    個人的には頭が悪いかもしれませんが、Python Quick Startの例を見ると硬くて分からないので、彼のhelloworldよりも自分でdemoをカスタマイズして走ってみたほうがいいです.これは新しいフレームワークを学ぶのにいい方法です.
    プロファイル
    https://github.com/okcd00/CDAlter/tree/master/CDMemory/protos
    まず勧誘されるprotobuf言語ですが、ああ、私はどうすればいいか習ったことがありません.ああ、頭が痛いようです.そして見ていて、ああ、これですか?まあ、論理を書く必要はありません.配置ですか.じゃ、私はできます.ymlもできません.でも、ひょうたんのように配置を書くことはできますか.
    //        proto3   ,     ,  python2   proto2 ,    
    syntax = "proto3";  
    
    //               ,              
    package keyvaluestore;  
    
    // service                       
    //                 ,     python    
    service KeyValueStore {
      //     : rpc     (    ) returns (    ) {}
      //          message         
      rpc ask (Key) returns (Response) {}
      rpc remember (Item) returns (Response) {}
    }
    
    //    :Key
    //        ,      
    message Key {
      string key = 1;
    }
    
    //    :Item
    //        ,        
    message Item {
      string key = 1;
      string value = 2;  
      //   1 2         ,          ,               
    }
    
    //    :Response 
    //        ,      
    message Response {
      string value = 1;
    }
    

    この.protoファイルが設定されたら、コマンドライン(cmd、コマンドプロンプト、git bashでもよい)を介してtoolを呼び出して2つのpythonファイルを自動的に生成します.呼び出し方法は、まずprotoが存在するフォルダ(例えばproto)に入り、次のコマンド(ここで私のprotoファイルはcd /i/Github/CDAlter/CDMemory/protosと呼ばれています):
    python -m grpc_tools.protoc \
        -I ./ \ 
        --python_out=. \
        --grpc_python_out=. \
        keyvaluestore.proto
    

    中のkeyvaluestore.protoはフォーマットをきれいにするためで、サボったら直接1行入力できるものを提供しています.
    python -m grpc_tools.protoc -I ./ --python_out=. --grpc_python_out=. keyvaluestore.proto
    

    私のプロファイルは\というので、現在のディレクトリの下でkeyvaluestore.protokeyvaluestore_pb2.pyの2つのファイルが生成されました.
    サービス側とクライアント
    https://github.com/okcd00/CDAlter/tree/master/CDNerve
    サービス側
    # -*- coding: gbk -*-
    # ==========================================================================
    #   Copyright (C) since 2020 All rights reserved.
    #
    #   filename : grpc_server.py
    #   author   : chendian / [email protected]
    #   date     : 2020-04-16
    #   desc     : server in grpc service
    # ==========================================================================
    import sys
    import time
    import grpc
    from concurrent import futures
    from multiprocessing import Pool
    from collections import OrderedDict
    from grpc._cython.cygrpc import CompressionAlgorithm, CompressionLevel
    
    """
    # generate it at first
    python -m grpc_tools.protoc \
        -I ./ \ 
        --python_out=. \
        --grpc_python_out=. \
        keyvaluestore.proto
    # then you can get keyvaluestore_pb2_grpc and keyvaluestore_pb2
    """
    #     protos       ,     pythonpath
    sys.path.append('../CDMemory/protos/')
    from CDMemory.protos import keyvaluestore_pb2_grpc, keyvaluestore_pb2
    
    
    class KVServicer(keyvaluestore_pb2_grpc.KeyValueStoreServicer):
        def __init__(self):
        	#               ,   self.model = AlbertModel()
            self.records = OrderedDict()  #           Demo     
    
        #     service                  ,           ,
        #      :"Exception iterating requests!"
        #              ,      `def ask(self, Key, context):` 
        def ask(self, request, context):  
        	#    rpc ask (Key) returns (Response) {}
            #    request    `ask`      `Key`
            _value = self.records.get(request.key, "Empty")  
            #   `keyvaluestore_pb2.Response`   `returns`       `Response`
            return keyvaluestore_pb2.Response(value=_value)
    	
    	#              ,      `def remember(self, Item, context):` 
        def remember(self, request, context):
    	    #    rpc remember (Item) returns (Response) {}
            #    request    `remember `      `Item`
            key, value = request.key, request.value  #      Item    key/value
            self.records.update({key: value})  #   grpc demo          
            return keyvaluestore_pb2.Response(
                value="Remembered: the value for {} is {}".format(key, value))
    
    
    def serve(n_worker=4, port=20416):
        max_receive_message_length = 512
        #     Servicer   
        service = KVServicer()
        #     ,      
        service.process_pool = Pool(processes=n_worker)
        #    server   
        server = grpc.server(futures.ThreadPoolExecutor(max_workers=n_worker), 
        	options=[  #    options      ,                 
    	        ('grpc.max_receive_message_length', max_receive_message_length),
    	        ('grpc.default_compression_algorithm', CompressionAlgorithm.gzip),
    	        ('grpc.grpc.default_compression_level', CompressionLevel.high)
        ])
        #        keyvaluestore_pb2_grpc     Servicer     
        keyvaluestore_pb2_grpc.add_KeyValueStoreServicer_to_server(service, server)
    	#      (        )
        server.add_insecure_port('[::]:{}'.format(port))
    	#     
        server.start()
        # server.wait_for_termination()
        return server
    
    
    if __name__ == "__main__":
        print("starting server...")
        server_agent = serve()
        print("server started.")
        while True:
            time.sleep(10000)
            print("[ALIVE] {}".format(time.ctime()))
    
    

    クライアント
    # -*- coding: gbk -*-
    # ==========================================================================
    #   Copyright (C) since 2020 All rights reserved.
    #
    #   filename : grpc_client.py
    #   author   : chendian / [email protected]
    #   date     : 2020-04-16
    #   desc     : client in grpc service
    # ==========================================================================
    import os
    import sys
    if os.environ.get('https_proxy'):
        del os.environ['https_proxy']
    if os.environ.get('http_proxy'):
        del os.environ['http_proxy']
    import grpc
    
    """
    # generate it at first
    python -m grpc_tools.protoc \
        -I ./ \ 
        --python_out=. \
        --grpc_python_out=. \
        keyvaluestore.proto
    # then you can get keyvaluestore_pb2_grpc and keyvaluestore_pb2
    """
    #     protos       ,     pythonpath
    sys.path.append('../CDMemory/protos/')  
    from CDMemory.protos import keyvaluestore_pb2_grpc, keyvaluestore_pb2
    
    
    if __name__ == '__main__':
    	#         grpc  
        with grpc.insecure_channel('localhost:20416') as channel:
            #             
            stub = keyvaluestore_pb2_grpc.KeyValueStoreStub(channel)
    		#        remember  ,      key=name, value=cd
            response = stub.remember(keyvaluestore_pb2.Item(key='name', value='cd'))
            print(response)  # value: "Remembered: the value for name is cd"
            #        ask  ,      key=name     value
            response = stub.ask(keyvaluestore_pb2.Key(key='name'))
            print(response)  # value: "cd"