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

cartservice - managed grpc #454

Merged
merged 11 commits into from
Dec 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions kubernetes-manifests/cartservice.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,8 @@ spec:
env:
- name: REDIS_ADDR
value: "redis-cart:6379"
- name: PORT
value: "7070"
- name: LISTEN_ADDR
value: "0.0.0.0"
- name: ASPNETCORE_URLS
value: "http://0.0.0.0:7070"
resources:
requests:
cpu: 200m
Expand Down
6 changes: 2 additions & 4 deletions release/kubernetes-manifests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -427,10 +427,8 @@ spec:
env:
- name: REDIS_ADDR
value: "redis-cart:6379"
- name: PORT
value: "7070"
- name: LISTEN_ADDR
value: "0.0.0.0"
- name: ASPNETCORE_URLS
value: "http://0.0.0.0:7070"
resources:
requests:
cpu: 200m
Expand Down
24 changes: 4 additions & 20 deletions src/cartservice/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.

FROM mcr.microsoft.com/dotnet/sdk:5.0 as builder
FROM mcr.microsoft.com/dotnet/sdk:5.0.100 as builder
WORKDIR /app
COPY . .
RUN dotnet publish -r linux-musl-x64 --self-contained true -c release -o /cartservice

# cartservice
FROM alpine:3.12
RUN dotnet publish -p:PublishSingleFile=true -r linux-musl-x64 --self-contained true -p:PublishTrimmed=True -p:TrimMode=Link -c release -o /cartservice

FROM mcr.microsoft.com/dotnet/runtime-deps:5.0.0-alpine3.12-amd64
RUN GRPC_HEALTH_PROBE_VERSION=v0.3.5 && \
wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \
chmod +x /bin/grpc_health_probe

# Dependencies for runtime
# busybox-extras => telnet
RUN apk add --no-cache \
busybox-extras \
libc6-compat \
libunwind \
libuuid \
libgcc \
libstdc++ \
libssl1.1 \
libintl \
icu \
gcompat
WORKDIR /app
COPY --from=builder /cartservice .
ENTRYPOINT ["./cartservice", "start"]
ENTRYPOINT ["/app/cartservice"]
174 changes: 14 additions & 160 deletions src/cartservice/Program.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2018 Google LLC
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -12,172 +12,26 @@
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using cartservice.cartstore;
using cartservice.interfaces;
using CommandLine;
using Grpc.Core;
using Microsoft.Extensions.Configuration;
using System;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;

namespace cartservice
{
class Program
public class Program
{
const string CART_SERVICE_ADDRESS = "LISTEN_ADDR";
const string REDIS_ADDRESS = "REDIS_ADDR";
const string CART_SERVICE_PORT = "PORT";

[Verb("start", HelpText = "Starts the server listening on provided port")]
class ServerOptions
public static void Main(string[] args)
{
[Option('h', "hostname", HelpText = "The ip on which the server is running. If not provided, LISTEN_ADDR environment variable value will be used. If not defined, localhost is used")]
public string Host { get; set; }

[Option('p', "port", HelpText = "The port on for running the server")]
public int Port { get; set; }

[Option('r', "redis", HelpText = "The ip of redis cache")]
public string Redis { get; set; }
CreateHostBuilder(args).Build().Run();
}

static object StartServer(string host, int port, ICartStore cartStore)
{
// Run the server in a separate thread and make the main thread busy waiting.
// The busy wait is because when we run in a container, we can't use techniques such as waiting on user input (Console.Readline())
Task serverTask = Task.Run(async () =>
{
try
{
await cartStore.InitializeAsync();

Console.WriteLine($"Trying to start a grpc server at {host}:{port}");
Server server = new Server
{
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) }
};

Console.WriteLine($"Cart server is listening at {host}:{port}");
server.Start();

Console.WriteLine("Initialization completed");

// Keep the server up and running
while(true)
{
Thread.Sleep(TimeSpan.FromMinutes(10));
}
}
catch (Exception ex)
// Additional configuration is required to successfully run gRPC on macOS.
// For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
Console.WriteLine(ex);
}
});

return Task.WaitAny(new[] { serverTask });
}

