纪事本 乱码
使用微服务的问题之一是性能。 由于序列化 ,消息传递和反序列化的成本, 延迟可能会更高,这会降低吞吐量 。 尤其是吞吐量不佳是一个问题,因为我们设计可伸缩系统的原因是要提高吞吐量。
在第2部分中,我们了解了如何获取组件并向其中添加传输以使其成为服务。
在线程之间传递数据不是免费的。 该信息需要通过L2 CPU缓存一致性总线传递。 如果您以不受控制的方式执行此操作,而只是让应用程序发现需要将其从一个线程的高速缓存拉到其自己的对象中的对象,则它可能比仅传递需要的数据(串行传输)要慢。 Chronicle Queue通过记录每个事件来提高透明度,使您可以精确地确定一个线程传递给另一个线程的内容。
在一个低延迟的交易系统中,在添加Chronicle Queue之前,使用Chronicle Queue优化线程之间传递的数据后,从读入到写出的平均延迟为35微秒,延迟降至23微秒。 使用Chronicle Queue还显示了以前不明显的问题,并且通过事件重播,可以放心这些问题已得到解决。
使用JMH进行基准测试
JMH (Java Micro-benchmark Harness)是一个出色的工具,可以端到端地测量吞吐量和采样延迟。 我们可以看一个关于示例微服务有什么好处的示例。
我们在Chronicle-Core中创建了自己的微基准测试工具,用于测量跨多个线程运行的异步任务,您需要在这些线程中对各个部分进行计时(第4部分将对此进行介绍)。 现在,我们将研究端到端的延迟。
我们服务的JMH延迟测试
借助JMH,我们可以衡量端到端基准测试的时间安排。 我们必须包括一个生产者来驱动测试,而不是单独安排服务时间。 我们正在研究单线程计时。
我们的JMH基准
@Setup
public void setup() {
String target = OS.TMP;
upQueuePath = new File(target, "ComponentsBenchmark-up-" + System.nanoTime());
upQueue = SingleChronicleQueueBuilder.binary(upQueuePath).build(); (1)
smdWriter = upQueue.createAppender().methodWriter(SidedMarketDataListener.class); (2)
downQueuePath = new File(target, "ComponentsBenchmark-down-" + System.nanoTime());
downQueue = SingleChronicleQueueBuilder.binary(downQueuePath).build(); (3)
MarketDataListener mdWriter = downQueue.createAppender().methodWriter(MarketDataListener.class); (4)
SidedMarketDataCombiner combiner = new SidedMarketDataCombiner(mdWriter); (5)
reader = upQueue.createTailer().methodReader(combiner); (6)
System.out.println("up-q " + upQueuePath);
}
@TearDown
public void tearDown() {
upQueue.close();
downQueue.close();
IOTools.shallowDeleteDirWithFiles(upQueuePath);
IOTools.shallowDeleteDirWithFiles(downQueuePath);
}
@Benchmark
public void benchmarkComponents() {
switch (counter++ & 3) {
case 0:
smdWriter.onSidedPrice(sidedPrice.init("EURUSD", 123456789000L, Side.Sell, 1.1172, 1e6));
break;
case 1:
smdWriter.onSidedPrice(sidedPrice.init("EURUSD", 123456789100L, Side.Buy, 1.1160, 1e6));
break;
case 2:
smdWriter.onSidedPrice(sidedPrice.init("EURUSD", 123456789000L, Side.Sell, 1.1172, 2e6));
break;
case 3:
smdWriter.onSidedPrice(sidedPrice.init("EURUSD", 123456789100L, Side.Buy, 1.1160, 2e6));
break;
}
assertTrue(reader.readOne()); (7)
}
Create an upstream queue.
Create a proxy which writes all methods calls to the upstream queue.
Create a downstream queue.
Create a proxy for the downstream queue.
Create the component which will write all outputs to the downstream queue.
Create a reader for the upstream queue which will call the combiner.
After writing a message to the queue, read it and call the appropriate method in the component.
第一部分设置并拆除测试。 实际的基准测试会注入一条消息,该消息在读取和处理后会触发输出消息。
在JMH中运行测试
Percentiles, us/op:
p(0.0000) = 2.552 us/op
p(50.0000) = 2.796 us/op
p(90.0000) = 5.600 us/op
p(95.0000) = 5.720 us/op
p(99.0000) = 8.496 us/op (1)
p(99.9000) = 15.232 us/op (1)
p(99.9900) = 19.977 us/op (2)
p(99.9990) = 422.475 us/op
p(99.9999) = 438.784 us/op
p(100.0000) = 438.784 us/op
Critical latency threashold for many systems.
Can still be important in some systems.
它在我的开发机器(Ubuntu 15.04,两个E5-2650 v2,128 GB内存)上运行。 为了获得更好的结果,我建议使用最新的Haswell或Skylake和Centos。 该基准测试的确切时间并不重要,因为消息中字段的数量和类型也是一个重要因素。 对我来说特别有趣的是99.9%的延迟(在1000中最差的1个),在此示例中始终低于20毫秒。 这证明了高性能和始终如一的快速延迟。
查看JMH的调用方式。
为了控制JMH的运行方式,使用了以下参数:
int time = Boolean.getBoolean("longTest") ? 30 : 3;
System.out.println("measurementTime: " + time + " secs");
Options opt = new OptionsBuilder()
.include(ComponentsBenchmark.class.getSimpleName())
.warmupIterations(8)
.forks(1)
.mode(Mode.SampleTime) (1)
.measurementTime(TimeValue.seconds(time))
.timeUnit(TimeUnit.MICROSECONDS)
.build();
new Runner(opt).run();
SampleTime
但是,我在分析和调试JMH基准测试时遇到了麻烦,因此我根据测试的启动方式更改了测试的运行方式:
在Flight Recorder中运行和调试
if (Jvm.isFlightRecorder()) {
// -verbose:gc -XX:+UnlockCommercialFeatures -XX:+FlightRecorder
// -XX:StartFlightRecording=dumponexit=true,filename=myrecording.jfr,settings=profile
// -XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepoints (2)
System.out.println("Detected Flight Recorder");
main.setup();
long start = System.currentTimeMillis();
while (start + 60e3 > System.currentTimeMillis()) { (1)
for (int i = 0; i < 1000; i++)
main.benchmarkComponents();
}
main.tearDown();
} else if (Jvm.isDebug()) {
for (int i = 0; i < 10; i++) {
runAll(main, Setup.class);
runAll(main, Benchmark.class);
runAll(main, TearDown.class);
}
Run for 1 minute before shutting down.
Enable profiling between safepoints.
在下一部分
第4部分:我们如何只计时在另一个线程中运行的组件 。 尤其要注意以不同的时间读取,处理和写入每条消息需要花费多长时间。
翻译自: https://www.javacodegeeks.com/2017/03/microservices-chronicle-world-part-3.html
纪事本 乱码