Skip to main content

gRPC

In addition to running Reboot servicers, a Reboot application can also run "legacy" gRPC servicers, i.e., classes that implement a gRPC service. This is a particularly useful setup during a multi-service migration from gRPC to Reboot or when needing to pull in pre-existing service implementations that do not use Reboot.

note

This feature is not yet implemented for TypeScript. Please reach out if it would be important for you!

To include a gRPC servicer in a Reboot application, pass its class into a Reboot Application with the legacy_grpc_servicers parameter.

await Application(
servicers=[RebootGreeterServicer] + reboot.aio.memoize.servicers(),
legacy_grpc_servicers=[
DeprecatedGreeterServicer, ProxyGreeterServicer
],
initialize=initialize,
).run()

google.api.http

Reboot exposes support for the google.api.http annotations on legacy gRPC methods allowing you to expose them as arbitrary HTTP endpoints.

To expose a legacy gRPC method as an HTTP endpoint, annotate it with google.api.http options:

// Return the current time once.
rpc CurrentTime(google.protobuf.Empty) returns (Time) {
option (google.api.http) = {
// Also make this method available via GET, at a custom path.
get: "/current_time"
};
}

...and then call it using any HTTP client:

$ curl -XGET localhost:9991/current_time
...

Calling a legacy gRPC servicer from a Reboot servicer

Reboot readers, writers, transactions, and workflows can use their passed-in context to construct gRPC channels and call methods on legacy gRPC servicers. Here is an example:

async with context.legacy_grpc_channel() as channel:
# Now that we have a channel for our legacy servicer, we can call
# its gRPC methods in the standard gRPC fashion.
deprecated_greeter_stub = greeter_pb2_grpc.DeprecatedGreeterStub(
channel
)

salutation_response = await deprecated_greeter_stub.GetSalutation(
Empty()
)

Calling a Reboot servicer from a legacy gRPC servicer

When you run a legacy gRPC servicer using Reboot we will pass a LegacyGrpcContext instead of a grpc.ServicerContext to each of the legacy gRPC servicers methods. LegacyGrpcContext is a custom subclass of grpc.ServicerContext that adds the ability to construct a Reboot ExternalContext. The servicer can then use the ExternalContext to act as a client for Reboot servicers.

For example, here is a Greet method of a legacy gRPC servicer getting an ExternalContext to make a call:

async def Greet(
self,
request: greeter_pb2.GreetRequest,
legacy_context: LegacyGrpcContext,
) -> greeter_pb2.GreetResponse:
# As part of a migration, one may want to slowly ramp up traffic to the
# new Reboot service. This proxy servicer will forward traffic to
# either the RebootGreeter or the DeprecatedGreeter with a 50/50
# ratio.
context = legacy_context.external_context(
name="Call into `RebootGreeter`"
)
if random.random() < 0.5:
# Route to RebootGreeter.
reboot_greeter = RebootGreeter.ref("my-greeter")
return await reboot_greeter.Greet(context, name=request.name)
else:
# Route to DeprecatedGreeter.
async with context.legacy_grpc_channel() as channel:
deprecated_greeter_stub = greeter_pb2_grpc.DeprecatedGreeterStub(
channel
)

return await deprecated_greeter_stub.Greet(request)