未验证 提交 c1bc7de2 编写于 作者: J Jacob Schmitt 提交者: GitHub

Merge pull request #149 from d5h/microservices-with-grpc

Add materials for https://github.com/realpython/tutorial-drafts/pull/602
# Running the Example
1. Install Docker if you haven't already.
2. Run `./build_and_run.sh`.
#!/bin/bash
./gen_certs.sh
DOCKER_BUILDKIT=1 docker build . -f marketplace/Dockerfile -t marketplace --secret id=ca.key,src=ca.key
DOCKER_BUILDKIT=1 docker build . -f recommendations/Dockerfile -t recommendations --secret id=ca.key,src=ca.key
docker-compose up
version: "3.8"
services:
marketplace:
environment:
RECOMMENDATIONS_HOST: recommendations
# DOCKER_BUILDKIT=1 docker build . -f marketplace/Dockerfile \
# -t marketplace --secret id=ca.key,src=ca.key
image: marketplace
networks:
- microservices
ports:
- 5000:5000
recommendations:
# DOCKER_BUILDKIT=1 docker build . -f recommendations/Dockerfile \
# -t recommendations --secret id=ca.key,src=ca.key
image: recommendations
networks:
- microservices
networks:
microservices:
#!/bin/bash
# Generate CA key and self-signed cert
openssl req -x509 -nodes -newkey rsa:4096 -keyout ca.key -out ca.pem -subj /O=me
# Generate a private key and certificate signing request for the client and server
openssl req -nodes -newkey rsa:4096 -keyout client.key -out client.csr -subj /CN=marketplace
openssl req -nodes -newkey rsa:4096 -keyout server.key -out server.csr -subj /CN=recommendations
# Sign the client and server certs with the CA cert
openssl x509 -req -in client.csr -CA ca.pem -CAkey ca.key -set_serial 1 -out client.pem
openssl x509 -req -in server.csr -CA ca.pem -CAkey ca.key -set_serial 1 -out server.pem
#!/bin/bash
set -euo pipefail
docker-compose up -d
trap "docker-compose down" EXIT
sleep 5 # Give the services time to warm up
docker-compose exec marketplace pytest
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: marketplace
labels:
app: marketplace
spec:
replicas: 1
selector:
matchLabels:
app: marketplace
template:
metadata:
labels:
app: marketplace
spec:
containers:
- name: marketplace
image: hidan/python-microservices-article-marketplace:0.1
env:
- name: RECOMMENDATIONS_HOST
value: recommendations
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: recommendations
labels:
app: recommendations
spec:
replicas: 1
selector:
matchLabels:
app: recommendations
template:
metadata:
labels:
app: recommendations
spec:
containers:
- name: recommendations
image: hidan/python-microservices-article-recommendations:0.1
---
apiVersion: v1
kind: Service
metadata:
name: recommendations
spec:
selector:
app: recommendations
ports:
- protocol: TCP
port: 50051
targetPort: 50051
---
apiVersion: v1
kind: Service
metadata:
name: marketplace
spec:
type: LoadBalancer
selector:
app: marketplace
ports:
- protocol: TCP
port: 5000
targetPort: 5000
# syntax = docker/dockerfile:1.0-experimental
# DOCKER_BUILDKIT=1 docker build . -f marketplace/Dockerfile -t marketplace --secret id=ca.key,src=ca.key
FROM python
RUN mkdir /service
COPY protobufs/ /service/protobufs/
COPY marketplace/ /service/marketplace/
COPY ca.pem /service/marketplace/
WORKDIR /service/marketplace
RUN pip install -r requirements.txt
RUN python -m grpc_tools.protoc -I ../protobufs --python_out=. \
--grpc_python_out=. ../protobufs/recommendations.proto
RUN openssl req -nodes -newkey rsa:4096 -subj /CN=marketplace \
-keyout client.key -out client.csr
RUN --mount=type=secret,id=ca.key \
openssl x509 -req -in client.csr -CA ca.pem -CAkey /run/secrets/ca.key \
-set_serial 1 -out client.pem
EXPOSE 5000
ENV FLASK_APP=marketplace.py
ENTRYPOINT [ "flask", "run", "--host=0.0.0.0"]
import os
from flask import Flask, render_template
import grpc
from recommendations_pb2 import BookCategory, RecommendationRequest
from recommendations_pb2_grpc import RecommendationsStub
app = Flask(__name__)
recommendations_host = os.getenv("RECOMMENDATIONS_HOST", "localhost")
with open("client.key", "rb") as fp:
client_key = fp.read()
with open("client.pem", "rb") as fp:
client_cert = fp.read()
with open("ca.pem", "rb") as fp:
ca_cert = fp.read()
creds = grpc.ssl_channel_credentials(ca_cert, client_key, client_cert)
recommendations_channel = grpc.secure_channel(
f"{recommendations_host}:443", creds
)
recommendations_client = RecommendationsStub(recommendations_channel)
@app.route("/")
def render_homepage():
recommendations_request = RecommendationRequest(
user_id=1, category=BookCategory.MYSTERY, max_results=3
)
recommendations_response = recommendations_client.Recommend(
recommendations_request
)
return render_template(
"homepage.html",
recommendations=recommendations_response.recommendations,
)
from urllib.request import urlopen
def test_render_homepage():
homepage_html = urlopen("http://localhost:5000").read().decode("utf-8")
assert "<title>Online Books For You</title>" in homepage_html
assert homepage_html.count("<li>") == 3
# -*- coding: utf-8 -*-
# flake8: noqa
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: recommendations.proto
from google.protobuf.internal import enum_type_wrapper
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="recommendations.proto",
package="",
syntax="proto3",
serialized_options=None,
serialized_pb=b'\n\x15recommendations.proto"^\n\x15RecommendationRequest\x12\x0f\n\x07user_id\x18\x01 \x01(\x05\x12\x1f\n\x08\x63\x61tegory\x18\x02 \x01(\x0e\x32\r.BookCategory\x12\x13\n\x0bmax_results\x18\x03 \x01(\x05"/\n\x12\x42ookRecommendation\x12\n\n\x02id\x18\x01 \x01(\x05\x12\r\n\x05title\x18\x02 \x01(\t"F\n\x16RecommendationResponse\x12,\n\x0frecommendations\x18\x01 \x03(\x0b\x32\x13.BookRecommendation*?\n\x0c\x42ookCategory\x12\x0b\n\x07MYSTERY\x10\x00\x12\x13\n\x0fSCIENCE_FICTION\x10\x01\x12\r\n\tSELF_HELP\x10\x02\x32O\n\x0fRecommendations\x12<\n\tRecommend\x12\x16.RecommendationRequest\x1a\x17.RecommendationResponseb\x06proto3',
)
_BOOKCATEGORY = _descriptor.EnumDescriptor(
name="BookCategory",
full_name="BookCategory",
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name="MYSTERY",
index=0,
number=0,
serialized_options=None,
type=None,
),
_descriptor.EnumValueDescriptor(
name="SCIENCE_FICTION",
index=1,
number=1,
serialized_options=None,
type=None,
),
_descriptor.EnumValueDescriptor(
name="SELF_HELP",
index=2,
number=2,
serialized_options=None,
type=None,
),
],
containing_type=None,
serialized_options=None,
serialized_start=242,
serialized_end=305,
)
_sym_db.RegisterEnumDescriptor(_BOOKCATEGORY)
BookCategory = enum_type_wrapper.EnumTypeWrapper(_BOOKCATEGORY)
MYSTERY = 0
SCIENCE_FICTION = 1
SELF_HELP = 2
_RECOMMENDATIONREQUEST = _descriptor.Descriptor(
name="RecommendationRequest",
full_name="RecommendationRequest",
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name="user_id",
full_name="RecommendationRequest.user_id",
index=0,
number=1,
type=5,
cpp_type=1,
label=1,
has_default_value=False,
default_value=0,
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
_descriptor.FieldDescriptor(
name="category",
full_name="RecommendationRequest.category",
index=1,
number=2,
type=14,
cpp_type=8,
label=1,
has_default_value=False,
default_value=0,
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
_descriptor.FieldDescriptor(
name="max_results",
full_name="RecommendationRequest.max_results",
index=2,
number=3,
type=5,
cpp_type=1,
label=1,
has_default_value=False,
default_value=0,
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=25,
serialized_end=119,
)
_BOOKRECOMMENDATION = _descriptor.Descriptor(
name="BookRecommendation",
full_name="BookRecommendation",
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name="id",
full_name="BookRecommendation.id",
index=0,
number=1,
type=5,
cpp_type=1,
label=1,
has_default_value=False,
default_value=0,
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
_descriptor.FieldDescriptor(
name="title",
full_name="BookRecommendation.title",
index=1,
number=2,
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=121,
serialized_end=168,
)
_RECOMMENDATIONRESPONSE = _descriptor.Descriptor(
name="RecommendationResponse",
full_name="RecommendationResponse",
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name="recommendations",
full_name="RecommendationResponse.recommendations",
index=0,
number=1,
type=11,
cpp_type=10,
label=3,
has_default_value=False,
default_value=[],
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=170,
serialized_end=240,
)
_RECOMMENDATIONREQUEST.fields_by_name["category"].enum_type = _BOOKCATEGORY
_RECOMMENDATIONRESPONSE.fields_by_name[
"recommendations"
].message_type = _BOOKRECOMMENDATION
DESCRIPTOR.message_types_by_name[
"RecommendationRequest"
] = _RECOMMENDATIONREQUEST
DESCRIPTOR.message_types_by_name["BookRecommendation"] = _BOOKRECOMMENDATION
DESCRIPTOR.message_types_by_name[
"RecommendationResponse"
] = _RECOMMENDATIONRESPONSE
DESCRIPTOR.enum_types_by_name["BookCategory"] = _BOOKCATEGORY
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
RecommendationRequest = _reflection.GeneratedProtocolMessageType(
"RecommendationRequest",
(_message.Message,),
{
"DESCRIPTOR": _RECOMMENDATIONREQUEST,
"__module__": "recommendations_pb2"
# @@protoc_insertion_point(class_scope:RecommendationRequest)
},
)
_sym_db.RegisterMessage(RecommendationRequest)
BookRecommendation = _reflection.GeneratedProtocolMessageType(
"BookRecommendation",
(_message.Message,),
{
"DESCRIPTOR": _BOOKRECOMMENDATION,
"__module__": "recommendations_pb2"
# @@protoc_insertion_point(class_scope:BookRecommendation)
},
)
_sym_db.RegisterMessage(BookRecommendation)
RecommendationResponse = _reflection.GeneratedProtocolMessageType(
"RecommendationResponse",
(_message.Message,),
{
"DESCRIPTOR": _RECOMMENDATIONRESPONSE,
"__module__": "recommendations_pb2"
# @@protoc_insertion_point(class_scope:RecommendationResponse)
},
)
_sym_db.RegisterMessage(RecommendationResponse)
_RECOMMENDATIONS = _descriptor.ServiceDescriptor(
name="Recommendations",
full_name="Recommendations",
file=DESCRIPTOR,
index=0,
serialized_options=None,
serialized_start=307,
serialized_end=386,
methods=[
_descriptor.MethodDescriptor(
name="Recommend",
full_name="Recommendations.Recommend",
index=0,
containing_service=None,
input_type=_RECOMMENDATIONREQUEST,
output_type=_RECOMMENDATIONRESPONSE,
serialized_options=None,
),
],
)
_sym_db.RegisterServiceDescriptor(_RECOMMENDATIONS)
DESCRIPTOR.services_by_name["Recommendations"] = _RECOMMENDATIONS
# @@protoc_insertion_point(module_scope)
# flake8: noqa
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
import grpc
import recommendations_pb2 as recommendations__pb2
class RecommendationsStub(object):
"""Missing associated documentation comment in .proto file"""
def __init__(self, channel):
"""Constructor.
Args:
channel: A grpc.Channel.
"""
self.Recommend = channel.unary_unary(
"/Recommendations/Recommend",
request_serializer=recommendations__pb2.RecommendationRequest.SerializeToString,
response_deserializer=recommendations__pb2.RecommendationResponse.FromString,
)
class RecommendationsServicer(object):
"""Missing associated documentation comment in .proto file"""
def Recommend(self, request, context):
"""Missing associated documentation comment in .proto file"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details("Method not implemented!")
raise NotImplementedError("Method not implemented!")
def add_RecommendationsServicer_to_server(servicer, server):
rpc_method_handlers = {
"Recommend": grpc.unary_unary_rpc_method_handler(
servicer.Recommend,
request_deserializer=recommendations__pb2.RecommendationRequest.FromString,
response_serializer=recommendations__pb2.RecommendationResponse.SerializeToString,
),
}
generic_handler = grpc.method_handlers_generic_handler(
"Recommendations", rpc_method_handlers
)
server.add_generic_rpc_handlers((generic_handler,))
# This class is part of an EXPERIMENTAL API.
class Recommendations(object):
"""Missing associated documentation comment in .proto file"""
@staticmethod
def Recommend(
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,
"/Recommendations/Recommend",
recommendations__pb2.RecommendationRequest.SerializeToString,
recommendations__pb2.RecommendationResponse.FromString,
options,
channel_credentials,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
)
flask ~= 1.1
grpcio-tools ~= 1.30
Jinja2 ~= 2.11
pytest ~= 5.4
<!doctype html>
<html lang="en">
<head>
<title>Online Books For You</title>
</head>
<body>
<h1>Mystery books you may like</h1>
<ul>
{% for book in recommendations %}
<li>{{ book.title }}</li>
{% endfor %}
</ul>
</body>
syntax = "proto3";
enum BookCategory {
MYSTERY = 0;
SCIENCE_FICTION = 1;
SELF_HELP = 2;
}
message RecommendationRequest {
int32 user_id = 1;
BookCategory category = 2;
int32 max_results = 3;
}
message BookRecommendation {
int32 id = 1;
string title = 2;
}
message RecommendationResponse {
repeated BookRecommendation recommendations = 1;
}
service Recommendations {
rpc Recommend (RecommendationRequest) returns (RecommendationResponse);
}
# syntax = docker/dockerfile:1.0-experimental
# DOCKER_BUILDKIT=1 docker build . -f recommendations/Dockerfile -t recommendations --secret id=ca.key,src=ca.key
FROM python
RUN mkdir /service
COPY protobufs/ /service/protobufs/
COPY recommendations/ /service/recommendations/
COPY ca.pem /service/recommendations/
WORKDIR /service/recommendations
RUN pip install -r requirements.txt
RUN python -m grpc_tools.protoc -I ../protobufs --python_out=. \
--grpc_python_out=. ../protobufs/recommendations.proto
RUN openssl req -nodes -newkey rsa:4096 -subj /CN=recommendations \
-keyout server.key -out server.csr
RUN --mount=type=secret,id=ca.key \
openssl x509 -req -in server.csr -CA ca.pem -CAkey /run/secrets/ca.key \
-set_serial 1 -out server.pem
EXPOSE 50051
ENTRYPOINT [ "python", "recommendations.py" ]
from concurrent import futures
import random
import grpc
from grpc_interceptor import ExceptionToStatusInterceptor
from grpc_interceptor.exceptions import NotFound
from recommendations_pb2 import (
BookCategory,
BookRecommendation,
RecommendationResponse,
)
import recommendations_pb2_grpc
books_by_category = {
BookCategory.MYSTERY: [
BookRecommendation(id=1, title="The Maltese Falcon"),
BookRecommendation(id=2, title="Murder on the Orient Express"),
BookRecommendation(id=3, title="The Hound of the Baskervilles"),
],
BookCategory.SCIENCE_FICTION: [
BookRecommendation(id=4, title="The Hitchhiker's Guide To The Galaxy"),
BookRecommendation(id=5, title="Ender's Game"),
BookRecommendation(id=6, title="The Dune Chronicles"),
],
BookCategory.SELF_HELP: [
BookRecommendation(
id=7, title="The 7 Habits of Highly Effective People"
),
BookRecommendation(
id=8, title="How to Win Friends and Influence People"
),
BookRecommendation(id=9, title="Man’s Search for Meaning"),
],
}
class RecommendationService(recommendations_pb2_grpc.RecommendationsServicer):
def Recommend(self, request, context):
if request.category not in books_by_category:
raise NotFound("Category not found")
books_for_category = books_by_category[request.category]
num_results = min(request.max_results, len(books_for_category))
books_to_recommend = random.sample(books_for_category, num_results)
return RecommendationResponse(recommendations=books_to_recommend)
def serve():
interceptors = [ExceptionToStatusInterceptor()]
server = grpc.server(
futures.ThreadPoolExecutor(max_workers=10), interceptors=interceptors
)
recommendations_pb2_grpc.add_RecommendationsServicer_to_server(
RecommendationService(), server
)
with open("server.key", "rb") as fp:
server_key = fp.read()
with open("server.pem", "rb") as fp:
server_cert = fp.read()
with open("ca.pem", "rb") as fp:
ca_cert = fp.read()
creds = grpc.ssl_server_credentials(
[(server_key, server_cert)],
root_certificates=ca_cert,
require_client_auth=True,
)
server.add_secure_port("[::]:443", creds)
server.start()
server.wait_for_termination()
if __name__ == "__main__":
serve()
# -*- coding: utf-8 -*-
# flake8: noqa
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: recommendations.proto
from google.protobuf.internal import enum_type_wrapper
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="recommendations.proto",
package="",
syntax="proto3",
serialized_options=None,
serialized_pb=b'\n\x15recommendations.proto"^\n\x15RecommendationRequest\x12\x0f\n\x07user_id\x18\x01 \x01(\x05\x12\x1f\n\x08\x63\x61tegory\x18\x02 \x01(\x0e\x32\r.BookCategory\x12\x13\n\x0bmax_results\x18\x03 \x01(\x05"/\n\x12\x42ookRecommendation\x12\n\n\x02id\x18\x01 \x01(\x05\x12\r\n\x05title\x18\x02 \x01(\t"F\n\x16RecommendationResponse\x12,\n\x0frecommendations\x18\x01 \x03(\x0b\x32\x13.BookRecommendation*?\n\x0c\x42ookCategory\x12\x0b\n\x07MYSTERY\x10\x00\x12\x13\n\x0fSCIENCE_FICTION\x10\x01\x12\r\n\tSELF_HELP\x10\x02\x32O\n\x0fRecommendations\x12<\n\tRecommend\x12\x16.RecommendationRequest\x1a\x17.RecommendationResponseb\x06proto3',
)
_BOOKCATEGORY = _descriptor.EnumDescriptor(
name="BookCategory",
full_name="BookCategory",
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name="MYSTERY",
index=0,
number=0,
serialized_options=None,
type=None,
),
_descriptor.EnumValueDescriptor(
name="SCIENCE_FICTION",
index=1,
number=1,
serialized_options=None,
type=None,
),
_descriptor.EnumValueDescriptor(
name="SELF_HELP",
index=2,
number=2,
serialized_options=None,
type=None,
),
],
containing_type=None,
serialized_options=None,
serialized_start=242,
serialized_end=305,
)
_sym_db.RegisterEnumDescriptor(_BOOKCATEGORY)
BookCategory = enum_type_wrapper.EnumTypeWrapper(_BOOKCATEGORY)
MYSTERY = 0
SCIENCE_FICTION = 1
SELF_HELP = 2
_RECOMMENDATIONREQUEST = _descriptor.Descriptor(
name="RecommendationRequest",
full_name="RecommendationRequest",
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name="user_id",
full_name="RecommendationRequest.user_id",
index=0,
number=1,
type=5,
cpp_type=1,
label=1,
has_default_value=False,
default_value=0,
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
_descriptor.FieldDescriptor(
name="category",
full_name="RecommendationRequest.category",
index=1,
number=2,
type=14,
cpp_type=8,
label=1,
has_default_value=False,
default_value=0,
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
_descriptor.FieldDescriptor(
name="max_results",
full_name="RecommendationRequest.max_results",
index=2,
number=3,
type=5,
cpp_type=1,
label=1,
has_default_value=False,
default_value=0,
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=25,
serialized_end=119,
)
_BOOKRECOMMENDATION = _descriptor.Descriptor(
name="BookRecommendation",
full_name="BookRecommendation",
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name="id",
full_name="BookRecommendation.id",
index=0,
number=1,
type=5,
cpp_type=1,
label=1,
has_default_value=False,
default_value=0,
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
_descriptor.FieldDescriptor(
name="title",
full_name="BookRecommendation.title",
index=1,
number=2,
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=121,
serialized_end=168,
)
_RECOMMENDATIONRESPONSE = _descriptor.Descriptor(
name="RecommendationResponse",
full_name="RecommendationResponse",
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name="recommendations",
full_name="RecommendationResponse.recommendations",
index=0,
number=1,
type=11,
cpp_type=10,
label=3,
has_default_value=False,
default_value=[],
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=170,
serialized_end=240,
)
_RECOMMENDATIONREQUEST.fields_by_name["category"].enum_type = _BOOKCATEGORY
_RECOMMENDATIONRESPONSE.fields_by_name[
"recommendations"
].message_type = _BOOKRECOMMENDATION
DESCRIPTOR.message_types_by_name[
"RecommendationRequest"
] = _RECOMMENDATIONREQUEST
DESCRIPTOR.message_types_by_name["BookRecommendation"] = _BOOKRECOMMENDATION
DESCRIPTOR.message_types_by_name[
"RecommendationResponse"
] = _RECOMMENDATIONRESPONSE
DESCRIPTOR.enum_types_by_name["BookCategory"] = _BOOKCATEGORY
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
RecommendationRequest = _reflection.GeneratedProtocolMessageType(
"RecommendationRequest",
(_message.Message,),
{
"DESCRIPTOR": _RECOMMENDATIONREQUEST,
"__module__": "recommendations_pb2"
# @@protoc_insertion_point(class_scope:RecommendationRequest)
},
)
_sym_db.RegisterMessage(RecommendationRequest)
BookRecommendation = _reflection.GeneratedProtocolMessageType(
"BookRecommendation",
(_message.Message,),
{
"DESCRIPTOR": _BOOKRECOMMENDATION,
"__module__": "recommendations_pb2"
# @@protoc_insertion_point(class_scope:BookRecommendation)
},
)
_sym_db.RegisterMessage(BookRecommendation)
RecommendationResponse = _reflection.GeneratedProtocolMessageType(
"RecommendationResponse",
(_message.Message,),
{
"DESCRIPTOR": _RECOMMENDATIONRESPONSE,
"__module__": "recommendations_pb2"
# @@protoc_insertion_point(class_scope:RecommendationResponse)
},
)
_sym_db.RegisterMessage(RecommendationResponse)
_RECOMMENDATIONS = _descriptor.ServiceDescriptor(
name="Recommendations",
full_name="Recommendations",
file=DESCRIPTOR,
index=0,
serialized_options=None,
serialized_start=307,
serialized_end=386,
methods=[
_descriptor.MethodDescriptor(
name="Recommend",
full_name="Recommendations.Recommend",
index=0,
containing_service=None,
input_type=_RECOMMENDATIONREQUEST,
output_type=_RECOMMENDATIONRESPONSE,
serialized_options=None,
),
],
)
_sym_db.RegisterServiceDescriptor(_RECOMMENDATIONS)
DESCRIPTOR.services_by_name["Recommendations"] = _RECOMMENDATIONS
# @@protoc_insertion_point(module_scope)
# flake8: noqa
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
import grpc
import recommendations_pb2 as recommendations__pb2
class RecommendationsStub(object):
"""Missing associated documentation comment in .proto file"""
def __init__(self, channel):
"""Constructor.
Args:
channel: A grpc.Channel.
"""
self.Recommend = channel.unary_unary(
"/Recommendations/Recommend",
request_serializer=recommendations__pb2.RecommendationRequest.SerializeToString,
response_deserializer=recommendations__pb2.RecommendationResponse.FromString,
)
class RecommendationsServicer(object):
"""Missing associated documentation comment in .proto file"""
def Recommend(self, request, context):
"""Missing associated documentation comment in .proto file"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details("Method not implemented!")
raise NotImplementedError("Method not implemented!")
def add_RecommendationsServicer_to_server(servicer, server):
rpc_method_handlers = {
"Recommend": grpc.unary_unary_rpc_method_handler(
servicer.Recommend,
request_deserializer=recommendations__pb2.RecommendationRequest.FromString,
response_serializer=recommendations__pb2.RecommendationResponse.SerializeToString,
),
}
generic_handler = grpc.method_handlers_generic_handler(
"Recommendations", rpc_method_handlers
)
server.add_generic_rpc_handlers((generic_handler,))
# This class is part of an EXPERIMENTAL API.
class Recommendations(object):
"""Missing associated documentation comment in .proto file"""
@staticmethod
def Recommend(
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,
"/Recommendations/Recommend",
recommendations__pb2.RecommendationRequest.SerializeToString,
recommendations__pb2.RecommendationResponse.FromString,
options,
channel_credentials,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
)
from recommendations import RecommendationService
from recommendations_pb2 import BookCategory, RecommendationRequest
def test_recommendations():
service = RecommendationService()
request = RecommendationRequest(
user_id=1, category=BookCategory.MYSTERY, max_results=1
)
response = service.Recommend(request, None)
assert len(response.recommendations) == 1
grpc-interceptor ~= 0.11.0
grpcio-tools ~= 1.30
pytest ~= 5.4
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册