static void Main(string[] args)
{
if (args.Length == 0)
{
Console.WriteLine("Invalid number of arguments supplied");
Environment.Exit(-1);
}

switch (args[0])
{
case "start":
Parser.Default.ParseArguments<ServerOptions>(args).MapResult(
(ServerOptions options) =>
{
Console.WriteLine($"Started as process with id {System.Diagnostics.Process.GetCurrentProcess().Id}");

// Set hostname/ip address
string hostname = options.Host;
if (string.IsNullOrEmpty(hostname))
{
Console.WriteLine($"Reading host address from {CART_SERVICE_ADDRESS} environment variable");
hostname = Environment.GetEnvironmentVariable(CART_SERVICE_ADDRESS);
if (string.IsNullOrEmpty(hostname))
{
Console.WriteLine($"Environment variable {CART_SERVICE_ADDRESS} was not set. Setting the host to 0.0.0.0");
hostname = "0.0.0.0";
}
}

// Set the port
int port = options.Port;
if (options.Port <= 0)
{
Console.WriteLine($"Reading cart service port from {CART_SERVICE_PORT} environment variable");
string portStr = Environment.GetEnvironmentVariable(CART_SERVICE_PORT);
if (string.IsNullOrEmpty(portStr))
{
Console.WriteLine($"{CART_SERVICE_PORT} environment variable was not set. Setting the port to 8080");
port = 8080;
}
else
{
port = int.Parse(portStr);
}
}

// Set redis cache host (hostname+port)
ICartStore cartStore;
string redis = ReadRedisAddress(options.Redis);

// Redis was specified via command line or environment variable
if (!string.IsNullOrEmpty(redis))
{
// If you want to start cart store using local cache in process, you can replace the following line with this:
// cartStore = new LocalCartStore();
cartStore = new RedisCartStore(redis);

return StartServer(hostname, port, cartStore);
}
else
{
Console.WriteLine("Redis cache host(hostname+port) was not specified. Starting a cart service using local store");
Console.WriteLine("If you wanted to use Redis Cache as a backup store, you should provide its address via command line or REDIS_ADDRESS environment variable.");
cartStore = new LocalCartStore();
}

return StartServer(hostname, port, cartStore);
},
errs => 1);
break;
default:
Console.WriteLine("Invalid command");
break;
}
}

private static string ReadRedisAddress(string address)
{
if (!string.IsNullOrEmpty(address))
{
return address;
}

Console.WriteLine($"Reading redis cache address from environment variable {REDIS_ADDRESS}");
string redis = Environment.GetEnvironmentVariable(REDIS_ADDRESS);
if (!string.IsNullOrEmpty(redis))
{
return redis;
}

return null;
}
webBuilder.UseStartup<Startup>();
});
}
}
72 changes: 72 additions & 0 deletions src/cartservice/Startup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Hosting;
using cartservice.cartstore;
using cartservice.services;

namespace cartservice
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}

public IConfiguration Configuration { get; }

// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
string redisAddress = Configuration["REDIS_ADDR"];
ICartStore cartStore = null;
if (!string.IsNullOrEmpty(redisAddress))
{
cartStore = new RedisCartStore(redisAddress);
}
else
{
Console.WriteLine("Redis cache host(hostname+port) was not specified. Starting a cart service using local store");
Console.WriteLine("If you wanted to use Redis Cache as a backup store, you should provide its address via command line or REDIS_ADDRESS environment variable.");
cartStore = new LocalCartStore();
}

// Initialize the redis store
cartStore.InitializeAsync().GetAwaiter().GetResult();
Console.WriteLine("Initialization completed");

services.AddSingleton<ICartStore>(cartStore);

services.AddGrpc();
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}

app.UseRouting();

app.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService<CartService>();
endpoints.MapGrpcService<cartservice.services.HealthCheckService>();

endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
});
});
}
}
}
15 changes: 15 additions & 0 deletions src/cartservice/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"Kestrel": {
"EndpointDefaults": {
"Protocols": "Http2"
}
}
}
12 changes: 2 additions & 10 deletions src/cartservice/cartservice.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,12 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.8.0" />
<PackageReference Include="Google.Protobuf" Version="3.14.0" />
<PackageReference Include="Google.Protobuf.Tools" Version="3.14.0" />
<PackageReference Include="Grpc" Version="2.33.1" />
<PackageReference Include="Grpc.AspNetCore" Version="2.33.1" />
<PackageReference Include="Grpc.HealthCheck" Version="2.33.1" />
<PackageReference Include="grpc.tools" Version="2.33.1" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" />
<PackageReference Include="StackExchange.Redis" Version="2.2.4" />
</ItemGroup>

<ItemGroup>
<None Update="Dockerfile">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<Protobuf Include="protos\Cart.proto" GrpcServices="Server" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@

using System.Threading.Tasks;

namespace cartservice.interfaces
namespace cartservice.cartstore
{
internal interface ICartStore
public interface ICartStore
{
Task InitializeAsync();

Expand Down
2 changes: 0 additions & 2 deletions src/cartservice/cartstore/LocalCartStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
using System.Collections.Concurrent;
using System.Threading.Tasks;
using System.Linq;
using cartservice.interfaces;
using Hipstershop;

namespace cartservice.cartstore
{
Expand Down
Loading