forked from Netflix/Hystrix
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add demo that composes async command executions
- Loading branch information
Matt Jacobs
committed
Jun 6, 2016
1 parent
ff552f9
commit f4db4e8
Showing
1 changed file
with
199 additions
and
0 deletions.
There are no files selected for viewing
199 changes: 199 additions & 0 deletions
199
...rix-examples/src/main/java/com/netflix/hystrix/examples/demo/HystrixCommandAsyncDemo.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
/** | ||
* Copyright 2012 Netflix, Inc. | ||
* | ||
* 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 com.netflix.hystrix.examples.demo; | ||
|
||
import java.math.BigDecimal; | ||
import java.net.HttpCookie; | ||
import java.util.Random; | ||
|
||
import com.netflix.config.ConfigurationManager; | ||
import com.netflix.hystrix.HystrixCommandKey; | ||
import com.netflix.hystrix.HystrixCommandMetrics; | ||
import com.netflix.hystrix.HystrixCommandMetrics.HealthCounts; | ||
import com.netflix.hystrix.HystrixRequestLog; | ||
import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext; | ||
import rx.Observable; | ||
import rx.functions.Action0; | ||
import rx.functions.Action1; | ||
import rx.functions.Func1; | ||
import rx.functions.Func2; | ||
|
||
/** | ||
* Executable client that demonstrates the lifecycle, metrics, request log and behavior of HystrixCommands. | ||
*/ | ||
public class HystrixCommandAsyncDemo { | ||
|
||
// public static void main(String args[]) { | ||
// new HystrixCommandAsyncDemo().startDemo(true); | ||
// } | ||
|
||
public HystrixCommandAsyncDemo() { | ||
/* | ||
* Instead of using injected properties we'll set them via Archaius | ||
* so the rest of the code behaves as it would in a real system | ||
* where it picks up properties externally provided. | ||
*/ | ||
ConfigurationManager.getConfigInstance().setProperty("hystrix.threadpool.default.coreSize", 8); | ||
ConfigurationManager.getConfigInstance().setProperty("hystrix.command.CreditCardCommand.execution.isolation.thread.timeoutInMilliseconds", 3000); | ||
ConfigurationManager.getConfigInstance().setProperty("hystrix.command.GetUserAccountCommand.execution.isolation.thread.timeoutInMilliseconds", 50); | ||
// set the rolling percentile more granular so we see data change every second rather than every 10 seconds as is the default | ||
ConfigurationManager.getConfigInstance().setProperty("hystrix.command.default.metrics.rollingPercentile.numBuckets", 60); | ||
} | ||
|
||
public void startDemo(boolean shouldLog) { | ||
startMetricsMonitor(shouldLog); | ||
while (true) { | ||
Observable<CreditCardAuthorizationResult> o = runSimulatedRequestOnCallerThread(shouldLog); | ||
o.subscribe(); | ||
try { | ||
Thread.sleep(400); | ||
} catch (InterruptedException ex) { | ||
ex.printStackTrace(); | ||
} | ||
} | ||
} | ||
|
||
private final static Random r = new Random(); | ||
|
||
private Observable<CreditCardAuthorizationResult> runSimulatedRequestOnCallerThread(final boolean shouldLog) { | ||
final HystrixRequestContext context = HystrixRequestContext.initializeContext(); | ||
//System.out.println("Map at start : " + map.get() + " : " + Thread.currentThread().getName()); | ||
|
||
return observeSimulatedUserRequestForOrderConfirmationAndCreditCardPayment() | ||
.doOnUnsubscribe(new Action0() { | ||
@Override | ||
public void call() { | ||
if (shouldLog) { | ||
System.out.println("Request => " + HystrixRequestLog.getCurrentRequest().getExecutedCommandsAsString()); | ||
} | ||
context.shutdown(); | ||
} | ||
}) | ||
.doOnError(new Action1<Throwable>() { | ||
@Override | ||
public void call(Throwable throwable) { | ||
throwable.printStackTrace(); | ||
} | ||
}); | ||
} | ||
|
||
private class Pair<A, B> { | ||
private final A a; | ||
private final B b; | ||
|
||
Pair(A a, B b) { | ||
this.a = a; | ||
this.b = b; | ||
} | ||
|
||
A a() { | ||
return this.a; | ||
} | ||
|
||
B b() { | ||
return this.b; | ||
} | ||
} | ||
|
||
public Observable<CreditCardAuthorizationResult> observeSimulatedUserRequestForOrderConfirmationAndCreditCardPayment() { | ||
/* fetch user object with http cookies */ | ||
Observable<UserAccount> user = new GetUserAccountCommand(new HttpCookie("mockKey", "mockValueFromHttpRequest")).observe(); | ||
|
||
/* fetch the payment information (asynchronously) for the user so the credit card payment can proceed */ | ||
Observable<PaymentInformation> paymentInformation = user.flatMap(new Func1<UserAccount, Observable<PaymentInformation>>() { | ||
@Override | ||
public Observable<PaymentInformation> call(UserAccount userAccount) { | ||
return new GetPaymentInformationCommand(userAccount).observe(); | ||
} | ||
}); | ||
/* fetch the order we're processing for the user */ | ||
int orderIdFromRequestArgument = 13579; | ||
final Observable<Order> previouslySavedOrder = new GetOrderCommand(orderIdFromRequestArgument).observe(); | ||
|
||
return Observable.zip(paymentInformation, previouslySavedOrder, new Func2<PaymentInformation, Order, Pair<PaymentInformation, Order>>() { | ||
@Override | ||
public Pair<PaymentInformation, Order> call(PaymentInformation paymentInformation, Order order) { | ||
return new Pair<PaymentInformation, Order>(paymentInformation, order); | ||
} | ||
}).flatMap(new Func1<Pair<PaymentInformation, Order>, Observable<CreditCardAuthorizationResult>>() { | ||
@Override | ||
public Observable<CreditCardAuthorizationResult> call(Pair<PaymentInformation, Order> pair) { | ||
return new CreditCardCommand(pair.b(), pair.a(), new BigDecimal(123.45)).observe(); | ||
} | ||
}); | ||
} | ||
|
||
public void startMetricsMonitor(final boolean shouldLog) { | ||
Thread t = new Thread(new Runnable() { | ||
|
||
@Override | ||
public void run() { | ||
while (true) { | ||
/** | ||
* Since this is a simple example and we know the exact HystrixCommandKeys we are interested in | ||
* we will retrieve the HystrixCommandMetrics objects directly. | ||
* | ||
* Typically you would instead retrieve metrics from where they are published which is by default | ||
* done using Servo: https://github.com/Netflix/Hystrix/wiki/Metrics-and-Monitoring | ||
*/ | ||
|
||
// wait 5 seconds on each loop | ||
try { | ||
Thread.sleep(5000); | ||
} catch (Exception e) { | ||
// ignore | ||
} | ||
|
||
// we are using default names so can use class.getSimpleName() to derive the keys | ||
HystrixCommandMetrics creditCardMetrics = HystrixCommandMetrics.getInstance(HystrixCommandKey.Factory.asKey(CreditCardCommand.class.getSimpleName())); | ||
HystrixCommandMetrics orderMetrics = HystrixCommandMetrics.getInstance(HystrixCommandKey.Factory.asKey(GetOrderCommand.class.getSimpleName())); | ||
HystrixCommandMetrics userAccountMetrics = HystrixCommandMetrics.getInstance(HystrixCommandKey.Factory.asKey(GetUserAccountCommand.class.getSimpleName())); | ||
HystrixCommandMetrics paymentInformationMetrics = HystrixCommandMetrics.getInstance(HystrixCommandKey.Factory.asKey(GetPaymentInformationCommand.class.getSimpleName())); | ||
|
||
if (shouldLog) { | ||
// print out metrics | ||
StringBuilder out = new StringBuilder(); | ||
out.append("\n"); | ||
out.append("#####################################################################################").append("\n"); | ||
out.append("# CreditCardCommand: " + getStatsStringFromMetrics(creditCardMetrics)).append("\n"); | ||
out.append("# GetOrderCommand: " + getStatsStringFromMetrics(orderMetrics)).append("\n"); | ||
out.append("# GetUserAccountCommand: " + getStatsStringFromMetrics(userAccountMetrics)).append("\n"); | ||
out.append("# GetPaymentInformationCommand: " + getStatsStringFromMetrics(paymentInformationMetrics)).append("\n"); | ||
out.append("#####################################################################################").append("\n"); | ||
System.out.println(out.toString()); | ||
} | ||
} | ||
} | ||
|
||
private String getStatsStringFromMetrics(HystrixCommandMetrics metrics) { | ||
StringBuilder m = new StringBuilder(); | ||
if (metrics != null) { | ||
HealthCounts health = metrics.getHealthCounts(); | ||
m.append("Requests: ").append(health.getTotalRequests()).append(" "); | ||
m.append("Errors: ").append(health.getErrorCount()).append(" (").append(health.getErrorPercentage()).append("%) "); | ||
m.append("Mean: ").append(metrics.getExecutionTimePercentile(50)).append(" "); | ||
m.append("75th: ").append(metrics.getExecutionTimePercentile(75)).append(" "); | ||
m.append("90th: ").append(metrics.getExecutionTimePercentile(90)).append(" "); | ||
m.append("99th: ").append(metrics.getExecutionTimePercentile(99)).append(" "); | ||
} | ||
return m.toString(); | ||
} | ||
|
||
}); | ||
t.setDaemon(true); | ||
t.start(); | ||
} | ||
} |