Introducing super basic health check for cart service (#44)

* Introducing super basic health check for cart service
  - Generated C# proto implementation for grpc health check
  - Moved all C# protos to a dedicated folder
  - Implemented basic health checking to ping CartStore (which is Redis in default implementation)
  - Base plumbing for health checks

* Introducing super basic health check for cart service

- Generated C# proto implementation for grpc health check
- Moved all C# protos to a dedicated folder
- Implemented basic health checking to ping CartStore (which is Redis in default implementation)
- Base plumbing for health checks

* Changing Ping health probe to call Redis Cache Ping method
This commit is contained in:
Simon Zeltser 2018-09-21 19:09:52 +00:00 committed by Ahmet Alp Balkan
parent 1bab006af1
commit 1f60819dee
9 changed files with 770 additions and 895 deletions

View file

@ -0,0 +1,22 @@
using System;
using cartservice.interfaces;
using Grpc.Health.V1;
using StackExchange.Redis;
using static Grpc.Health.V1.Health;
namespace cartservice {
internal class HealthImpl : HealthBase {
private ICartStore dependency { get; }
public HealthImpl (ICartStore dependency) {
this.dependency = dependency;
}
public HealthCheckResponse Check (HealthCheckRequest request) {
Console.WriteLine ("Checking CartService Health");
return new HealthCheckResponse {
Status = dependency.Ping() ? HealthCheckResponse.Types.ServingStatus.Serving : HealthCheckResponse.Types.ServingStatus.NotServing
};
}
}
}

View file

@ -56,7 +56,14 @@ namespace cartservice
Console.WriteLine($"Trying to start a grpc server at {host}:{port}");
Server server = new Server
{
Services = { Hipstershop.CartService.BindService(new CartServiceImpl(cartStore)) },
Services =
{
// Cart Service Endpoint
Hipstershop.CartService.BindService(new CartServiceImpl(cartStore)),
// Health Endpoint
Grpc.Health.V1.Health.BindService(new HealthImpl(cartStore))
},
Ports = { new ServerPort(host, port, ServerCredentials.Insecure) }
};

View file

@ -7,9 +7,10 @@
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.2.1" />
<PackageReference Include="Google.Protobuf" Version="3.5.1" />
<PackageReference Include="Google.Protobuf" Version="3.6.1" />
<PackageReference Include="Google.Protobuf.Tools" Version="3.5.1" />
<PackageReference Include="grpc" Version="1.12.0" />
<PackageReference Include="Grpc.HealthCheck" Version="1.15.0" />
<PackageReference Include="grpc.tools" Version="1.12.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.1.1" />

View file

@ -82,5 +82,10 @@ namespace cartservice.cartstore
return Task.FromResult(cart);
}
public bool Ping()
{
return true;
}
}
}

View file

@ -197,5 +197,20 @@ namespace cartservice.cartstore
throw new RpcException(new Status(StatusCode.FailedPrecondition, $"Can't access cart storage. {ex}"));
}
}
public bool Ping()
{
try
{
var redis = ConnectionMultiplexer.Connect(redisConnectionOptions);
var cache = redis.GetDatabase();
var res = cache.Ping();
return res != TimeSpan.Zero;
}
catch (Exception)
{
return false;
}
}
}
}

View file

@ -22,6 +22,6 @@ cd /d %~dp0
set NUGET_PATH=%UserProfile%\.nuget\packages
set TOOLS_PATH=%NUGET_PATH%\Grpc.Tools\1.12.0\tools\windows_x64
%TOOLS_PATH%\protoc.exe -I%~dp0/../../pb;%NUGET_PATH%\google.protobuf.tools\3.5.1\tools\ --csharp_out %~dp0 %~dp0\..\..\pb\demo.proto --grpc_out %~dp0 --plugin=protoc-gen-grpc=%TOOLS_PATH%\grpc_csharp_plugin.exe
%TOOLS_PATH%\protoc.exe -I%~dp0/../../pb;%NUGET_PATH%\google.protobuf.tools\3.5.1\tools\ --csharp_out %~dp0\grpc_generated %~dp0\..\..\pb\demo.proto --grpc_out %~dp0\grpc_generated --plugin=protoc-gen-grpc=%TOOLS_PATH%\grpc_csharp_plugin.exe
endlocal

