Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add grpc-health service implementation #2147

Merged
merged 14 commits into from
Mar 15, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
** xref:{page-version}@servicetalk-examples::grpc/index.adoc#Compression[Compression]
** xref:{page-version}@servicetalk-examples::grpc/index.adoc#Deadlines[Deadlines]
** xref:{page-version}@servicetalk-examples::grpc/index.adoc#Observer[Observer]
** xref:{page-version}@servicetalk-examples::grpc/index.adoc#Health[Health Checking]
** xref:{page-version}@servicetalk-examples::grpc/index.adoc#errors[Application Errors]
** xref:{page-version}@servicetalk-examples::grpc/index.adoc#execution-strategy[Execution Strategy]
** xref:{page-version}@servicetalk-examples::grpc/index.adoc#route-guide[Route Guide]
Expand Down
15 changes: 14 additions & 1 deletion servicetalk-examples/docs/modules/ROOT/pages/grpc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ Extends the blocking "Hello World" example to demonstrate configuration of debug
* link:{source-root}/servicetalk-examples/grpc/debugging/src/main/java/io/servicetalk/examples/grpc/debugging/DebuggingClient.java[DebuggingClient]
– Sends hello requests to the server and receives a greeting response.


[#Observer]
== Observer
This example demonstrates the following:
Expand All @@ -122,6 +121,20 @@ on the server builder.
link:{source-root}/servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/GrpcLifecycleObserver.java[GrpcLifecycleObserver]
on via a client filter on the client builder.

[#Health]
== Health Checking
This example demonstrates the following:
- Use of
link:{source-root}/servicetalk-grpc-health/src/main/java/io/servicetalk/grpc/health/DefaultHealthService.java[DefaultHealthService]
which implements link:https://github.com/grpc/grpc/blob/master/doc/health-checking.md[gRPC health checking] paired with a simple "hello world" service.

Using the following classes:
Scottmitch marked this conversation as resolved.
Show resolved Hide resolved
* link:{source-root}/servicetalk-examples/grpc/health/src/main/java/io/servicetalk/examples/grpc/health/HealthServerExample.java[HealthServerExample] a server
that installs link:{source-root}/servicetalk-grpc-health/src/main/java/io/servicetalk/grpc/health/DefaultHealthService.java[DefaultHealthService] in addition to
a simple "hello world" service.
* link:{source-root}/servicetalk-examples/grpc/health/src/main/java/io/servicetalk/examples/grpc/health/HealthClientExample.java[HealthClientExample] a client
that calls the "hello world" server, the "health check" server, and prints results.

[#errors]
== Application Errors
The gRPC protocol supports propagating application level errors, and also provides serialization/deserialization of
Expand Down
3 changes: 3 additions & 0 deletions servicetalk-examples/grpc/health/README.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
== ServiceTalk gRPC Health Example

Extends "Hello World" ServiceTalk gRPC example to demonstrate default health service.
81 changes: 81 additions & 0 deletions servicetalk-examples/grpc/health/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright © 2022 Apple Inc. and the ServiceTalk project 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.
*/

buildscript {
dependencies {
classpath "com.google.protobuf:protobuf-gradle-plugin:$protobufGradlePluginVersion"
}
}

apply plugin: "java"
apply plugin: "com.google.protobuf"
apply from: "../../gradle/idea.gradle"

dependencies {
implementation project(":servicetalk-annotations")
implementation project(":servicetalk-grpc-netty")
implementation project(":servicetalk-grpc-protoc")
implementation project(":servicetalk-grpc-protobuf")
implementation project(":servicetalk-grpc-health")

implementation "org.slf4j:slf4j-api:$slf4jVersion"
runtimeOnly "org.apache.logging.log4j:log4j-slf4j-impl:$log4jVersion"
}

protobuf {
protoc {
artifact = "com.google.protobuf:protoc:$protobufVersion"
}

//// REMOVE if outside of ServiceTalk gradle project
def pluginJar = file("${project.rootProject.rootDir}/servicetalk-grpc-protoc/build" +
"/buildExecutable/servicetalk-grpc-protoc-${project.version}-all.jar")
//// REMOVE if outside of ServiceTalk gradle project

plugins {
servicetalk_grpc {
//// REMOVE if outside of ServiceTalk gradle project - use "artifact" as demonstrated below
//// "path" is used only because we want to use the gradle project local version of the plugin.
path = pluginJar.path
//// REMOVE if outside of ServiceTalk gradle project - use "artifact" as demonstrated below

// artifact = "io.servicetalk:servicetalk-grpc-protoc:$serviceTalkVersion:all@jar"
}
}
generateProtoTasks {
all().each { task ->
//// REMOVE if outside of ServiceTalk gradle project
task.dependsOn(":servicetalk-grpc-protoc:buildExecutable") // use gradle project local grpc-protoc dependency

// you may need to manually add the artifact name as an input
task.inputs
.file(pluginJar)
.withNormalizer(ClasspathNormalizer)
.withPropertyName("servicetalkPluginJar")
.withPathSensitivity(PathSensitivity.RELATIVE)
//// REMOVE if outside of ServiceTalk gradle project

task.plugins {
servicetalk_grpc {
// Need to tell protobuf-gradle-plugin to output in the correct directory if all generated
// code for a single proto goes to a single file (e.g. "java_multiple_files = false" in the .proto).
outputSubDir = "java"
}
}
}
}
generatedFilesBaseDir = "$buildDir/generated/sources/proto"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright © 2022 Apple Inc. and the ServiceTalk project 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.
*/
package io.servicetalk.examples.grpc.health;

import io.servicetalk.grpc.api.GrpcStatusException;
import io.servicetalk.grpc.health.DefaultHealthService;
import io.servicetalk.grpc.netty.GrpcClients;
import io.servicetalk.health.v1.Health;
import io.servicetalk.health.v1.Health.BlockingHealthClient;
import io.servicetalk.health.v1.HealthCheckRequest;
import io.servicetalk.health.v1.HealthCheckResponse;

import io.grpc.examples.health.Greeter;
import io.grpc.examples.health.Greeter.BlockingGreeterClient;
import io.grpc.examples.health.HelloReply;
import io.grpc.examples.health.HelloRequest;

/**
* Extends the async "Hello World" example to demonstrate {@link DefaultHealthService} usage.
*/
public final class HealthClientExample {
public static void main(String... args) throws Exception {
final String serviceName = "World";
try (BlockingGreeterClient client = GrpcClients.forAddress("localhost", 8080)
.buildBlocking(new Greeter.ClientFactory());
BlockingHealthClient healthClient = GrpcClients.forAddress("localhost", 8080)
.buildBlocking(new Health.ClientFactory())) {
// Check health before
checkHealth(healthClient, serviceName);

HelloReply reply = client.sayHello(HelloRequest.newBuilder().setName("World").build());
System.out.println("HelloReply=" + reply.getMessage());

// Check the health after to observe it changed.
checkHealth(healthClient, serviceName);
}
}

private static void checkHealth(BlockingHealthClient healthClient, String serviceName) throws Exception {
try {
HealthCheckResponse response = healthClient.check(
HealthCheckRequest.newBuilder().setService(serviceName).build());
System.out.println("Service '" + serviceName + "' health=" + response.getStatus());
} catch (GrpcStatusException e) {
System.out.println("Service '" + serviceName + "' health exception=" + e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright © 2022 Apple Inc. and the ServiceTalk project 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.
*/
package io.servicetalk.examples.grpc.health;

import io.servicetalk.grpc.health.DefaultHealthService;
import io.servicetalk.grpc.netty.GrpcServers;

import io.grpc.examples.health.Greeter.GreeterService;
import io.grpc.examples.health.HelloReply;

import static io.servicetalk.concurrent.api.Single.succeeded;
import static io.servicetalk.health.v1.HealthCheckResponse.ServingStatus.SERVING;

/**
* A simple extension of the gRPC "Hello World" example which demonstrates {@link DefaultHealthService}.
*/
public final class HealthServerExample {
public static void main(String... args) throws Exception {
DefaultHealthService healthService = new DefaultHealthService();
GreeterService greeterService = (ctx, request) -> {
// For demonstration purposes, just use the name as a service and mark it as SERVING.
healthService.setStatus(request.getName(), SERVING);
return succeeded(HelloReply.newBuilder().setMessage("Hello " + request.getName()).build());
};
GrpcServers.forPort(8080)
.listenAndAwait(healthService, greeterService)
.awaitShutdown();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright © 2022 Apple Inc. and the ServiceTalk project 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.
*/
@ElementsAreNonnullByDefault
package io.servicetalk.examples.grpc.health;

import io.servicetalk.annotations.ElementsAreNonnullByDefault;
37 changes: 37 additions & 0 deletions servicetalk-examples/grpc/health/src/main/proto/helloworld.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2015 The 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.health";
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 {
string message = 1;
}
35 changes: 35 additions & 0 deletions servicetalk-examples/grpc/health/src/main/resources/log4j2.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright © 2018, 2021 Apple Inc. and the ServiceTalk project 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.
-->
<Configuration status="info">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d %30t [%-5level] %-30logger{1} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<!-- Prints server start and shutdown -->
<Logger name="io.servicetalk.http.netty.H2ServerParentConnectionContext" level="DEBUG"/>

<!-- Prints default subscriber errors-->
<Logger name="io.servicetalk.concurrent.api" level="DEBUG"/>

<!-- Use `-Dservicetalk.logger.level=DEBUG` to change the root logger level via command line -->
<Root level="${sys:servicetalk.logger.level:-INFO}">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,12 @@ final class ServiceTalkLibraryPlugin extends ServiceTalkCorePlugin {
incrementalAnalysis = true
ruleSets = []
ruleSetConfig = resources.text.fromString(getClass().getResourceAsStream("pmd/basic.xml").text)

pmdMain {
excludes = [
'**/src/generated/**'
]
}
}

tasks.withType(Pmd).all {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,7 @@
<suppress checks="LineLength" files="docs[\\/]modules[\\/]ROOT[\\/]pages[\\/].*\.adoc"/>
<suppress checks="LineLength" files="docs[\\/]generation[\\/]supplemental-ui[\\/]partials[\\/].*\.hbs"/>
<suppress checks="LineLength" files="docs[\\/]generation[\\/]site-remote.yml"/>

<!-- generated -->
<suppress checks="." files=".*[\\/]src[\\/]generated[\\/].*"/>
</suppressions>
Loading