-
Notifications
You must be signed in to change notification settings - Fork 566
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Santiago Pericas-Geertsen <[email protected]>
- Loading branch information
Showing
3 changed files
with
225 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,222 @@ | ||
/////////////////////////////////////////////////////////////////////////////// | ||
|
||
Copyright (c) 2019, 2024 Oracle and/or its affiliates. | ||
|
||
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. | ||
|
||
/////////////////////////////////////////////////////////////////////////////// | ||
= gRPC MP Client | ||
:description: Building Helidon gRPC MicroProfile Clients | ||
:keywords: helidon, java, grpc, microprofile, micro-profile, mp | ||
:feature-name: gRPC MicroProfile Clients | ||
:rootdir: {docdir}/../.. | ||
:microprofile-bundle: false | ||
include::{rootdir}/includes/mp.adoc[] | ||
== Contents | ||
- <<Overview, Overview>> | ||
- <<Maven Coordinates, Maven Coordinates>> | ||
- <<API, API>> | ||
- <<Configuration, Configuration>> | ||
** <<Configuring TLS, Configuring TLS>> | ||
- <<Usage, Usage>> | ||
** <<Defining a Client Interface, Defining a Client Interface>> | ||
** <<Injecting Client Proxies, Injecting Client Proxies>> | ||
** <<Injecting Channels, Injecting Channels>> | ||
- <<Examples, Examples>> | ||
== Overview | ||
Building Java-based gRPC clients using the Helidon MP gRPC API is very simple and removes a lot of | ||
the boilerplate code typically associated with more traditional approaches of writing gRPC clients. | ||
At its simplest, a gRPC Java client can be written using nothing more than a suitably annotated | ||
Java interface. | ||
include::{rootdir}/includes/dependencies.adoc[] | ||
[source,xml] | ||
---- | ||
<dependency> | ||
<groupId>io.helidon.microprofile.grpc</groupId> | ||
<artifactId>helidon-microprofile-grpc-client</artifactId> | ||
</dependency> | ||
---- | ||
== API | ||
The following annotations are used to work with Helidon MP gRPC clients: | ||
* `@Grpc.GrpcChannel` - an annotation used to inject a gRPC channel. | ||
* `@Grpc.GrpcProxy` - an annotation used to mark an injection point for a gRPC service client proxy. | ||
* `@Grpc.GrpcService` - an annotation used to specify the name of a gRPC service to connect to. | ||
== Configuration | ||
For a gRPC client to connect to a server, it requires a channel. Channels are configured in the | ||
`grpc` section of the Helidon application configuration. The examples below use an `application.yaml` | ||
file but there are many other ways to to configure Helidon. See | ||
xref:{rootdir}/mp/config/introduction.adoc[Configuration in Helidon] for more information. | ||
[source,yaml] | ||
---- | ||
grpc: | ||
client: | ||
channels: # <1> | ||
- name: "string-channel" # <2> | ||
host: localhost # <3> | ||
port: 8080 # <4> | ||
---- | ||
<1> Channels are configured in the `channels` section under `grpc.client`. | ||
<2> The name of the channel as referred to in the application code. | ||
<3> The host name for the channel (defaults to localhost). | ||
<4> The port number for the channel (defaults to 1408). | ||
While most client applications only connect to a single server, it is possible to configure multiple | ||
(an array of) named channels if the client needs to connect to multiple servers. | ||
=== Configuring TLS | ||
gRPC runs on top of HTTP/2 which prefers secure TLS connections. Most gRPC channels will also | ||
include a section to configure TLS. Here is a sample of that configuration for the `string-channel`: | ||
[source,yaml] | ||
---- | ||
grpc: | ||
client: | ||
channels: | ||
- name: "string-channel" | ||
port: 8080 | ||
tls: | ||
trust: | ||
keystore: | ||
passphrase: "password" | ||
trust-store: true | ||
resource: | ||
resource-path: "client.p12" | ||
private-key: | ||
keystore: | ||
passphrase: "password" | ||
resource: | ||
resource-path: "client.p12" | ||
---- | ||
TLS in the gRPC MP client section is configured in the same way as in other Helidon | ||
components such as the webserver. For more information see | ||
xref:{rootdir}/se/webserver.adoc#_configuring_tls[Configuring TLS]. | ||
== Usage | ||
=== Defining a Client Interface | ||
The next step is to produce an interface with the service methods that the client requires. | ||
For example, suppose we have a simple service that has a unary method to convert a string | ||
to uppercase. To write a client for this service, all that is required is an interface | ||
as shown next: | ||
[source,java] | ||
---- | ||
@ApplicationScoped | ||
@Grpc.GrpcService("StringService") // <1> | ||
@Grpc.GrpcChannel("string-channel") // <2> | ||
interface StringServiceClient { | ||
@Grpc.Unary | ||
String upper(String s); | ||
} | ||
---- | ||
<1> The `@Grpc.GrpcService` annotation is necessary to provide the name of the gRPC | ||
service when it differs from the interface name, as it is the case in this example. | ||
<2> The `@Grpc.GrpcChannel` annotation is the qualifier that supplies the channel name. | ||
This is the same name as used in the channel configuration in the examples provided in | ||
the <<Configuration, Configuration Section>>. | ||
There is no need to write any code to implement the client. The Helidon MP gRPC API will | ||
create a dynamic proxy for the interface using the information from the annotations and | ||
method signatures. | ||
The interface in the example above uses the same method signature as the server, but this | ||
does not need to be the case. For example, it can use a `StreamObserver<String>` as a | ||
second parameter to return the result: | ||
[source,java] | ||
---- | ||
@ApplicationScoped | ||
@Grpc.GrpcService("StringService") | ||
@Grpc.GrpcChannel("string-channel") | ||
interface StringServiceClient { | ||
@Grpc.Unary | ||
void upper(String s, StreamObserver<String> response); | ||
} | ||
---- | ||
=== Injecting Client Proxies | ||
Now that there is a client interface and a channel configuration, we can then use these | ||
in the client application. | ||
We can declare a field of the same type as the client service interface in the application | ||
class that requires the client. The field is then annotated so that CDI will inject the | ||
client proxy into the field. | ||
[source,java] | ||
---- | ||
@ApplicationScoped | ||
public class MyAppBean { | ||
@Inject // <1> | ||
@Grpc.GrpcProxy // <2> | ||
@Grpc.GrpcChannel("string-channel") // <3> | ||
private StringServiceClient stringServiceClient; | ||
} | ||
---- | ||
<1> The `@Inject` annotation tells CDI to inject the client implementation. | ||
<2> The `@Grpc.GrpcProxy` annotation is used by the CDI container to match the injection point to | ||
the gRPC MP APIs provider. | ||
<3> The `@Grpc.GrpcChannel` annotation identifies the gRPC channel to be used by the client. The name | ||
used in the annotation refers to a channel name in the application configuration. | ||
When the CDI container instantiates `MyAppBean`, it will inject a dynamic proxy into | ||
the `stringServiceClient` field, and then provide the necessary logic for the proxy | ||
methods to convert a method call into a gRPC call. | ||
In the example above, there is no need to use a channel directly. The correct channel is added to | ||
the dynamic client proxy internally by the Helidon MP gRPC APIs. | ||
=== Injecting Channels | ||
Channels can also be in directly injected into application bean instances. | ||
The Helidon gRPC client APIs have CDI producers that can provide `io.grpc.Channel` instances. | ||
For example, a class might have an injectable `io.grpc.Channel` field as follows: | ||
[source,java] | ||
---- | ||
@Inject // <1> | ||
@Grpc.GrpcChannel("string-channel") // <2> | ||
private Channel channel; | ||
---- | ||
<1> The `@Inject` annotation tells CDI to inject the channel. | ||
<2> The `@Grpc.GrpcChannel` annotation is the qualifier that supplies the channel name. | ||
This is the same name as used in the channel configuration in the examples provided in | ||
the <<Configuration, configuration section>>. | ||
When an instance of the CDI bean with the channel field is instantiated, a channel will | ||
be injected into it. An injected channel can be used when instantiating `protoc` generated stub, | ||
for example. | ||
== Examples | ||
Please refer to the link:{helidon-github-examples-url}/microprofile/grpc[Helidon gRPC MP Example]. | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters