意见箱
恒创运营部门将仔细参阅您的意见和建议,必要时将通过预留邮箱与您保持联络。感谢您的支持!
意见/建议
提交建议

grpc压力测试 基于Locust 父类和client重写

来源:恒创科技 编辑:恒创科技编辑部
2022-09-22 00:00:00

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


grpc压力测试 基于Locust 父类和client重写

# -*- 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模式:

运行locust -f grpc_client.py 填写user 10 hatch 2也就是每秒发多少请求

​​http://localhost:8089/​​

grpc压力测试 基于Locust 父类和client重写_java

charts :

grpc压力测试 基于Locust 父类和client重写_python_02

项目依赖centos7 :

certifi==2020.4.5.1
chardet==3.0.4
click==7.1.2
ConfigArgParse==1.2.3
coverage==5.1
Cython==0.29.17
enum34==1.1.10
Flask==1.1.2
gevent==20.4.0
geventhttpclient-wheels==1.3.1.dev2
greenlet==0.4.15
grpcio==1.28.1
grpcio-tools==1.28.1
idna==2.9
itsdangerous==1.1.0
Jinja2==2.11.2
locust==0.0
locustio==0.14.6
MarkupSafe==1.1.1
msgpack==1.0.0
protobuf==3.11.3
psutil==5.7.0
pyzmq==19.0.0
requests==2.23.0
six==1.14.0
urllib3==1.25.9
Werkzeug==1.0.1



上一篇: 租用美国服务器:潜在的风险与应对策略。 下一篇: MongoDB 5.0 扩展开源文档数据库操作