Python GRPC使用

Python GRPC使用说明

参考地址:

(1) [python quickstart](https://grpc.io/docs/quickstart/python.html#run-a-grpc-application)
(2) https://blog.csdn.net/sunt2018/article/details/90176015
(3) https://www.jianshu.com/p/43fdfeb105ff?from=timeline&isappinstalled=0

1. proto文件编写

// 说明使用proto3语法定义协议
syntax = "proto3";

package cmdcall;
// rpc服务的名字 ; 后面服务端会用到 , 客户端会用到 
service CmdCall {
    // Call方法 CallRequest:请求对象 CallResponse:响应对象
    rpc Call (CallRequest) returns (CallResponse) {}
    rpc CallWithResult (CallRequest) returns (CallResponse) {}
    rpc CallAndTransferXmlToJson (CallRequest) returns (CallResponse) {}
    rpc CallAndSplitKVToJson (CallRequest) returns (CallResponse) {}
    rpc CallAndGetOutput (CallRequest) returns (CallResponse) {}
}

// Call方法请求对象参数(只有一个参数):cmd(string)
message CallRequest {
    string cmd = 1;
}

 // Call方法响应内容(只有一个响应内容):json(string)
 message CallResponse {
     string json = 1;
 }

2. 安装依赖

pip install grpcio

3. 编译

使用 protoc 编译 proto 文件, 生成 python 语言的实现

3.1 安装protoc编译器

安装 python 下的 protoc 编译器

pip install grpcio-tools
3.2 编译proto文件
python -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I. cmdcall.proto

备注:
(1)python -m grpc_tools.protoc: python 下的 protoc 编译器通过 python 模块(module) 实现, 所以说这一步非常省心
(2)--python_out=. : 编译生成处理 protobuf 相关的代码的路径, 这里生成到当前目录
(3)--grpc_python_out=. : 编译生成处理 grpc 相关的代码的路径, 这里生成到当前目录
(4)-I : 参数指定协议文件的查找目录,我们都将它们设置为当前目录./cmdcall.proto : proto 文件的路径, 这里的 proto 文件在当前目录
3.3 生成文件简介
(1)compute_pb2.py里面有客户端和服务端消息序列化类
(2)compute_pb2_grpc.py包含了服务器Servicer类(CmdCallServicer)和客户端Stub类(CmdCallStub),以及待实现的服务RPC接口

【注意】
(1)编译生成的文件,不要随意更改

4. GRPC使用

4.1 项目目录结构
RPC:           
+---proto
|       cmdcall.proto
|       cmdcall_pb2.py
|       cmdcall_pb2_grpc.py
|       README.md
|       __init__.py
+---rpcClient
|       client.py
|       __init__.py  
+---rpcService
|       rpc_service.py
|       rpc_util.py
|       __init__.py  
\---utils
        constant.py
        exception.py
        excute.py
        logger.py
        net_util.py
        util.py
        __init__.py
4.2 服务端

rpc_service.py

import grpc
from concurrent import futures
from proto import cmdcall_pb2_grpc
from rpcService.rpc_util import CmdCallServicer


def daemonize():
    # 多线程服务器
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    servicer = CmdCallServicer()
    # 注册本地服务
    cmdcall_pb2_grpc.add_CmdCallServicer_to_server(servicer, server)
    # 监听ip端口
    server.add_insecure_port("127.0.0.1:19990")
    # 开始接收请求进行服务
    server.start()
    server.wait_for_termination()
    
  
if __name__ == '__main__':
    daemonize()

rpc_util.py

import os
import traceback
from json import dumps

from proto import cmdcall_pb2_grpc, cmdcall_pb2
from utils import logger
from utils.constant import KUBESDS_RPC_SERVICE_LOG_FN
from utils.exception import ExecuteException
from utils.excute import Operation

class CmdCallServicer(cmdcall_pb2_grpc.CmdCallServicer):

    @staticmethod
    def getCmdCallServiceOperation(request, context):
        return CmdCallServiceOperation(request, context)

    @staticmethod
    def deleteCmdCallServiceOperation(callServiceOperation):
        del callServiceOperation

    def Call(self, request, context):
        callServiceOperation = self.getCmdCallServiceOperation(request, context)
        result = callServiceOperation.call()
        self.deleteCmdCallServiceOperation(callServiceOperation)
        return result

    def CallWithResult(self, request, context):
        callServiceOperation = self.getCmdCallServiceOperation(request, context)
        result = callServiceOperation.callWithResult()
        self.deleteCmdCallServiceOperation(callServiceOperation)
        return result

    class CmdCallServiceOperation(object):

    def __init__(self, request, context):
        self.request = request
        self.context = context
        self.cmd = str(self.request.cmd)
        self.with_result = 'with_result'
        self.success_message = 'kubesds-rpc call cmd %s successful.'
        self.failure_message = 'kubesds-rpc call cmd %s failure.'

    @staticmethod
    def get_result(cmd, params=None, with_result=False):
        params = {} if params is None else params
        logger.debug('CMD: %s' % cmd)
        if with_result:
            result = Operation(cmd, params, with_result=with_result).execute()
        else:
            result = Operation(cmd, params).execute()
        return result

    def call(self):
        try:
            self.get_result(self.cmd)
            return cmdcall_pb2.CallResponse(
                json=dumps({'result': {'code': 0, 'msg': self.success_message % self.cmd}, 'data': {}}))
        except ExecuteException, e:
            logger.debug(traceback.format_exc())
            return cmdcall_pb2.CallResponse(
                json=dumps({'result': {'code': 1, 'msg': self.failure_message % e.message}, 'data': {}}))
        except Exception:
            logger.debug(traceback.format_exc())
            return cmdcall_pb2.CallResponse(
                json=dumps({'result': {'code': 1, 'msg': self.failure_message % traceback.format_exc()}, 'data': {}}))

    def callWithResult(self):
        try:
            result = self.get_result(self.cmd, with_result=True)
            if result['result']['code'] == 0:
                return cmdcall_pb2.CallResponse(json=dumps(result))
            else:
                result['result']['msg'] = self.failure_message % result['result']['msg']
                return cmdcall_pb2.CallResponse(json=dumps(result))
        except ExecuteException, e:
            logger.debug(traceback.format_exc())
            return cmdcall_pb2.CallResponse(
                json=dumps({'result': {'code': 1, 'msg': self.failure_message % e.message}, 'data': {}}))
        except Exception:
            logger.debug(traceback.format_exc())
            return cmdcall_pb2.CallResponse(
                json=dumps({'result': {'code': 1, 'msg': self.failure_message % traceback.format_exc()}, 'data': {}}))

excute.py

import os
import subprocess
import traceback
from json import loads, dumps

import xmltodict

from utils import logger
from utils.constant import KUBESDS_RPC_SERVICE_LOG_FN, ERR_NAME
from utils.exception import ExecuteException

logger = logger.set_logger(os.path.basename(__file__), KUBESDS_RPC_SERVICE_LOG_FN)


class Operation(object):
    def __init__(self, cmd, params, with_result=False, xml_to_json=False, kv_to_json=False, output=False, rbd=False):
        if cmd is None or cmd == "":
            raise Exception("plz give me right cmd.")
        if not isinstance(params, dict):
            raise Exception("plz give me right parameters.")
        self.cmd = cmd
        self.params = params
        self.with_result = with_result

    def get_cmd(self):
        cmd = self.cmd
        for key in self.params.keys():
            cmd = "%s --%s %s " % (cmd, key, self.params[key])
        return cmd

    def execute(self):
        cmd = self.get_cmd()

        if not cmd:
            logger.debug('No CMD to execute.')
            return
        if self.with_result:
            return runCmdWithResult(cmd)
        else:
            return runCmd(cmd)


def runCmdWithResult(cmd):
    p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    try:
        std_out = p.stdout.readlines()
        std_err = p.stderr.readlines()
        if std_out:
            msg = ''
            for index, line in enumerate(std_out):
                if not str.strip(line):
                    continue
                msg = msg + str.strip(line)
            msg = str.strip(msg)
            logger.debug('MSG: ' + msg)
            try:
                result = loads(msg)
                if isinstance(result, dict) and 'result' in result.keys():
                    if result['result']['code'] != 0:
                        if std_err:
                            error_msg = ''
                            for index, line in enumerate(std_err):
                                if not str.strip(line):
                                    continue
                                error_msg = error_msg + str.strip(line)
                            error_msg = str.strip(error_msg).replace('"', "'")
                            result['result']['msg'] = '%s. cstor error output: %s' % (
                                result['result']['msg'], error_msg)
                return result
            except Exception:
                logger.debug(cmd)
                logger.debug(traceback.format_exc())
                error_msg = ''
                for index, line in enumerate(std_err):
                    if not str.strip(line):
                        continue
                    error_msg = error_msg + str.strip(line)
                error_msg = str.strip(error_msg)
                raise ExecuteException('RunCmdError',
                                       'can not parse cstor-cli output to json----%s. %s' % (msg, error_msg))
        if std_err:
            msg = ''
            for index, line in enumerate(std_err):
                msg = msg + line + ', '
            logger.debug(cmd)
            logger.debug(msg)
            logger.debug(traceback.format_exc())
            if msg.strip() != '':
                raise ExecuteException(ERR_NAME, msg)
    finally:
        p.stdout.close()
        p.stderr.close()


def runCmd(cmd):
    p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    try:
        std_out = p.stdout.readlines()
        std_err = p.stderr.readlines()
        p.wait()
        return_code = p.returncode
        # 状态码异常(状态码不为0)才会打印
        if return_code != 0:
            logger.debug('p.returncode: %d' % p.returncode)

        if std_out:
            logger.debug(std_out)

        if std_err:
            msg = ''
            for index, line in enumerate(std_err):
                msg = msg + line
            logger.debug("msg: %s" % msg)
            if msg.strip() != '' and p.returncode != 0:
                raise ExecuteException(ERR_NAME, msg)
        return
    finally:
        p.stdout.close()
        p.stderr.close()
4.3 客户端

client.py

import os
import traceback
from json import loads

import grpc

from proto import cmdcall_pb2_grpc, cmdcall_pb2
from utils import logger
from utils.exception import ExecuteException

logger = logger.set_logger(os.path.basename(__file__), KUBESDS_RPC_CLIENT_LOG_FN)


class RPCClient(object):
    """RPC服务客户端"""

    def __init__(self, cmd):
        self.cmd = cmd
        self.with_result = 'with_result'

    def rpcCall(self):
        return self._runRpcCall('', self.cmd)

    def rpcCallWithResult(self):
        return self._runRpcCall(self.with_result, self.cmd)

    def _runRpcCall(self, operation, cmd):
        if not cmd:
            raise ExecuteException(ERR_NAME, 'cmd is None.')
        try:
            host = get_docker0_IP()
            channel = grpc.insecure_channel("127.0.0.1:19990")
            client = cmdcall_pb2_grpc.CmdCallStub(channel)
            # ideally, you should have try catch block here too
            if operation == self.with_result:
                response = client.CallWithResult(cmdcall_pb2.CallRequest(cmd=cmd))
            else:
                response = client.Call(cmdcall_pb2.CallRequest(cmd=cmd))
            result = loads(str(response.json))
            if self.with_result:
                return result
            else:
                pass
        except grpc.RpcError, e:
            logger.debug(traceback.format_exc())
            # ouch!
            # lets print the gRPC error message
            # which is "Length of `Name` cannot be more than 10 characters"
            logger.debug(e.details())
            # lets access the error code, which is `INVALID_ARGUMENT`
            # `type` of `status_code` is `grpc.StatusCode`
            status_code = e.code()
            # should print `INVALID_ARGUMENT`
            logger.debug(status_code.name)
            # should print `(3, 'invalid argument')`
            logger.debug(status_code.value)
            # want to do some specific action based on the error?
            if grpc.StatusCode.INVALID_ARGUMENT == status_code:
                # do your stuff here
                pass
            raise ExecuteException(ERR_NAME, "excute cmd: %s failed!" % cmd)
        except Exception:
            logger.debug(traceback.format_exc())
            raise ExecuteException(ERR_NAME, 'can not parse rpc response to json.')

你可能感兴趣的:(python,开发语言,rpc)