From a76225094363a289ba89d3f9805d49710ce3495e Mon Sep 17 00:00:00 2001 From: Andrew Jenkins Date: Wed, 20 Jun 2018 15:46:08 -0600 Subject: [PATCH 1/3] Productpage: write to stdout for better k8s compatibility --- src/productpage/productpage.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/productpage/productpage.py b/src/productpage/productpage.py index 18ef873..2c63244 100644 --- a/src/productpage/productpage.py +++ b/src/productpage/productpage.py @@ -258,8 +258,6 @@ def write(self, data): details['name'] = details_name = sys.argv[2] reviews['name'] = reviews_name = sys.argv[3] ratings['name'] = ratings_name = sys.argv[4] - sys.stderr = Writer('stderr.log') - sys.stdout = Writer('stdout.log') print "start at port %s" % (p) app.run(host='0.0.0.0', port=p, debug=True, threaded=True) From a5ad33bbf4ac7383ac56ccfd48f36dda542563ec Mon Sep 17 00:00:00 2001 From: Neeraj Poddar Date: Tue, 6 Mar 2018 17:34:43 -0800 Subject: [PATCH 2/3] Add an experiment with multiple services (blue demo) Problem: I just added a webcache to enhance the performance of the ratings service. I want to test it but I don't yet want to deploy it to production, and I don't want to replicate the existing ratings service in my test environment. Besides, whenever I run ratings locally, it's pretty fast - seems to only get slow in production. Solution: Add a new experiment template for this - I'll define an experimental deployment of the services I'm interested in and then use AspenMesh & Istio to route traffic to my deployments. --- Jenkinsfile | 9 +++-- kube/experiment/templates/productpage.yaml | 2 + kube/experiment/templates/reviews.yaml | 43 ++++++++++++++++++++++ src/ratings/ratings.js | 4 +- 4 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 kube/experiment/templates/reviews.yaml diff --git a/Jenkinsfile b/Jenkinsfile index d33eeee..77065c4 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -27,7 +27,7 @@ node('docker') { chmod +x $kubectl ''' - withCredentials([file(credentialsId: 'dummy_k8s_cluster_jenkins_config', variable: 'KUBECONFIG')]) { + withCredentials([file(credentialsId: 'blue_demo_k8s_cluster_jenkins_config', variable: 'KUBECONFIG')]) { sh "./deploy.sh $repository $version $experimentNamespace" } } @@ -37,7 +37,8 @@ node('docker') { } experimentName = "bookinfo-$branchName" baselineNamespace = "default" - service = "productpage" + service1 = "productpage" + service2 = "reviews" stage('Aspen Experiment') { if (env.BRANCH_NAME != 'master') { withEnv(["BINDIR=$tmpDir"]) { @@ -49,8 +50,8 @@ node('docker') { mv $BINDIR'/aspenctl-linux-amd64' $aspenctl chmod +x $aspenctl ''' - withCredentials([string(credentialsId: 'dummy_user_agent_token', variable: 'TOKEN')]) { - sh "./create-experiment.sh $experimentName $baselineNamespace $experimentNamespace $service" + withCredentials([string(credentialsId: 'blue_demo_agent_token', variable: 'TOKEN')]) { + sh "./create-experiment.sh $experimentName $baselineNamespace $experimentNamespace $service1 $service2" } } } else { diff --git a/kube/experiment/templates/productpage.yaml b/kube/experiment/templates/productpage.yaml index 61c2a13..2b59679 100644 --- a/kube/experiment/templates/productpage.yaml +++ b/kube/experiment/templates/productpage.yaml @@ -38,4 +38,6 @@ spec: imagePullPolicy: IfNotPresent ports: - containerPort: 9080 + imagePullSecrets: + - name: aspenmesh-pull-secret --- diff --git a/kube/experiment/templates/reviews.yaml b/kube/experiment/templates/reviews.yaml new file mode 100644 index 0000000..8ac4f9d --- /dev/null +++ b/kube/experiment/templates/reviews.yaml @@ -0,0 +1,43 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: + labels: + istio-injection: enabled +--- +apiVersion: v1 +kind: Service +metadata: + name: reviews + namespace: + labels: + app: reviews +spec: + ports: + - port: 9080 + name: http + selector: + app: reviews +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: reviews + namespace: +spec: + replicas: 1 + template: + metadata: + labels: + app: reviews + version: + spec: + containers: + - name: reviews + image: /examples-bookinfo-reviews-v3: + imagePullPolicy: IfNotPresent + ports: + - containerPort: 9080 + imagePullSecrets: + - name: aspenmesh-pull-secret +--- diff --git a/src/ratings/ratings.js b/src/ratings/ratings.js index 5f50d42..6a365c1 100644 --- a/src/ratings/ratings.js +++ b/src/ratings/ratings.js @@ -122,8 +122,8 @@ function getLocalReviews (productId) { return { id: productId, ratings: { - 'Reviewer1': 5, - 'Reviewer2': 4 + 'Reviewer1': 1, + 'Reviewer2': 2 } } } From 6ad03ac55d75450f1e774c2c43ca06aed16760e5 Mon Sep 17 00:00:00 2001 From: Andrew Jenkins Date: Tue, 13 Mar 2018 18:58:48 -0600 Subject: [PATCH 3/3] Add a 15s cache for ratings Problem: Sometimes the ratings service is slow to return results. Solution: Make a simple cache - if it is less than 15s since we last got the ratings for this productid, just use the ratings from the cache. --- .../application/rest/LibertyRestEndpoint.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/reviews/reviews-application/src/main/java/application/rest/LibertyRestEndpoint.java b/src/reviews/reviews-application/src/main/java/application/rest/LibertyRestEndpoint.java index b66bfce..4defee4 100644 --- a/src/reviews/reviews-application/src/main/java/application/rest/LibertyRestEndpoint.java +++ b/src/reviews/reviews-application/src/main/java/application/rest/LibertyRestEndpoint.java @@ -16,6 +16,10 @@ package application.rest; import java.io.StringReader; +import java.util.Map; +import java.util.HashMap; +import java.util.Date; +import java.util.Collections; import javax.json.Json; import javax.json.JsonObject; import javax.json.JsonObjectBuilder; @@ -43,6 +47,39 @@ public class LibertyRestEndpoint extends Application { private final static Boolean ratings_enabled = Boolean.valueOf(System.getenv("ENABLE_RATINGS")); private final static String star_color = System.getenv("STAR_COLOR") == null ? "black" : System.getenv("STAR_COLOR"); private final static String ratings_service = System.getenv("RATINGS_SERVICE") == null ? "http://ratings:9080/ratings" : System.getenv("RATINGS_SERVICE"); + private final static long refetchAgeMs = 15 * 1000; + + protected class RatingsCacheEntry { + public RatingsCacheEntry(JsonObject _j, long _f) { + this.json = _j; + this.fetched = _f; + } + public JsonObject json; + public long fetched; + } + private static Map ratingsCache = + Collections.synchronizedMap( + new HashMap() + ); + protected JsonObject getRatingFromCache(String productId) { + RatingsCacheEntry e = ratingsCache.get(productId); + if (e == null) { + return null; + } + long now = (new Date()).getTime(); + if (e.fetched + refetchAgeMs < now) { + System.out.println("Expiring " + productId + " from the cache (" + + ratingsCache.size() + ")"); + return null; + } + return e.json; + } + protected void storeRatingToCache(String productId, JsonObject json) { + RatingsCacheEntry e = new RatingsCacheEntry(json, (new Date()).getTime()); + ratingsCache.put(productId, e); + System.out.println("Stored ratings for " + productId + " to cache (" + + ratingsCache.size() + ")"); + } private String getJsonResponse (String productId, int starsReviewer1, int starsReviewer2) { String result = "{"; @@ -85,6 +122,13 @@ private String getJsonResponse (String productId, int starsReviewer1, int starsR private JsonObject getRatings(String productId, Cookie user, String xreq, String xtraceid, String xspanid, String xparentspanid, String xsampled, String xflags, String xotspan){ + // See if we have this result in the cache. + JsonObject cachedRatings = this.getRatingFromCache(productId); + if (cachedRatings != null) { + System.out.println("Returning request for " + productId + " from the cache"); + return cachedRatings; + } + ClientBuilder cb = ClientBuilder.newBuilder(); String timeout = star_color.equals("black") ? "10000" : "2500"; cb.property("com.ibm.ws.jaxrs.client.connection.timeout", timeout); @@ -122,6 +166,10 @@ private JsonObject getRatings(String productId, Cookie user, String xreq, String StringReader stringReader = new StringReader(r.readEntity(String.class)); try (JsonReader jsonReader = Json.createReader(stringReader)) { JsonObject j = jsonReader.readObject(); + + // Store to cache. + this.storeRatingToCache(productId, j); + return j; } }else{