View file

@ -1,17 +1,3 @@
// Copyright 2018 Google LLC
//
// 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.
// <auto-generated>
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: demo.proto
@ -522,8 +508,8 @@ namespace Hipstershop {
static readonly grpc::Marshaller<global::Hipstershop.Empty> __Marshaller_Empty = grpc::Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Hipstershop.Empty.Parser.ParseFrom);
static readonly grpc::Marshaller<global::Hipstershop.GetSupportedCurrenciesResponse> __Marshaller_GetSupportedCurrenciesResponse = grpc::Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Hipstershop.GetSupportedCurrenciesResponse.Parser.ParseFrom);
static readonly grpc::Marshaller<global::Hipstershop.ConversionRequest> __Marshaller_ConversionRequest = grpc::Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Hipstershop.ConversionRequest.Parser.ParseFrom);
static readonly grpc::Marshaller<global::Hipstershop.ConversionResponse> __Marshaller_ConversionResponse = grpc::Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Hipstershop.ConversionResponse.Parser.ParseFrom);
static readonly grpc::Marshaller<global::Hipstershop.CurrencyConversionRequest> __Marshaller_CurrencyConversionRequest = grpc::Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Hipstershop.CurrencyConversionRequest.Parser.ParseFrom);
static readonly grpc::Marshaller<global::Hipstershop.Money> __Marshaller_Money = grpc::Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Hipstershop.Money.Parser.ParseFrom);
static readonly grpc::Method<global::Hipstershop.Empty, global::Hipstershop.GetSupportedCurrenciesResponse> __Method_GetSupportedCurrencies = new grpc::Method<global::Hipstershop.Empty, global::Hipstershop.GetSupportedCurrenciesResponse>(
grpc::MethodType.Unary,
@ -532,12 +518,12 @@ namespace Hipstershop {
__Marshaller_Empty,
__Marshaller_GetSupportedCurrenciesResponse);
static readonly grpc::Method<global::Hipstershop.ConversionRequest, global::Hipstershop.ConversionResponse> __Method_Convert = new grpc::Method<global::Hipstershop.ConversionRequest, global::Hipstershop.ConversionResponse>(
static readonly grpc::Method<global::Hipstershop.CurrencyConversionRequest, global::Hipstershop.Money> __Method_Convert = new grpc::Method<global::Hipstershop.CurrencyConversionRequest, global::Hipstershop.Money>(
grpc::MethodType.Unary,
__ServiceName,
"Convert",
__Marshaller_ConversionRequest,
__Marshaller_ConversionResponse);
__Marshaller_CurrencyConversionRequest,
__Marshaller_Money);
/// <summary>Service descriptor</summary>
public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor
@ -553,7 +539,7 @@ namespace Hipstershop {
throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
}
public virtual global::System.Threading.Tasks.Task<global::Hipstershop.ConversionResponse> Convert(global::Hipstershop.ConversionRequest request, grpc::ServerCallContext context)
public virtual global::System.Threading.Tasks.Task<global::Hipstershop.Money> Convert(global::Hipstershop.CurrencyConversionRequest request, grpc::ServerCallContext context)
{
throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
}
@ -599,19 +585,19 @@ namespace Hipstershop {
{
return CallInvoker.AsyncUnaryCall(__Method_GetSupportedCurrencies, null, options, request);
}
public virtual global::Hipstershop.ConversionResponse Convert(global::Hipstershop.ConversionRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
public virtual global::Hipstershop.Money Convert(global::Hipstershop.CurrencyConversionRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
{
return Convert(request, new grpc::CallOptions(headers, deadline, cancellationToken));
}
public virtual global::Hipstershop.ConversionResponse Convert(global::Hipstershop.ConversionRequest request, grpc::CallOptions options)
public virtual global::Hipstershop.Money Convert(global::Hipstershop.CurrencyConversionRequest request, grpc::CallOptions options)
{
return CallInvoker.BlockingUnaryCall(__Method_Convert, null, options, request);
}
public virtual grpc::AsyncUnaryCall<global::Hipstershop.ConversionResponse> ConvertAsync(global::Hipstershop.ConversionRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
public virtual grpc::AsyncUnaryCall<global::Hipstershop.Money> ConvertAsync(global::Hipstershop.CurrencyConversionRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
{
return ConvertAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
}
public virtual grpc::AsyncUnaryCall<global::Hipstershop.ConversionResponse> ConvertAsync(global::Hipstershop.ConversionRequest request, grpc::CallOptions options)
public virtual grpc::AsyncUnaryCall<global::Hipstershop.Money> ConvertAsync(global::Hipstershop.CurrencyConversionRequest request, grpc::CallOptions options)
{
return CallInvoker.AsyncUnaryCall(__Method_Convert, null, options, request);
}
@ -806,18 +792,9 @@ namespace Hipstershop {
{
static readonly string __ServiceName = "hipstershop.CheckoutService";
static readonly grpc::Marshaller<global::Hipstershop.CreateOrderRequest> __Marshaller_CreateOrderRequest = grpc::Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Hipstershop.CreateOrderRequest.Parser.ParseFrom);
static readonly grpc::Marshaller<global::Hipstershop.CreateOrderResponse> __Marshaller_CreateOrderResponse = grpc::Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Hipstershop.CreateOrderResponse.Parser.ParseFrom);
static readonly grpc::Marshaller<global::Hipstershop.PlaceOrderRequest> __Marshaller_PlaceOrderRequest = grpc::Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Hipstershop.PlaceOrderRequest.Parser.ParseFrom);
static readonly grpc::Marshaller<global::Hipstershop.PlaceOrderResponse> __Marshaller_PlaceOrderResponse = grpc::Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Hipstershop.PlaceOrderResponse.Parser.ParseFrom);
static readonly grpc::Method<global::Hipstershop.CreateOrderRequest, global::Hipstershop.CreateOrderResponse> __Method_CreateOrder = new grpc::Method<global::Hipstershop.CreateOrderRequest, global::Hipstershop.CreateOrderResponse>(
grpc::MethodType.Unary,
__ServiceName,
"CreateOrder",
__Marshaller_CreateOrderRequest,
__Marshaller_CreateOrderResponse);
static readonly grpc::Method<global::Hipstershop.PlaceOrderRequest, global::Hipstershop.PlaceOrderResponse> __Method_PlaceOrder = new grpc::Method<global::Hipstershop.PlaceOrderRequest, global::Hipstershop.PlaceOrderResponse>(
grpc::MethodType.Unary,
__ServiceName,
@ -834,11 +811,6 @@ namespace Hipstershop {
/// <summary>Base class for server-side implementations of CheckoutService</summary>
public abstract partial class CheckoutServiceBase
{
public virtual global::System.Threading.Tasks.Task<global::Hipstershop.CreateOrderResponse> CreateOrder(global::Hipstershop.CreateOrderRequest request, grpc::ServerCallContext context)
{
throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
}
public virtual global::System.Threading.Tasks.Task<global::Hipstershop.PlaceOrderResponse> PlaceOrder(global::Hipstershop.PlaceOrderRequest request, grpc::ServerCallContext context)
{
throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@ -869,22 +841,6 @@ namespace Hipstershop {
{
}
public virtual global::Hipstershop.CreateOrderResponse CreateOrder(global::Hipstershop.CreateOrderRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
{
return CreateOrder(request, new grpc::CallOptions(headers, deadline, cancellationToken));
}
public virtual global::Hipstershop.CreateOrderResponse CreateOrder(global::Hipstershop.CreateOrderRequest request, grpc::CallOptions options)
{
return CallInvoker.BlockingUnaryCall(__Method_CreateOrder, null, options, request);
}
public virtual grpc::AsyncUnaryCall<global::Hipstershop.CreateOrderResponse> CreateOrderAsync(global::Hipstershop.CreateOrderRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
{
return CreateOrderAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
}
public virtual grpc::AsyncUnaryCall<global::Hipstershop.CreateOrderResponse> CreateOrderAsync(global::Hipstershop.CreateOrderRequest request, grpc::CallOptions options)
{
return CallInvoker.AsyncUnaryCall(__Method_CreateOrder, null, options, request);
}
public virtual global::Hipstershop.PlaceOrderResponse PlaceOrder(global::Hipstershop.PlaceOrderRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
{
return PlaceOrder(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@ -913,10 +869,94 @@ namespace Hipstershop {
public static grpc::ServerServiceDefinition BindService(CheckoutServiceBase serviceImpl)
{
return grpc::ServerServiceDefinition.CreateBuilder()
.AddMethod(__Method_CreateOrder, serviceImpl.CreateOrder)
.AddMethod(__Method_PlaceOrder, serviceImpl.PlaceOrder).Build();
}
}
public static partial class AdService
{
static readonly string __ServiceName = "hipstershop.AdService";
static readonly grpc::Marshaller<global::Hipstershop.AdRequest> __Marshaller_AdRequest = grpc::Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Hipstershop.AdRequest.Parser.ParseFrom);
static readonly grpc::Marshaller<global::Hipstershop.AdResponse> __Marshaller_AdResponse = grpc::Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Hipstershop.AdResponse.Parser.ParseFrom);
static readonly grpc::Method<global::Hipstershop.AdRequest, global::Hipstershop.AdResponse> __Method_GetAds = new grpc::Method<global::Hipstershop.AdRequest, global::Hipstershop.AdResponse>(
grpc::MethodType.Unary,
__ServiceName,
"GetAds",
__Marshaller_AdRequest,
__Marshaller_AdResponse);
/// <summary>Service descriptor</summary>
public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor
{
get { return global::Hipstershop.DemoReflection.Descriptor.Services[8]; }
}
/// <summary>Base class for server-side implementations of AdService</summary>
public abstract partial class AdServiceBase
{
public virtual global::System.Threading.Tasks.Task<global::Hipstershop.AdResponse> GetAds(global::Hipstershop.AdRequest request, grpc::ServerCallContext context)
{
throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
}
}
/// <summary>Client for AdService</summary>
public partial class AdServiceClient : grpc::ClientBase<AdServiceClient>
{
/// <summary>Creates a new client for AdService</summary>
/// <param name="channel">The channel to use to make remote calls.</param>
public AdServiceClient(grpc::Channel channel) : base(channel)
{
}
/// <summary>Creates a new client for AdService that uses a custom <c>CallInvoker</c>.</summary>
/// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
public AdServiceClient(grpc::CallInvoker callInvoker) : base(callInvoker)
{
}
/// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
protected AdServiceClient() : base()
{
}
/// <summary>Protected constructor to allow creation of configured clients.</summary>
/// <param name="configuration">The client configuration.</param>
protected AdServiceClient(ClientBaseConfiguration configuration) : base(configuration)
{
}
public virtual global::Hipstershop.AdResponse GetAds(global::Hipstershop.AdRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
{
return GetAds(request, new grpc::CallOptions(headers, deadline, cancellationToken));
}
public virtual global::Hipstershop.AdResponse GetAds(global::Hipstershop.AdRequest request, grpc::CallOptions options)
{
return CallInvoker.BlockingUnaryCall(__Method_GetAds, null, options, request);
}
public virtual grpc::AsyncUnaryCall<global::Hipstershop.AdResponse> GetAdsAsync(global::Hipstershop.AdRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
{
return GetAdsAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
}
public virtual grpc::AsyncUnaryCall<global::Hipstershop.AdResponse> GetAdsAsync(global::Hipstershop.AdRequest request, grpc::CallOptions options)
{
return CallInvoker.AsyncUnaryCall(__Method_GetAds, null, options, request);
}
/// <summary>Creates a new instance of client from given <c>ClientBaseConfiguration</c>.</summary>
protected override AdServiceClient NewInstance(ClientBaseConfiguration configuration)
{
return new AdServiceClient(configuration);
}
}
/// <summary>Creates service definition that can be registered with a server</summary>
/// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
public static grpc::ServerServiceDefinition BindService(AdServiceBase serviceImpl)
{
return grpc::ServerServiceDefinition.CreateBuilder()
.AddMethod(__Method_GetAds, serviceImpl.GetAds).Build();
}
}
}
#endregion

View file

@ -24,5 +24,7 @@ namespace cartservice.interfaces
Task EmptyCartAsync(string userId);
Task<Hipstershop.Cart> GetCartAsync(string userId);
bool Ping();
}
}