proto文件helloword.proto:
// Copyright 2015 gRPC authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. syntax = "proto3"; option java_multiple_files = true; option java_package = "io.grpc.examples.helloworld"; option java_outer_classname = "HelloWorldProto"; option objc_class_prefix = "HLW"; package helloworld; // The greeting service definition. service Greeter { // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply) {} } // The request message containing the user's name. message HelloRequest { string name = 1; } // The response message containing the greetings message HelloReply { bytes message = 1; }
grpc_server.py:
# Copyright 2015 gRPC authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """The Python implementation of the GRPC helloworld.Greeter server.""" from concurrent import futures import logging import grpc import helloworld_pb2 import helloworld_pb2_grpc class Greeter(helloworld_pb2_grpc.GreeterServicer): def SayHello(self, request, context): return helloworld_pb2.HelloReply(message= request.name) def serve(): server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server) server.add_insecure_port('[::]:50051') server.start() server.wait_for_termination() if __name__ == '__main__': logging.basicConfig() serve()
helloword_pb2.py
# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: helloworld.proto from google.protobuf import descriptor as _descriptor from google.protobuf import message as _message from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor.FileDescriptor( name='helloworld.proto', package='helloworld', syntax='proto3', serialized_options=b'\n\033io.grpc.examples.helloworldB\017HelloWorldProtoP\001\242\002\003HLW', serialized_pb=b'\n\x10helloworld.proto\x12\nhelloworld\"\x1c\n\x0cHelloRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\"\x1d\n\nHelloReply\x12\x0f\n\x07message\x18\x01 \x01(\t2I\n\x07Greeter\x12>\n\x08SayHello\x12\x18.helloworld.HelloRequest\x1a\x16.helloworld.HelloReply\"\x00\x42\x36\n\x1bio.grpc.examples.helloworldB\x0fHelloWorldProtoP\x01\xa2\x02\x03HLWb\x06proto3' ) _HELLOREQUEST = _descriptor.Descriptor( name='HelloRequest', full_name='helloworld.HelloRequest', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='name', full_name='helloworld.HelloRequest.name', index=0, number=1, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[], enum_types=[ ], serialized_options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=32, serialized_end=60, ) _HELLOREPLY = _descriptor.Descriptor( name='HelloReply', full_name='helloworld.HelloReply', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='message', full_name='helloworld.HelloReply.message', index=0, number=1, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[], enum_types=[ ], serialized_options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=62, serialized_end=91, ) DESCRIPTOR.message_types_by_name['HelloRequest'] = _HELLOREQUEST DESCRIPTOR.message_types_by_name['HelloReply'] = _HELLOREPLY _sym_db.RegisterFileDescriptor(DESCRIPTOR) HelloRequest = _reflection.GeneratedProtocolMessageType('HelloRequest', (_message.Message,), { 'DESCRIPTOR' : _HELLOREQUEST, '__module__' : 'helloworld_pb2' # @@protoc_insertion_point(class_scope:helloworld.HelloRequest) }) _sym_db.RegisterMessage(HelloRequest) HelloReply = _reflection.GeneratedProtocolMessageType('HelloReply', (_message.Message,), { 'DESCRIPTOR' : _HELLOREPLY, '__module__' : 'helloworld_pb2' # @@protoc_insertion_point(class_scope:helloworld.HelloReply) }) _sym_db.RegisterMessage(HelloReply) DESCRIPTOR._options = None _GREETER = _descriptor.ServiceDescriptor( name='Greeter', full_name='helloworld.Greeter', file=DESCRIPTOR, index=0, serialized_options=None, serialized_start=93, serialized_end=166, methods=[ _descriptor.MethodDescriptor( name='SayHello', full_name='helloworld.Greeter.SayHello', index=0, containing_service=None, input_type=_HELLOREQUEST, output_type=_HELLOREPLY, serialized_options=None, ), ]) _sym_db.RegisterServiceDescriptor(_GREETER) DESCRIPTOR.services_by_name['Greeter'] = _GREETER # @@protoc_insertion_point(module_scope)
helloword_pb2_grpc.py:
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! import grpc import helloworld_pb2 as helloworld__pb2 class GreeterStub(object): """The greeting service definition. """ def __init__(self, channel): """Constructor. Args: channel: A grpc.Channel. """ self.SayHello = channel.unary_unary( '/helloworld.Greeter/SayHello', request_serializer=helloworld__pb2.HelloRequest.SerializeToString, response_deserializer=helloworld__pb2.HelloReply.FromString, ) class GreeterServicer(object): """The greeting service definition. """ def SayHello(self, request, context): """Sends a greeting """ context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') def add_GreeterServicer_to_server(servicer, server): rpc_method_handlers = { 'SayHello': grpc.unary_unary_rpc_method_handler( servicer.SayHello, request_deserializer=helloworld__pb2.HelloRequest.FromString, response_serializer=helloworld__pb2.HelloReply.SerializeToString, ), } generic_handler = grpc.method_handlers_generic_handler( 'helloworld.Greeter', rpc_method_handlers) server.add_generic_rpc_handlers((generic_handler,)) # This class is part of an EXPERIMENTAL API. class Greeter(object): """The greeting service definition. """ @staticmethod def SayHello(request, target, options=(), channel_credentials=None, call_credentials=None, compression=None, wait_for_ready=None, timeout=None, metadata=None): return grpc.experimental.unary_unary(request, target, '/helloworld.Greeter/SayHello', helloworld__pb2.HelloRequest.SerializeToString, helloworld__pb2.HelloReply.FromString, options, channel_credentials, call_credentials, compression, wait_for_ready, timeout, metadata)
项目结构:
demos
├─.idea
├─dependency
├─protos
└─python
└─helloword
---greeter_server.py
---helloworld_pb2.py
---helloworld_pb2_grpc.py
└─__pycache__
压力测试类:
import logging import time import grpc import helloworld_pb2 import helloworld_pb2_grpc import json from locust import (TaskSet, task, events, Locust) from gevent._semaphore import Semaphore import random log_fmt = "[%(levelname)s]%(asctime)s line %(lineno)d :\n%(message)s" c_fmt = "[%(levelname)s]%(asctime)s %(filename)s.%(funcName)s():line %(lineno)d :\n%(message)s" date_format = "%Y-%m-%d %H:%M:%S %a" # 设置控制台输出level logging.basicConfig(level=logging.INFO, format=c_fmt, datefmt=date_format ) all_locusts_spawned = Semaphore() all_locusts_spawned.acquire() host = "192.168.110.135" port = "50051" def on_hatch_complete(**kwargs): all_locusts_spawned.release() events.hatch_complete += on_hatch_complete def run(): """test grpc server demo """ with grpc.insecure_channel('192.168.110.135:50051') as channel: stub = helloworld_pb2_grpc.GreeterStub(channel) req = {"name": "jack", "id": 1001} body = json.dumps(req).encode("utf-8") response = stub.SayHello(helloworld_pb2.HelloRequest(name=body)) print(response.message) # str class GrpcClient(object): """overide client""" def __init__(self): self.host = host self.port = port def grpc_request(self, body): start_time = int(time.time()) response = None try: address = "{}:{}".format(host, port) channel = grpc.insecure_channel(address) # p2_grpc.Stub class implement new locust client client_stub = helloworld_pb2_grpc.GreeterStub(channel=channel) request_object = helloworld_pb2.HelloRequest(name=body) back_req = client_stub.SayHello(request_object) response = back_req.message elapsed = int((time.time() - start_time) * 1000) text = json.loads(response) logging.info("get response is {}".format(response)) if text["retcode"] != 0: raise Exception("response get not expect,actual is {}".format(text)) events.request_success.fire( request_type='grpc', name=r'/SayHello', response_time=elapsed, response_length=0 ) except Exception as e: total_time = int((time.time() - start_time) * 1000) events.request_failure.fire( request_type='grpc', name='/SayHello', response_time=total_time, exception=e ) return response class GrpcLocust(Locust): """ overide Locust to implement GrpcLocust""" def __init__(self, *args, **kwargs): super(GrpcLocust, self).__init__() self.client = GrpcClient() class GrpcUserBehavior(TaskSet): """ super TaskSet class implement new GrpcTaskSet""" def on_task(self): """wait task event spawn""" all_locusts_spawned.wait() def on_stop(self): pass @task def inference_task(self): code = random.choice([101, 102, 0, 0]) data = {"name": "{}".format("ZhangSan"), "retcode": code} body = json.dumps(data).encode("utf-8") response_msg = self.client.grpc_request(body) # print(response_msg) class WebsiteUser(GrpcLocust): task_set = GrpcUserBehavior min_wait = 200 # think time ms max_wait = 500
压力测试命令:locust -f grpc_client.py -c 4 -r 2 --run-time 15s --no-web
[2020-05-01 18:45:25,820] DESKTOP-PBNSFDJ/INFO/root: get response is {"name": "ZhangSan", "retcode": 101}
[2020-05-01 18:45:25,870] DESKTOP-PBNSFDJ/INFO/root: get response is {"name": "ZhangSan", "retcode": 101}
[2020-05-01 18:45:26,012] DESKTOP-PBNSFDJ/INFO/root: get response is {"name": "ZhangSan", "retcode": 102}
[2020-05-01 18:45:26,083] DESKTOP-PBNSFDJ/INFO/root: get response is {"name": "ZhangSan", "retcode": 101}
[2020-05-01 18:45:26,088] DESKTOP-PBNSFDJ/INFO/root: get response is {"name": "ZhangSan", "retcode": 0}
[2020-05-01 18:45:26,129] DESKTOP-PBNSFDJ/INFO/locust.main: Time limit reached. Stopping Locust.
[2020-05-01 18:45:26,130] DESKTOP-PBNSFDJ/INFO/locust.main: Shutting down (exit code 1), bye.
[2020-05-01 18:45:26,130] DESKTOP-PBNSFDJ/INFO/locust.main: Cleaning up runner...
[2020-05-01 18:45:26,131] DESKTOP-PBNSFDJ/INFO/locust.main: Running teardowns...
Name # reqs # fails Avg Min Max | Median req/s
--------------------------------------------------------------------------------------------------------------------------------------------
grpc /SayHello 158 87(35.51%) 532 10 1052 | 540 11.40
--------------------------------------------------------------------------------------------------------------------------------------------
Total 158 87(55.06%) 11.40
Percentage of the requests completed within given times
Name # reqs 50% 66% 75% 80% 90% 95% 98% 99% 100%
--------------------------------------------------------------------------------------------------------------------------------------------
grpc /SayHello 158 540 690 760 840 960 980 990 1000 1100
--------------------------------------------------------------------------------------------------------------------------------------------
Total 158 540 690 760 840 960 980 990 1000 1100
Error report
# occurrences Error
--------------------------------------------------------------------------------------------------------------------------------------------
46 grpc /SayHello: 'Exception("response get not expect,actual is {\'name\': \'ZhangSan\', \'retcode\': 101}")'
41 grpc /SayHello: 'Exception("response get not expect,actual is {\'name\': \'ZhangSan\', \'retcode\': 102}")'
--------------------------------------------------------------------------------------------------------------------------------------------
web模式: