Skip to content
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
#!/usr/bin/env sh
#
# Copyright 2015 the original author or 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
#
# https://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.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
rootProject.name = 'hipstershop'
/*
* 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.
*/
package hipstershop;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import hipstershop.Demo.Ad;
import hipstershop.Demo.AdRequest;
import hipstershop.Demo.AdResponse;
import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.StatusRuntimeException;
import io.grpc.health.v1.HealthCheckResponse.ServingStatus;
import io.grpc.services.*;
import io.grpc.stub.StreamObserver;
import io.opencensus.common.Duration;
import io.opencensus.contrib.grpc.metrics.RpcViews;
import io.opencensus.exporter.stats.stackdriver.StackdriverStatsConfiguration;
import io.opencensus.exporter.stats.stackdriver.StackdriverStatsExporter;
import io.opencensus.exporter.trace.jaeger.JaegerExporterConfiguration;
import io.opencensus.exporter.trace.jaeger.JaegerTraceExporter;
import io.opencensus.exporter.trace.stackdriver.StackdriverTraceConfiguration;
import io.opencensus.exporter.trace.stackdriver.StackdriverTraceExporter;
import io.opencensus.trace.AttributeValue;
import io.opencensus.trace.Span;
import io.opencensus.trace.Tracer;
import io.opencensus.trace.Tracing;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public final class AdService {
private static final Logger logger = LogManager.getLogger(AdService.class);
private static final Tracer tracer = Tracing.getTracer();
@SuppressWarnings("FieldCanBeLocal")
private static int MAX_ADS_TO_SERVE = 2;
private Server server;
private HealthStatusManager healthMgr;
private static final AdService service = new AdService();
private void start() throws IOException {
int port = Integer.parseInt(System.getenv().getOrDefault("PORT", "9555"));
healthMgr = new HealthStatusManager();
server =
ServerBuilder.forPort(port)
.addService(new AdServiceImpl())
.addService(healthMgr.getHealthService())
.build()
.start();
logger.info("Ad Service started, listening on " + port);
Runtime.getRuntime()
.addShutdownHook(
new Thread(
() -> {
// Use stderr here since the logger may have been reset by its JVM shutdown hook.
System.err.println(
"*** shutting down gRPC ads server since JVM is shutting down");
AdService.this.stop();
System.err.println("*** server shut down");
}));
healthMgr.setStatus("", ServingStatus.SERVING);
}
private void stop() {
if (server != null) {
healthMgr.clearStatus("");
server.shutdown();
}
}
private static class AdServiceImpl extends hipstershop.AdServiceGrpc.AdServiceImplBase {
/**
* Retrieves ads based on context provided in the request {@code AdRequest}.
*
* @param req the request containing context.
* @param responseObserver the stream observer which gets notified with the value of {@code
* AdResponse}
*/
@Override
public void getAds(AdRequest req, StreamObserver<AdResponse> responseObserver) {
AdService service = AdService.getInstance();
Span span = tracer.getCurrentSpan();
try {
span.putAttribute("method", AttributeValue.stringAttributeValue("getAds"));
List<Ad> allAds = new ArrayList<>();
logger.info("received ad request (context_words=" + req.getContextKeysList() + ")");
if (req.getContextKeysCount() > 0) {
span.addAnnotation(
"Constructing Ads using context",
ImmutableMap.of(
"Context Keys",
AttributeValue.stringAttributeValue(req.getContextKeysList().toString()),
"Context Keys length",
AttributeValue.longAttributeValue(req.getContextKeysCount())));
for (int i = 0; i < req.getContextKeysCount(); i++) {
Collection<Ad> ads = service.getAdsByCategory(req.getContextKeys(i));
allAds.addAll(ads);
}
} else {
span.addAnnotation("No Context provided. Constructing random Ads.");
allAds = service.getRandomAds();
}
if (allAds.isEmpty()) {
// Serve random ads.
span.addAnnotation("No Ads found based on context. Constructing random Ads.");
allAds = service.getRandomAds();
}
AdResponse reply = AdResponse.newBuilder().addAllAds(allAds).build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
} catch (StatusRuntimeException e) {
logger.log(Level.WARN, "GetAds Failed with status {}", e.getStatus());
responseObserver.onError(e);
}
}
}
private static final ImmutableListMultimap<String, Ad> adsMap = createAdsMap();
private Collection<Ad> getAdsByCategory(String category) {
return adsMap.get(category);
}
private static final Random random = new Random();
private List<Ad> getRandomAds() {
List<Ad> ads = new ArrayList<>(MAX_ADS_TO_SERVE);
Collection<Ad> allAds = adsMap.values();
for (int i = 0; i < MAX_ADS_TO_SERVE; i++) {
ads.add(Iterables.get(allAds, random.nextInt(allAds.size())));
}
return ads;
}
private static AdService getInstance() {
return service;
}
/** Await termination on the main thread since the grpc library uses daemon threads. */
private void blockUntilShutdown() throws InterruptedException {
if (server != null) {
server.awaitTermination();
}
}
private static ImmutableListMultimap<String, Ad> createAdsMap() {
Ad camera =
Ad.newBuilder()
.setRedirectUrl("/product/2ZYFJ3GM2N")
.setText("Film camera for sale. 50% off.")
.build();
Ad lens =
Ad.newBuilder()
.setRedirectUrl("/product/66VCHSJNUP")
.setText("Vintage camera lens for sale. 20% off.")
.build();
Ad recordPlayer =
Ad.newBuilder()
.setRedirectUrl("/product/0PUK6V6EV0")
.setText("Vintage record player for sale. 30% off.")
.build();
Ad bike =
Ad.newBuilder()
.setRedirectUrl("/product/9SIQT8TOJO")
.setText("City Bike for sale. 10% off.")
.build();
Ad baristaKit =
Ad.newBuilder()
.setRedirectUrl("/product/1YMWWN1N4O")
.setText("Home Barista kitchen kit for sale. Buy one, get second kit for free")
.build();
Ad airPlant =
Ad.newBuilder()
.setRedirectUrl("/product/6E92ZMYYFZ")
.setText("Air plants for sale. Buy two, get third one for free")
.build();
Ad terrarium =
Ad.newBuilder()
.setRedirectUrl("/product/L9ECAV7KIM")
.setText("Terrarium for sale. Buy one, get second one for free")
.build();
return ImmutableListMultimap.<String, Ad>builder()
.putAll("photography", camera, lens)
.putAll("vintage", camera, lens, recordPlayer)
.put("cycling", bike)
.put("cookware", baristaKit)
.putAll("gardening", airPlant, terrarium)
.build();
}
private static void initStats() {
if (System.getenv("DISABLE_STATS") != null) {
logger.info("Stats disabled.");
return;
}
logger.info("Stats enabled");
long sleepTime = 10; /* seconds */
int maxAttempts = 5;
boolean statsExporterRegistered = false;
for (int i = 0; i < maxAttempts; i++) {
try {
if (!statsExporterRegistered) {
StackdriverStatsExporter.createAndRegister(
StackdriverStatsConfiguration.builder()
.setExportInterval(Duration.create(60, 0))
.build());
statsExporterRegistered = true;
}
} catch (Exception e) {
if (i == (maxAttempts - 1)) {
logger.log(
Level.WARN,
"Failed to register Stackdriver Exporter."
+ " Stats data will not reported to Stackdriver. Error message: "
+ e.toString());
} else {
logger.info("Attempt to register Stackdriver Exporter in " + sleepTime + " seconds ");
try {
Thread.sleep(TimeUnit.SECONDS.toMillis(sleepTime));
} catch (Exception se) {
logger.log(Level.WARN, "Exception while sleeping" + se.toString());
}
}
}
}
logger.info("Stats enabled - Stackdriver Exporter initialized.");
}
private static void initTracing() {
if (System.getenv("DISABLE_TRACING") != null) {
logger.info("Tracing disabled.");
return;
}
logger.info("Tracing enabled");
long sleepTime = 10; /* seconds */
int maxAttempts = 5;
boolean traceExporterRegistered = false;
for (int i = 0; i < maxAttempts; i++) {
try {
if (!traceExporterRegistered) {
StackdriverTraceExporter.createAndRegister(
StackdriverTraceConfiguration.builder().build());
traceExporterRegistered = true;
}
} catch (Exception e) {
if (i == (maxAttempts - 1)) {
logger.log(
Level.WARN,
"Failed to register Stackdriver Exporter."
+ " Tracing data will not reported to Stackdriver. Error message: "
+ e.toString());
} else {
logger.info("Attempt to register Stackdriver Exporter in " + sleepTime + " seconds ");
try {
Thread.sleep(TimeUnit.SECONDS.toMillis(sleepTime));
} catch (Exception se) {
logger.log(Level.WARN, "Exception while sleeping" + se.toString());
}
}
}
}
logger.info("Tracing enabled - Stackdriver exporter initialized.");
}
private static void initJaeger() {
String jaegerAddr = System.getenv("JAEGER_SERVICE_ADDR");
if (jaegerAddr != null && !jaegerAddr.isEmpty()) {
String jaegerUrl = String.format("http://%s/api/traces", jaegerAddr);
// Register Jaeger Tracing.
JaegerTraceExporter.createAndRegister(
JaegerExporterConfiguration.builder()
.setThriftEndpoint(jaegerUrl)
.setServiceName("adservice")
.build());
logger.info("Jaeger initialization complete.");
} else {
logger.info("Jaeger initialization disabled.");
}
}
/** Main launches the server from the command line. */
public static void main(String[] args) throws IOException, InterruptedException {
// Registers all RPC views.
/*
[TODO:rghetia] replace registerAllViews with registerAllGrpcViews. registerAllGrpcViews
registers new views using new measures however current grpc version records against old
measures. When new version of grpc (0.19) is release revert back to new. After reverting back
to new the new measure will not provide any tags (like method). This will create some
discrepencies when compared grpc measurements in Go services.
*/
RpcViews.registerAllViews();
new Thread(
() -> {
initStats();
initTracing();
})
.start();
// Register Jaeger
initJaeger();
// Start the RPC server. You shouldn't see any output from gRPC before this.
logger.info("AdService starting.");
final AdService service = AdService.getInstance();
service.start();
service.blockUntilShutdown();
}
}
/*
* 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.
*/
package hipstershop;
import hipstershop.Demo.Ad;
import hipstershop.Demo.AdRequest;
import hipstershop.Demo.AdResponse;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.StatusRuntimeException;
import io.opencensus.common.Duration;
import io.opencensus.common.Scope;
import io.opencensus.contrib.grpc.metrics.RpcViews;
import io.opencensus.contrib.grpc.util.StatusConverter;
import io.opencensus.exporter.stats.stackdriver.StackdriverStatsConfiguration;
import io.opencensus.exporter.stats.stackdriver.StackdriverStatsExporter;
import io.opencensus.exporter.trace.stackdriver.StackdriverTraceConfiguration;
import io.opencensus.exporter.trace.stackdriver.StackdriverTraceExporter;
import io.opencensus.trace.Span;
import io.opencensus.trace.Tracer;
import io.opencensus.trace.Tracing;
import io.opencensus.trace.samplers.Samplers;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/** A simple client that requests ads from the Ads Service. */
public class AdServiceClient {
private static final Logger logger = LogManager.getLogger(AdServiceClient.class);
private static final Tracer tracer = Tracing.getTracer();
private final ManagedChannel channel;
private final hipstershop.AdServiceGrpc.AdServiceBlockingStub blockingStub;
/** Construct client connecting to Ad Service at {@code host:port}. */
private AdServiceClient(String host, int port) {
this(
ManagedChannelBuilder.forAddress(host, port)
// Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid
// needing certificates.
.usePlaintext()
.build());
}
/** Construct client for accessing RouteGuide server using the existing channel. */
private AdServiceClient(ManagedChannel channel) {
this.channel = channel;
blockingStub = hipstershop.AdServiceGrpc.newBlockingStub(channel);
}
private void shutdown() throws InterruptedException {
channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
}
/** Get Ads from Server. */
public void getAds(String contextKey) {
logger.info("Get Ads with context " + contextKey + " ...");
AdRequest request = AdRequest.newBuilder().addContextKeys(contextKey).build();
AdResponse response;
Span span =
tracer
.spanBuilder("AdsClient")
.setRecordEvents(true)
.setSampler(Samplers.alwaysSample())
.startSpan();
try (Scope ignored = tracer.withSpan(span)) {
tracer.getCurrentSpan().addAnnotation("Getting Ads");
response = blockingStub.getAds(request);
tracer.getCurrentSpan().addAnnotation("Received response from Ads Service.");
} catch (StatusRuntimeException e) {
tracer.getCurrentSpan().setStatus(StatusConverter.fromGrpcStatus(e.getStatus()));
logger.log(Level.WARN, "RPC failed: " + e.getStatus());
return;
} finally {
span.end();
}
for (Ad ads : response.getAdsList()) {
logger.info("Ads: " + ads.getText());
}
}
private static int getPortOrDefaultFromArgs(String[] args) {
int portNumber = 9555;
if (2 < args.length) {
try {
portNumber = Integer.parseInt(args[2]);
} catch (NumberFormatException e) {
logger.warn(String.format("Port %s is invalid, use default port %d.", args[2], 9555));
}
}
return portNumber;
}
private static String getStringOrDefaultFromArgs(
String[] args, int index, @Nullable String defaultString) {
String s = defaultString;
if (index < args.length) {
s = args[index];
}
return s;
}
/**
* Ads Service Client main. If provided, the first element of {@code args} is the context key to
* get the ads from the Ads Service
*/
public static void main(String[] args) throws InterruptedException {
// Add final keyword to pass checkStyle.
final String contextKeys = getStringOrDefaultFromArgs(args, 0, "camera");
final String host = getStringOrDefaultFromArgs(args, 1, "localhost");
final int serverPort = getPortOrDefaultFromArgs(args);
// Registers all RPC views.
RpcViews.registerAllGrpcViews();
// Registers Stackdriver exporters.
long sleepTime = 10; /* seconds */
int maxAttempts = 3;
for (int i = 0; i < maxAttempts; i++) {
try {
StackdriverTraceExporter.createAndRegister(StackdriverTraceConfiguration.builder().build());
StackdriverStatsExporter.createAndRegister(
StackdriverStatsConfiguration.builder()
.setExportInterval(Duration.create(15, 0))
.build());
} catch (Exception e) {
if (i == (maxAttempts - 1)) {
logger.log(
Level.WARN,
"Failed to register Stackdriver Exporter."
+ " Tracing and Stats data will not reported to Stackdriver. Error message: "
+ e.toString());
} else {
logger.info("Attempt to register Stackdriver Exporter in " + sleepTime + " seconds");
try {
Thread.sleep(TimeUnit.SECONDS.toMillis(sleepTime));
} catch (Exception se) {
logger.log(Level.WARN, "Exception while sleeping" + e.toString());
}
}
}
}
// Register Prometheus exporters and export metrics to a Prometheus HTTPServer.
// PrometheusStatsCollector.createAndRegister();
AdServiceClient client = new AdServiceClient(host, serverPort);
try {
client.getAds(contextKeys);
} finally {
client.shutdown();
}
logger.info("Exiting AdServiceClient...");
}
}
syntax = "proto3";
package hipstershop;
// -----------------Cart service-----------------
service CartService {
rpc AddItem(AddItemRequest) returns (Empty) {}
rpc GetCart(GetCartRequest) returns (Cart) {}
rpc EmptyCart(EmptyCartRequest) returns (Empty) {}
}
message CartItem {
string product_id = 1;
int32 quantity = 2;
}
message AddItemRequest {
string user_id = 1;
CartItem item = 2;
}
message EmptyCartRequest {
string user_id = 1;
}
message GetCartRequest {
string user_id = 1;
}
message Cart {
string user_id = 1;
repeated CartItem items = 2;
}
message Empty {}
// ---------------Recommendation service----------
service RecommendationService {
rpc ListRecommendations(ListRecommendationsRequest) returns (ListRecommendationsResponse){}
}
message ListRecommendationsRequest {
string user_id = 1;
repeated string product_ids = 2;
}
message ListRecommendationsResponse {
repeated string product_ids = 1;
}
// ---------------Product Catalog----------------
service ProductCatalogService {
rpc ListProducts(Empty) returns (ListProductsResponse) {}
rpc GetProduct(GetProductRequest) returns (Product) {}
rpc SearchProducts(SearchProductsRequest) returns (SearchProductsResponse) {}
}
message Product {
string id = 1;
string name = 2;
string description = 3;
string picture = 4;
Money price_usd = 5;
// Categories such as "vintage" or "gardening" that can be used to look up
// other related products.
repeated string categories = 6;
}
message ListProductsResponse {
repeated Product products = 1;
}
message GetProductRequest {
string id = 1;
}
message SearchProductsRequest {
string query = 1;
}
message SearchProductsResponse {
repeated Product results = 1;
}
// ---------------Shipping Service----------
service ShippingService {
rpc GetQuote(GetQuoteRequest) returns (GetQuoteResponse) {}
rpc ShipOrder(ShipOrderRequest) returns (ShipOrderResponse) {}
}
message GetQuoteRequest {
Address address = 1;
repeated CartItem items = 2;
}
message GetQuoteResponse {
Money cost_usd = 1;
}
message ShipOrderRequest {
Address address = 1;
repeated CartItem items = 2;
}
message ShipOrderResponse {
string tracking_id = 1;
}
message Address {
string street_address = 1;
string city = 2;
string state = 3;
string country = 4;
int32 zip_code = 5;
}
// -----------------Currency service-----------------
service CurrencyService {
rpc GetSupportedCurrencies(Empty) returns (GetSupportedCurrenciesResponse) {}
rpc Convert(CurrencyConversionRequest) returns (Money) {}
}
// Represents an amount of money with its currency type.
message Money {
// The 3-letter currency code defined in ISO 4217.
string currency_code = 1;
// The whole units of the amount.
// For example if `currencyCode` is `"USD"`, then 1 unit is one US dollar.
int64 units = 2;
// Number of nano (10^-9) units of the amount.
// The value must be between -999,999,999 and +999,999,999 inclusive.
// If `units` is positive, `nanos` must be positive or zero.
// If `units` is zero, `nanos` can be positive, zero, or negative.
// If `units` is negative, `nanos` must be negative or zero.
// For example $-1.75 is represented as `units`=-1 and `nanos`=-750,000,000.
int32 nanos = 3;
}
message GetSupportedCurrenciesResponse {
// The 3-letter currency code defined in ISO 4217.
repeated string currency_codes = 1;
}
message CurrencyConversionRequest {
Money from = 1;
// The 3-letter currency code defined in ISO 4217.
string to_code = 2;
}
// -------------Payment service-----------------
service PaymentService {
rpc Charge(ChargeRequest) returns (ChargeResponse) {}
}
message CreditCardInfo {
string credit_card_number = 1;
int32 credit_card_cvv = 2;
int32 credit_card_expiration_year = 3;
int32 credit_card_expiration_month = 4;
}
message ChargeRequest {
Money amount = 1;
CreditCardInfo credit_card = 2;
}
message ChargeResponse {
string transaction_id = 1;
}
// -------------Email service-----------------
service EmailService {
rpc SendOrderConfirmation(SendOrderConfirmationRequest) returns (Empty) {}
}
message OrderItem {
CartItem item = 1;
Money cost = 2;
}
message OrderResult {
string order_id = 1;
string shipping_tracking_id = 2;
Money shipping_cost = 3;
Address shipping_address = 4;
repeated OrderItem items = 5;
}
message SendOrderConfirmationRequest {
string email = 1;
OrderResult order = 2;
}
// -------------Checkout service-----------------
service CheckoutService {
rpc PlaceOrder(PlaceOrderRequest) returns (PlaceOrderResponse) {}
}
message PlaceOrderRequest {
string user_id = 1;
string user_currency = 2;
Address address = 3;
string email = 5;
CreditCardInfo credit_card = 6;
}
message PlaceOrderResponse {
OrderResult order = 1;
}
// ------------Ad service------------------
service AdService {
rpc GetAds(AdRequest) returns (AdResponse) {}
}
message AdRequest {
// List of important key words from the current page describing the context.
repeated string context_keys = 1;
}
message AdResponse {
repeated Ad ads = 1;
}
message Ad {
// url to redirect to when an ad is clicked.
string redirect_url = 1;
// short advertisement text to display.
string text = 2;
}
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="STDOUT" target="SYSTEM_OUT">
<!-- This is a JSON format that can be read by the Stackdriver Logging agent. The trace ID,
span ID, sampling decision, and timestamp are interpreted by Stackdriver. It uses the
special JSON keys that the Stackdriver Logging agent converts to "trace", "spanId",
"traceSampled", and "timestamp" in the Stackdriver LogEntry
(https://cloud.google.com/logging/docs/agent/configuration#special-fields). -->
<JsonLayout compact="true" eventEol="true">
<KeyValuePair key="logging.googleapis.com/trace" value="$${ctx:traceId}"/>
<KeyValuePair key="logging.googleapis.com/spanId" value="$${ctx:spanId}"/>
<KeyValuePair key="logging.googleapis.com/traceSampled" value="$${ctx:traceSampled}"/>
<KeyValuePair key="time" value="$${date:yyyy-MM-dd}T$${date:HH:mm:ss.SSS}Z"/>
</JsonLayout>
</Console>
</Appenders>
<Loggers>
<Logger name="io.grpc.netty" level="INFO"/>
<Logger name="io.netty" level="INFO"/>
<Logger name="sun.net" level="INFO"/>
<Root level="TRACE">
<AppenderRef ref="STDOUT"/>
</Root>
</Loggers>
</Configuration>
// 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.
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using cartservice.interfaces;
using Grpc.Core;
using Hipstershop;
using static Hipstershop.CartService;
namespace cartservice
{
// Cart wrapper to deal with grpc communication
internal class CartServiceImpl : CartServiceBase
{
private ICartStore cartStore;
private readonly static Empty Empty = new Empty();
public CartServiceImpl(ICartStore cartStore)
{
this.cartStore = cartStore;
}
public async override Task<Empty> AddItem(AddItemRequest request, Grpc.Core.ServerCallContext context)
{
await cartStore.AddItemAsync(request.UserId, request.Item.ProductId, request.Item.Quantity);
return Empty;
}
public async override Task<Empty> EmptyCart(EmptyCartRequest request, ServerCallContext context)
{
await cartStore.EmptyCartAsync(request.UserId);
return Empty;
}
public override Task<Hipstershop.Cart> GetCart(GetCartRequest request, ServerCallContext context)
{
return cartStore.GetCartAsync(request.UserId);
}
}
}
\ No newline at end of file
FROM microsoft/dotnet:2.1-sdk-alpine as builder
WORKDIR /app
COPY . .
RUN dotnet restore && \
dotnet build && \
dotnet publish -c release -r linux-musl-x64 -o /cartservice
# cartservice
FROM alpine:3.8
RUN GRPC_HEALTH_PROBE_VERSION=v0.2.0 && \
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++ \
libintl \
icu
WORKDIR /app
COPY --from=builder /cartservice .
ENTRYPOINT ["./cartservice", "start"]
using System;
using System.Threading.Tasks;
using cartservice.interfaces;
using Grpc.Core;
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 override Task<HealthCheckResponse> Check(HealthCheckRequest request, ServerCallContext context){
Console.WriteLine ("Checking CartService Health");
return Task.FromResult(new HealthCheckResponse {
Status = dependency.Ping() ? HealthCheckResponse.Types.ServingStatus.Serving : HealthCheckResponse.Types.ServingStatus.NotServing
});
}
}
}
// 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.
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;
namespace cartservice
{
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
{
[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; }
}
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)
{
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;
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.2.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.12.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" />
<PackageReference Include="StackExchange.Redis" Version="2.0.601" />
</ItemGroup>
<ItemGroup>
<None Update="Dockerfile">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
// 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.
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using System.Linq;
using cartservice.interfaces;
using Hipstershop;
namespace cartservice.cartstore
{
internal class LocalCartStore : ICartStore
{
// Maps between user and their cart
private ConcurrentDictionary<string, Hipstershop.Cart> userCartItems = new ConcurrentDictionary<string, Hipstershop.Cart>();
private readonly Hipstershop.Cart emptyCart = new Hipstershop.Cart();
public Task InitializeAsync()
{
Console.WriteLine("Local Cart Store was initialized");
return Task.CompletedTask;
}
public Task AddItemAsync(string userId, string productId, int quantity)
{
Console.WriteLine($"AddItemAsync called with userId={userId}, productId={productId}, quantity={quantity}");
var newCart = new Hipstershop.Cart
{
UserId = userId,
Items = { new Hipstershop.CartItem { ProductId = productId, Quantity = quantity } }
};
userCartItems.AddOrUpdate(userId, newCart,
(k, exVal) =>
{
// If the item exists, we update its quantity
var existingItem = exVal.Items.SingleOrDefault(item => item.ProductId == productId);
if (existingItem != null)
{
existingItem.Quantity += quantity;
}
else
{
exVal.Items.Add(new Hipstershop.CartItem { ProductId = productId, Quantity = quantity });
}
return exVal;
});
return Task.CompletedTask;
}
public Task EmptyCartAsync(string userId)
{
Console.WriteLine($"EmptyCartAsync called with userId={userId}");
userCartItems[userId] = new Hipstershop.Cart();
return Task.CompletedTask;
}
public Task<Hipstershop.Cart> GetCartAsync(string userId)
{
Console.WriteLine($"GetCartAsync called with userId={userId}");
Hipstershop.Cart cart = null;
if (!userCartItems.TryGetValue(userId, out cart))
{
Console.WriteLine($"No carts for user {userId}");
return Task.FromResult(emptyCart);
}
return Task.FromResult(cart);
}
public bool Ping()
{
return true;
}
}
}
\ No newline at end of file
此差异已折叠。
@REM Copyright 2018 Google LLC
@REM
@REM Licensed under the Apache License, Version 2.0 (the "License");
@REM you may not use this file except in compliance with the License.
@REM You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing, software
@REM distributed under the License is distributed on an "AS IS" BASIS,
@REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@REM See the License for the specific language governing permissions and
@REM limitations under the License.
@rem Generate the C# code for .proto files
setlocal
@rem enter this directory
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\grpc_generated %~dp0\..\..\pb\demo.proto --grpc_out %~dp0\grpc_generated --plugin=protoc-gen-grpc=%TOOLS_PATH%\grpc_csharp_plugin.exe
endlocal
#!/bin/bash -eu
#
# 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.
# Generate the C# code for .proto files
set -e
PROTODIR=../../pb
# enter this directory
CWD="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
protoc --csharp_out=$CWD/grpc_generated -I $PROTODIR $PROTODIR/demo.proto