This is an automated email from the ASF dual-hosted git repository. zhanglei pushed a commit to branch SCB-1321 in repository https://gitbox.apache.org/repos/asf/servicecomb-pack.git
commit 7392ae3fac841e3f6af9f0fa554e3ba24fe0129d Author: Lei Zhang <[email protected]> AuthorDate: Tue Jul 9 21:46:29 2019 +0800 SCB-1321 Add alpha benchmark tools --- alpha/alpha-benchmark/pom.xml | 88 +++++++++++ .../pack/alpha/benchmark/Application.java | 97 ++++++++++++ .../pack/alpha/benchmark/BenchmarkMetrics.java | 102 +++++++++++++ .../pack/alpha/benchmark/SagaEventBenchmark.java | 170 +++++++++++++++++++++ .../src/main/resources/application.yaml | 28 ++++ alpha/pom.xml | 1 + 6 files changed, 486 insertions(+) diff --git a/alpha/alpha-benchmark/pom.xml b/alpha/alpha-benchmark/pom.xml new file mode 100644 index 0000000..fcc3277 --- /dev/null +++ b/alpha/alpha-benchmark/pom.xml @@ -0,0 +1,88 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one or more + ~ contributor license agreements. See the NOTICE file distributed with + ~ this work for additional information regarding copyright ownership. + ~ The ASF licenses this file to You 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. + --> + +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>alpha</artifactId> + <groupId>org.apache.servicecomb.pack</groupId> + <version>0.5.0-SNAPSHOT</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>alpha-benchmark</artifactId> + <name>Pack::Alpha::Benchmark</name> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-dependencies</artifactId> + <version>${spring.boot.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + <dependencies> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter</artifactId> + </dependency> + <dependency> + <groupId>org.apache.servicecomb.pack</groupId> + <artifactId>alpha-fsm</artifactId> + <exclusions> + <exclusion> + <artifactId>log4j-slf4j-impl</artifactId> + <groupId>org.apache.logging.log4j</groupId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.apache.servicecomb.pack</groupId> + <artifactId>omega-spring-starter</artifactId> + </dependency> + <dependency> + <groupId>org.apache.servicecomb.pack</groupId> + <artifactId>omega-connector-grpc</artifactId> + </dependency> + <dependency> + <groupId>com.github.seanyinx</groupId> + <artifactId>unit-scaffolding</artifactId> + <scope>compile</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <!-- mixin plugin configurations declared in another pom, + just like importing dependencies managed in another pom --> + <plugin> + <groupId>com.github.odavid.maven.plugins</groupId> + <artifactId>mixin-maven-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + </plugin> + </plugins> + </build> + +</project> diff --git a/alpha/alpha-benchmark/src/main/java/org/apache/servicecomb/pack/alpha/benchmark/Application.java b/alpha/alpha-benchmark/src/main/java/org/apache/servicecomb/pack/alpha/benchmark/Application.java new file mode 100644 index 0000000..0a74910 --- /dev/null +++ b/alpha/alpha-benchmark/src/main/java/org/apache/servicecomb/pack/alpha/benchmark/Application.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.servicecomb.pack.alpha.benchmark; + +import org.apache.servicecomb.pack.omega.transaction.SagaMessageSender; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application implements CommandLineRunner { + + @Autowired + SagaEventBenchmark sagaEventBenchmark; + + @Autowired(required = false) + SagaMessageSender sender; + + @Value("${alpha.cluster.address}") + String address; + + @Value("${n:0}") + int requests; + + @Value("${c:1}") + int concurrency; + + public static void main(String[] args) { + boolean hasAlphaAddress = false; + for(String arg : args){ + if(arg.startsWith("--alpha.cluster.address")){ + hasAlphaAddress = true; + } + } + if(!hasAlphaAddress){ + printHelp(); + System.exit(0); + } + SpringApplication.run(Application.class, args); + } + + @Override + public void run(String... args) { + + try { + if (checkParamter()) { + sagaEventBenchmark.send(requests, concurrency); + } else { + printHelp(); + } + } finally { + sender.onDisconnected(); + System.exit(0); + } + + } + + private boolean checkParamter() { + if (address == null) { + return false; + } else { + if (requests == 0) { + return false; + } else { + return true; + } + } + } + + private static void printHelp() { + System.out.println("\nalpha-benchmark: wrong number of arguments"); + System.out.println( + "Usage: java -jar alpha-benchmark-0.5.0-SNAPSHOT-exec.jar --alpha.cluster.address=0.0.0.0:8080 --n=1000 --c=1"); + System.out.println("Options are:"); + System.out.println( + String.format("%-5s %-15s %-25s", " --n", "requests", "Number of requests to perform")); + System.out.println(String.format("%-5s %-15s %-25s", " --c", "concurrency", + "Number of multiple requests to make at a time")); + } +} diff --git a/alpha/alpha-benchmark/src/main/java/org/apache/servicecomb/pack/alpha/benchmark/BenchmarkMetrics.java b/alpha/alpha-benchmark/src/main/java/org/apache/servicecomb/pack/alpha/benchmark/BenchmarkMetrics.java new file mode 100644 index 0000000..e15a50d --- /dev/null +++ b/alpha/alpha-benchmark/src/main/java/org/apache/servicecomb/pack/alpha/benchmark/BenchmarkMetrics.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.servicecomb.pack.alpha.benchmark; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +public class BenchmarkMetrics { + + private int requests; + private AtomicInteger completeRequests = new AtomicInteger(); + private AtomicInteger failedRequests = new AtomicInteger(); + private int concurrency; + private long timeTaken; + private List<Long> transactionTime = Collections.synchronizedList(new ArrayList()); + + private int beforeProgress; + + public void setRequests(int requests) { + this.requests = requests; + } + + //并发用户数 + public int getConcurrency() { + return concurrency; + } + + public void setConcurrency(int concurrency) { + this.concurrency = concurrency; + } + + public void addTransactionTime(long time) { + transactionTime.add(time); + } + + public long getTimeTaken() { + return timeTaken / 1000; + } + + public void setTimeTaken(long timeTaken) { + this.timeTaken = timeTaken; + } + + public int getCompleteRequests() { + return completeRequests.get(); + } + + public int getFailedRequests() { + return failedRequests.get(); + } + + //总请求数量 + public void completeRequestsIncrement() { + completeRequests.incrementAndGet(); + printProgress(); + } + + //失败的请求数量 + public void failedRequestsIncrement() { + failedRequests.incrementAndGet(); + printProgress(); + } + + //吞吐率 + public long getRequestsPerSecond() { + return this.completeRequests.get() / (timeTaken / 1000); + } + + //用户平均请求等待时间 + public long getTimePerRequest() { + return this.timeTaken / (completeRequests.get() / concurrency); + } + + public List<Long> getTransactionTime() { + return transactionTime; + } + + private void printProgress() { + int progress = (int) ((float) completeRequests.get() / (float) requests * 100); + if (beforeProgress != progress) { + System.out.print("░"); + beforeProgress = progress; + } + } +} diff --git a/alpha/alpha-benchmark/src/main/java/org/apache/servicecomb/pack/alpha/benchmark/SagaEventBenchmark.java b/alpha/alpha-benchmark/src/main/java/org/apache/servicecomb/pack/alpha/benchmark/SagaEventBenchmark.java new file mode 100644 index 0000000..47e62aa --- /dev/null +++ b/alpha/alpha-benchmark/src/main/java/org/apache/servicecomb/pack/alpha/benchmark/SagaEventBenchmark.java @@ -0,0 +1,170 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.servicecomb.pack.alpha.benchmark; + +import java.lang.invoke.MethodHandles; +import java.util.ArrayList; +import java.util.List; +import java.util.OptionalDouble; +import java.util.UUID; +import java.util.concurrent.CountDownLatch; + +import org.apache.servicecomb.pack.common.EventType; +import org.apache.servicecomb.pack.omega.transaction.SagaMessageSender; +import org.apache.servicecomb.pack.omega.transaction.TxEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class SagaEventBenchmark { + + private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + @Autowired(required = false) + SagaMessageSender sender; + + BenchmarkMetrics metrics = new BenchmarkMetrics(); + + public void send(int requests, int concurrency) { + System.out.print("Benchmarking "); + metrics.setRequests(requests); + metrics.setConcurrency(concurrency); + long s = System.currentTimeMillis(); + CountDownLatch begin = new CountDownLatch(1); + CountDownLatch end = new CountDownLatch(concurrency); + begin.countDown(); + for (int i = 0; i < concurrency; i++) { + Execute execute = new Execute(sender, requests / concurrency, begin, end, metrics); + new Thread(execute).start(); + } + try { + end.await(); + long e = System.currentTimeMillis(); + metrics.setTimeTaken(e - s); + System.out.println("\n"); + System.out.println(String.format("%-25s %s", "Concurrency Level", metrics.getConcurrency())); + System.out.println( + String.format("%-25s %s", "Time taken for tests", metrics.getTimeTaken() + " seconds")); + System.out + .println(String.format("%-25s %s", "Complete requests", metrics.getCompleteRequests())); + System.out.println(String.format("%-25s %s", "Failed requests", metrics.getFailedRequests())); + System.out.println(String + .format("%-25s %s", "Requests per second", metrics.getRequestsPerSecond() + " [#/sec]")); + System.out.println( + String.format("%-25s %s", "Time per request", metrics.getTimePerRequest() + " [ms]")); + System.out.println(); + System.out.println("Percentage of the requests served within a certain time (ms)"); + + int size = metrics.getTransactionTime().size(); + int percentage = 50; + for (int i = 0; i <= 5; i++) { + float peek = size * ((float) percentage / 100); + System.out.println(String.format("%-5s %.2f", percentage + "%", getAverage( + metrics.getTransactionTime().subList(0, (int) peek)).getAsDouble())); + percentage = percentage + 10; + } + + } catch (InterruptedException e) { + LOG.error(e.getMessage(), e); + } + LOG.info("OK"); + } + + private OptionalDouble getAverage(List<Long> times) { + try{ + return times.stream().mapToLong(Long::longValue).average(); + }catch (Exception e){ + + throw e; + } + } + + private class Execute implements Runnable { + + SagaMessageSender sender; + CountDownLatch begin; + CountDownLatch end; + int requests; + + public Execute(SagaMessageSender sender, int requests, CountDownLatch begin, + CountDownLatch end, BenchmarkMetrics metrics) { + this.sender = sender; + this.requests = requests; + this.begin = begin; + this.end = end; + } + + @Override + public void run() { + try { + begin.await(); + for (int i = 0; i < requests; i++) { + metrics.completeRequestsIncrement(); + long s = System.currentTimeMillis(); + final String globalTxId = UUID.randomUUID().toString(); + final String localTxId_1 = UUID.randomUUID().toString(); + final String localTxId_2 = UUID.randomUUID().toString(); + final String localTxId_3 = UUID.randomUUID().toString(); + try { + sagaSuccessfulEvents(globalTxId, localTxId_1, localTxId_2, localTxId_3).stream() + .forEach(event -> sender.send(event)); + } catch (Throwable e) { + metrics.failedRequestsIncrement(); + }finally { + long e = System.currentTimeMillis(); + metrics.addTransactionTime(e - s); + } + } + end.countDown(); + } catch (InterruptedException e) { + LOG.error(e.getMessage(), e); + } + } + } + + public List<TxEvent> sagaSuccessfulEvents(String globalTxId, String localTxId_1, + String localTxId_2, String localTxId_3) { + List<TxEvent> sagaEvents = new ArrayList<>(); + sagaEvents.add( + new TxEvent(EventType.SagaStartedEvent, globalTxId, globalTxId, globalTxId, "", 0, null, + 0)); + sagaEvents.add( + new TxEvent(EventType.TxStartedEvent, globalTxId, localTxId_1, globalTxId, "service a", 0, + null, 0)); + sagaEvents.add( + new TxEvent(EventType.TxEndedEvent, globalTxId, localTxId_1, globalTxId, "service a", 0, + null, 0)); + sagaEvents.add( + new TxEvent(EventType.TxStartedEvent, globalTxId, localTxId_2, globalTxId, "service b", 0, + null, 0)); + sagaEvents.add( + new TxEvent(EventType.TxEndedEvent, globalTxId, localTxId_2, globalTxId, "service b", 0, + null, 0)); + sagaEvents.add( + new TxEvent(EventType.TxStartedEvent, globalTxId, localTxId_3, globalTxId, "service c", 0, + null, 0)); + sagaEvents.add( + new TxEvent(EventType.TxEndedEvent, globalTxId, localTxId_3, globalTxId, "service c", 0, + null, 0)); + sagaEvents.add( + new TxEvent(EventType.SagaEndedEvent, globalTxId, globalTxId, globalTxId, "", 0, null, 0)); + return sagaEvents; + } +} diff --git a/alpha/alpha-benchmark/src/main/resources/application.yaml b/alpha/alpha-benchmark/src/main/resources/application.yaml new file mode 100644 index 0000000..2124ca9 --- /dev/null +++ b/alpha/alpha-benchmark/src/main/resources/application.yaml @@ -0,0 +1,28 @@ +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You 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. +## --------------------------------------------------------------------------- +spring: + main: + banner-mode: "off" + application: + name: alpha-benchmark + +alpha: + cluster: + address: localhost:8080 + +omega: + enabled: true \ No newline at end of file diff --git a/alpha/pom.xml b/alpha/pom.xml index 3dfaf13..18ab23e 100644 --- a/alpha/pom.xml +++ b/alpha/pom.xml @@ -33,6 +33,7 @@ <modules> <module>alpha-core</module> <module>alpha-fsm</module> + <module>alpha-benchmark</module> <module>alpha-spring-boot-compatibility</module> <module>alpha-spring-cloud-starter-eureka</module> <module>alpha-spring-cloud-starter-consul</module>
