You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by zh...@apache.org on 2019/07/09 13:59:07 UTC

[servicecomb-pack] 01/02: SCB-1321 Add alpha benchmark tools

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 <zh...@apache.org>
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>