Skip to content

Commit b6ae642

Browse files
committed
Merge pull request #1235 from mattrjacobs/hystrix-examples-async
Add demo that composes async command executions
2 parents ff552f9 + f4db4e8 commit b6ae642

File tree

1 file changed

+199
-0
lines changed

1 file changed

+199
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
/**
2+
* Copyright 2012 Netflix, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.netflix.hystrix.examples.demo;
17+
18+
import java.math.BigDecimal;
19+
import java.net.HttpCookie;
20+
import java.util.Random;
21+
22+
import com.netflix.config.ConfigurationManager;
23+
import com.netflix.hystrix.HystrixCommandKey;
24+
import com.netflix.hystrix.HystrixCommandMetrics;
25+
import com.netflix.hystrix.HystrixCommandMetrics.HealthCounts;
26+
import com.netflix.hystrix.HystrixRequestLog;
27+
import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;
28+
import rx.Observable;
29+
import rx.functions.Action0;
30+
import rx.functions.Action1;
31+
import rx.functions.Func1;
32+
import rx.functions.Func2;
33+
34+
/**
35+
* Executable client that demonstrates the lifecycle, metrics, request log and behavior of HystrixCommands.
36+
*/
37+
public class HystrixCommandAsyncDemo {
38+
39+
// public static void main(String args[]) {
40+
// new HystrixCommandAsyncDemo().startDemo(true);
41+
// }
42+
43+
public HystrixCommandAsyncDemo() {
44+
/*
45+
* Instead of using injected properties we'll set them via Archaius
46+
* so the rest of the code behaves as it would in a real system
47+
* where it picks up properties externally provided.
48+
*/
49+
ConfigurationManager.getConfigInstance().setProperty("hystrix.threadpool.default.coreSize", 8);
50+
ConfigurationManager.getConfigInstance().setProperty("hystrix.command.CreditCardCommand.execution.isolation.thread.timeoutInMilliseconds", 3000);
51+
ConfigurationManager.getConfigInstance().setProperty("hystrix.command.GetUserAccountCommand.execution.isolation.thread.timeoutInMilliseconds", 50);
52+
// set the rolling percentile more granular so we see data change every second rather than every 10 seconds as is the default
53+
ConfigurationManager.getConfigInstance().setProperty("hystrix.command.default.metrics.rollingPercentile.numBuckets", 60);
54+
}
55+
56+
public void startDemo(boolean shouldLog) {
57+
startMetricsMonitor(shouldLog);
58+
while (true) {
59+
Observable<CreditCardAuthorizationResult> o = runSimulatedRequestOnCallerThread(shouldLog);
60+
o.subscribe();
61+
try {
62+
Thread.sleep(400);
63+
} catch (InterruptedException ex) {
64+
ex.printStackTrace();
65+
}
66+
}
67+
}
68+
69+
private final static Random r = new Random();
70+
71+
private Observable<CreditCardAuthorizationResult> runSimulatedRequestOnCallerThread(final boolean shouldLog) {
72+
final HystrixRequestContext context = HystrixRequestContext.initializeContext();
73+
//System.out.println("Map at start : " + map.get() + " : " + Thread.currentThread().getName());
74+
75+
return observeSimulatedUserRequestForOrderConfirmationAndCreditCardPayment()
76+
.doOnUnsubscribe(new Action0() {
77+
@Override
78+
public void call() {
79+
if (shouldLog) {
80+
System.out.println("Request => " + HystrixRequestLog.getCurrentRequest().getExecutedCommandsAsString());
81+
}
82+
context.shutdown();
83+
}
84+
})
85+
.doOnError(new Action1<Throwable>() {
86+
@Override
87+
public void call(Throwable throwable) {
88+
throwable.printStackTrace();
89+
}
90+
});
91+
}
92+
93+
private class Pair<A, B> {
94+
private final A a;
95+
private final B b;
96+
97+
Pair(A a, B b) {
98+
this.a = a;
99+
this.b = b;
100+
}
101+
102+
A a() {
103+
return this.a;
104+
}
105+
106+
B b() {
107+
return this.b;
108+
}
109+
}
110+
111+
public Observable<CreditCardAuthorizationResult> observeSimulatedUserRequestForOrderConfirmationAndCreditCardPayment() {
112+
/* fetch user object with http cookies */
113+
Observable<UserAccount> user = new GetUserAccountCommand(new HttpCookie("mockKey", "mockValueFromHttpRequest")).observe();
114+
115+
/* fetch the payment information (asynchronously) for the user so the credit card payment can proceed */
116+
Observable<PaymentInformation> paymentInformation = user.flatMap(new Func1<UserAccount, Observable<PaymentInformation>>() {
117+
@Override
118+
public Observable<PaymentInformation> call(UserAccount userAccount) {
119+
return new GetPaymentInformationCommand(userAccount).observe();
120+
}
121+
});
122+
/* fetch the order we're processing for the user */
123+
int orderIdFromRequestArgument = 13579;
124+
final Observable<Order> previouslySavedOrder = new GetOrderCommand(orderIdFromRequestArgument).observe();
125+
126+
return Observable.zip(paymentInformation, previouslySavedOrder, new Func2<PaymentInformation, Order, Pair<PaymentInformation, Order>>() {
127+
@Override
128+
public Pair<PaymentInformation, Order> call(PaymentInformation paymentInformation, Order order) {
129+
return new Pair<PaymentInformation, Order>(paymentInformation, order);
130+
}
131+
}).flatMap(new Func1<Pair<PaymentInformation, Order>, Observable<CreditCardAuthorizationResult>>() {
132+
@Override
133+
public Observable<CreditCardAuthorizationResult> call(Pair<PaymentInformation, Order> pair) {
134+
return new CreditCardCommand(pair.b(), pair.a(), new BigDecimal(123.45)).observe();
135+
}
136+
});
137+
}
138+
139+
public void startMetricsMonitor(final boolean shouldLog) {
140+
Thread t = new Thread(new Runnable() {
141+
142+
@Override
143+
public void run() {
144+
while (true) {
145+
/**
146+
* Since this is a simple example and we know the exact HystrixCommandKeys we are interested in
147+
* we will retrieve the HystrixCommandMetrics objects directly.
148+
*
149+
* Typically you would instead retrieve metrics from where they are published which is by default
150+
* done using Servo: https://github.com/Netflix/Hystrix/wiki/Metrics-and-Monitoring
151+
*/
152+
153+
// wait 5 seconds on each loop
154+
try {
155+
Thread.sleep(5000);
156+
} catch (Exception e) {
157+
// ignore
158+
}
159+
160+
// we are using default names so can use class.getSimpleName() to derive the keys
161+
HystrixCommandMetrics creditCardMetrics = HystrixCommandMetrics.getInstance(HystrixCommandKey.Factory.asKey(CreditCardCommand.class.getSimpleName()));
162+
HystrixCommandMetrics orderMetrics = HystrixCommandMetrics.getInstance(HystrixCommandKey.Factory.asKey(GetOrderCommand.class.getSimpleName()));
163+
HystrixCommandMetrics userAccountMetrics = HystrixCommandMetrics.getInstance(HystrixCommandKey.Factory.asKey(GetUserAccountCommand.class.getSimpleName()));
164+
HystrixCommandMetrics paymentInformationMetrics = HystrixCommandMetrics.getInstance(HystrixCommandKey.Factory.asKey(GetPaymentInformationCommand.class.getSimpleName()));
165+
166+
if (shouldLog) {
167+
// print out metrics
168+
StringBuilder out = new StringBuilder();
169+
out.append("\n");
170+
out.append("#####################################################################################").append("\n");
171+
out.append("# CreditCardCommand: " + getStatsStringFromMetrics(creditCardMetrics)).append("\n");
172+
out.append("# GetOrderCommand: " + getStatsStringFromMetrics(orderMetrics)).append("\n");
173+
out.append("# GetUserAccountCommand: " + getStatsStringFromMetrics(userAccountMetrics)).append("\n");
174+
out.append("# GetPaymentInformationCommand: " + getStatsStringFromMetrics(paymentInformationMetrics)).append("\n");
175+
out.append("#####################################################################################").append("\n");
176+
System.out.println(out.toString());
177+
}
178+
}
179+
}
180+
181+
private String getStatsStringFromMetrics(HystrixCommandMetrics metrics) {
182+
StringBuilder m = new StringBuilder();
183+
if (metrics != null) {
184+
HealthCounts health = metrics.getHealthCounts();
185+
m.append("Requests: ").append(health.getTotalRequests()).append(" ");
186+
m.append("Errors: ").append(health.getErrorCount()).append(" (").append(health.getErrorPercentage()).append("%) ");
187+
m.append("Mean: ").append(metrics.getExecutionTimePercentile(50)).append(" ");
188+
m.append("75th: ").append(metrics.getExecutionTimePercentile(75)).append(" ");
189+
m.append("90th: ").append(metrics.getExecutionTimePercentile(90)).append(" ");
190+
m.append("99th: ").append(metrics.getExecutionTimePercentile(99)).append(" ");
191+
}
192+
return m.toString();
193+
}
194+
195+
});
196+
t.setDaemon(true);
197+
t.start();
198+
}
199+
}

0 commit comments

Comments
 (0)