multiple protocols in a single Jina Flow?

Question:

I would like to serve both gRPC and HTTP in my flow, but the flow description only allows a single value in the protocol parameter. Is it possible to add both? If not, do i have to deploy two flows or is there a better workaround?

The documentation doesn’t mention if i can have two gateways from what i can see?

f = Flow(protocol='grpc', port=12345).add(uses=FooExecutor)
with f:
    client = Client(port=12345)
    docs = client.post(on='/')
    print(docs.texts)
Asked By: fogx

||

Answers:

Unfortunately by default, no.

But you can develop your own custom gateway that enables both protocols at the same time.

A sample custom gateway looks like the following (borrowed from here)

import grpc
from grpc_health.v1 import health, health_pb2, health_pb2_grpc
from grpc_reflection.v1alpha import reflection
from pydantic import BaseModel
from uvicorn import Config, Server

from jina import Gateway, __default_host__
from jina.proto import jina_pb2, jina_pb2_grpc


class DummyResponseModel(BaseModel):
    protocol: str


class MultiProtocolGateway(Gateway):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.http_port = self.ports[0]
        self.grpc_port = self.ports[1]
        self.health_servicer = health.HealthServicer(experimental_non_blocking=True)

    async def _setup_http_server(self):
        from fastapi import FastAPI

        app = FastAPI(
            title='HTTP Server',
        )

        @app.get(path='/', response_model=DummyResponseModel)
        def _get_response():
            return {'protocol': 'http'}

        self.http_server = Server(
            Config(app, host=__default_host__, port=self.http_port)
        )

    async def _setup_grpc_server(self):
        self.grpc_server = grpc.aio.server()

        jina_pb2_grpc.add_JinaRPCServicer_to_server(
            self.streamer._streamer, self.grpc_server
        )

        service_names = (
            jina_pb2.DESCRIPTOR.services_by_name['JinaRPC'].full_name,
            reflection.SERVICE_NAME,
        )
        # Mark all services as healthy.
        health_pb2_grpc.add_HealthServicer_to_server(
            self.health_servicer, self.grpc_server
        )
        for service in service_names:
            self.health_servicer.set(service, health_pb2.HealthCheckResponse.SERVING)
        reflection.enable_server_reflection(service_names, self.grpc_server)
        self.grpc_server.add_insecure_port(f'{__default_host__}:{self.grpc_port}')
        await self.grpc_server.start()

    async def setup_server(self):
        await self._setup_http_server()
        await self._setup_grpc_server()

    async def run_server(self):
        await self.http_server.serve()
        await self.grpc_server.wait_for_termination()

    async def shutdown(self):
        self.http_server.should_exit = True
        await self.grpc_server.stop(0)
        await self.http_server.shutdown()
        self.health_servicer.enter_graceful_shutdown()

    @property
    def _should_exit(self) -> bool:
        return self.http_server.should_exit

And you can access it in the following way:

from xxx import MultiProtocolGateway
from xxx import MyExecutor
from jina import Flow, Client, DocumentArray

http_port = 51000
grpc_port = 52000
flow = Flow().config_gateway(
    uses=MultiProtocolGateway,
    port=[http_port, grpc_port],
    protocol=['http', 'grpc'],
).add(MyExecutor)

with flow:
    c1 = Client(host='http://0.0.0.0:51000)
    c1.post(on='/', inputs=DocumentArray().empty(5))
    
    c2 = Client(host='grpc://0.0.0.0:52000)
    c2.post(on='/', inputs=DocumentArray().empty(5))
Answered By: Ziniu Yu
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.