This commit is contained in:
yidao620
2018-02-26 19:05:53 +08:00
commit 4d57c022c6
534 changed files with 96924 additions and 0 deletions

17
springboot-echarts/.gitignore vendored Normal file
View File

@ -0,0 +1,17 @@
# 此为注释– 将被Git 忽略
# /结尾表示是目录,忽略目录和目录下的所有件
# /开头表示根目录,否则是.gitignore的相对目录
# !开头表示反选
.idea/
target/
*.iml
*.ipr
*.iws
*.log
.svn/
.project
rebel.xml
.rebel-remote.xml.*
swagger.json
swagger.adoc

View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2018 Xiong Neng
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,32 @@
## Echarts + WebSocket实现图片生成
大致的过程是这样
对于每一种图表类型开放一个数据请求接口通过POST请求接受图表数据然后push到客户端去
客户端接受到请求后渲染echarts图表并且会将BASE64格式图片数据再次提交给另外一个图片保存接口进行实际的保存。
对于需要生成图片形式的客户端先打开一个浏览器连上服务器另外写一个HTTPClient程序通过POST调用图片保存接口即可。
## 使用案例
使用JMH做微基准测试的时候通常需要可视化显示测试结果这时候可将文本形式的测试结果上传自动生成非常漂亮的性能测试报告图表。
其他想要可视化数据保存图片的场景,都可以这样实现。
## 改进方案
通过配合PhantomJS来实现无浏览器的自动图片生成。
经过测试PhantomJS打开页面后可以连上socket服务器但是10秒后自动关闭了。
尝试采用页面js轮询方式1秒轮询一次有数据的时候就导出图片。
结果导出图片太大了,不知道怎么回事,另外轮询方案始终不是很好。
最后还是老老实实使用websocket方案自己手动打开这个网页吧。<http://localhost:9075/>
## 许可证
Copyright (c) 2018 Xiong Neng
基于 MIT 协议发布: <http://www.opensource.org/licenses/MIT>

218
springboot-echarts/pom.xml Normal file
View File

@ -0,0 +1,218 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.enzhico</groupId>
<artifactId>springboot-echarts</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springboot-echarts</name>
<description>通过接口实现Echarts图片保存</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<netty.version>4.1.19.Final</netty.version>
<thymeleaf.version>3.0.7.RELEASE</thymeleaf.version>
<thymeleaf-layout-dialect.version>2.2.2</thymeleaf-layout-dialect.version>
<jmh.version>1.20</jmh.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.11</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<!-- netty-socketio-->
<dependency>
<groupId>com.corundumstudio.socketio</groupId>
<artifactId>netty-socketio</artifactId>
<version>1.7.13</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-buffer</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-codec</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-codec-http</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-common</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-handler</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-resolver</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-epoll</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-unix-common</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>io.socket</groupId>
<artifactId>socket.io-client</artifactId>
<version>1.0.0</version>
</dependency>
<!-- JMH-->
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>${jmh.version}</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>${jmh.version}</version>
<scope>provided</scope>
</dependency>
<!-- Json libs-->
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
<classifier>jdk15</classifier>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.46</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.4</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.20</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
</project>

72
springboot-echarts/run.sh Normal file
View File

@ -0,0 +1,72 @@
#!/bin/bash
# 项目自动更新脚本
# 先clone相应的分支下来
# git clone ssh://git@120.24.173.142:7999/xxx.git
# 远程调试启动:
# nohup java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -Xms512m -Xmx1024m -jar -Dspring.profiles.active=${profile} ${jarfile} >/dev/null 2>&1 &
function start {
profile="$1"
echo "启动环境profile=${profile}"
jarfile=$(ls target/*.jar)
if [[ "$?" == "0" ]]; then
stop $profile $jarfile
fi
branch=$(git branch |awk '{print $2}')
git pull origin ${branch}
echo "更新完代码开始重新打包"
mvn clean && mvn clean && mvn package -DskipTests=true
if [[ "$?" != "0" ]]; then
echo "编译出错,退出!"
exit 1
fi
echo "nohup java -Xms512m -Xmx1024m -jar -Dspring.profiles.active=${profile} ${jarfile} >/dev/null 2>&1 &"
nohup java -Xms512m -Xmx1024m -jar -Dspring.profiles.active=${profile} ${jarfile} >/dev/null 2>&1 &
echo "启动应用中,请查看日志文件..."
}
function stop {
profile="$1"
jarfile="$2"
ps aux | grep "${jarfile}" | grep "spring.profiles.active=${profile}" | grep -v grep > /dev/null
if [[ "$?" == "0" ]]; then
echo "该应用还在跑,我先停了它"
pid=$(ps aux | grep "${jarfile}" | grep "spring.profiles.active=${profile}" | grep -v grep |awk '{print $2}')
if [[ "$pid" != "" ]]; then
kill -9 $pid
fi
echo "停止应用成功..."
fi
}
if [[ "$1" == "start" ]]; then
if [[ "$#" < 2 ]]; then
echo "请输入正确参数:./epay.sh start {profile}"
exit 1
fi
profile="$2"
if [[ "$profile" != "dev" && "$profile" != "test" && "$profile" != "show" && "$profile" != "production" ]]; then
echo "参数错误请输入正确的profile参数使用方法"
echo "./epay.sh start {profile} ==> 启动应用,{profile}取值dev|test|show|production"
exit 1
fi
start "${profile}"
elif [[ "$1" == "stop" ]]; then
if [[ "$#" < 2 ]]; then
echo "请输入正确参数:./epay.sh stop {profile}"
exit 1
fi
profile="$2"
if [[ "$profile" != "dev" && "$profile" != "test" && "$profile" != "show" && "$profile" != "production" ]]; then
echo "参数错误请输入正确的profile参数使用方法"
echo "./epay.sh stop {profile} ==> 停止应用,{profile}取值dev|test|show|production"
exit 1
fi
jarfile=$(ls target/*.jar)
stop $profile $jarfile
else
echo "参数错误,使用方法:{}参数是必填的,[]参数可选"
echo "./epay.sh start {profile} ==> 启动应用,{profile}取值dev|test|show|production"
echo "./epay.sh stop {profile} ==> 停止应用,{profile}取值dev|test|show|production"
exit 1
fi

View File

@ -0,0 +1,57 @@
package com.enzhico.benchmark.common;
import org.openjdk.jmh.infra.BenchmarkParams;
import org.openjdk.jmh.results.BenchmarkResult;
import org.openjdk.jmh.results.Result;
import org.openjdk.jmh.results.RunResult;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import static com.enzhico.echarts.common.util.ExportPngUtil.generateOption;
import static com.enzhico.echarts.common.util.ExportPngUtil.postOption;
/**
* 将基准测试结果导出图片
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/24
*/
public class ResultExporter {
public static void exportResult(String titleStr, Collection<RunResult> results,
String paramKey, String xunit) throws Exception {
// 几个测试对象
List<String> objects = new ArrayList<>();
// 测试维度输入值n
List<String> dimensions = new ArrayList<>();
// 有几个测试对象,就有几组测试数据,每组测试数据中对应几个维度的结果
List<List<Double>> allData = new ArrayList<>();
List<Double> temp = new ArrayList<>();
for (RunResult runResult : results) {
BenchmarkResult benchmarkResult = runResult.getAggregatedResult();
Result r = benchmarkResult.getPrimaryResult();
BenchmarkParams params = runResult.getParams();
if (!objects.contains(r.getLabel())) {
objects.add(r.getLabel());
if (!temp.isEmpty()) {
allData.add(temp);
temp = new ArrayList<>();
}
}
temp.add(Double.parseDouble(String.format("%.2f", r.getScore())));
// 测试维度
if (!dimensions.contains("n=" + params.getParam(paramKey))) {
dimensions.add("n=" + params.getParam(paramKey));
}
}
// 最后一组测试数据别忘记加进去了
allData.add(temp);
String optionStr = generateOption(titleStr, objects, dimensions, allData, xunit);
// POST到接口上
postOption(optionStr, "http://localhost:9075/api/v1/data");
}
}

View File

@ -0,0 +1,43 @@
package com.enzhico.benchmark.first;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.TimeUnit;
/**
* FirstBenchmark
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/24
*/
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Thread)
public class FirstBenchmark {
@Benchmark
public int sleepAWhile() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// ignore
}
return 0;
}
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(FirstBenchmark.class.getSimpleName())
.forks(1)
.warmupIterations(5)
.measurementIterations(5)
.build();
new Runner(opt).run();
}
}

View File

@ -0,0 +1,84 @@
package com.enzhico.benchmark.json;
import com.enzhico.benchmark.common.ResultExporter;
import com.enzhico.benchmark.json.model.FullName;
import com.enzhico.benchmark.json.model.Person;
import com.enzhico.benchmark.json.util.FastJsonUtil;
import com.enzhico.benchmark.json.util.GsonUtil;
import com.enzhico.benchmark.json.util.JacksonUtil;
import com.enzhico.benchmark.json.util.JsonLibUtil;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.results.RunResult;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* Json反序列化基准测试
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/24
*/
@BenchmarkMode(Mode.SingleShotTime)
@OutputTimeUnit(TimeUnit.SECONDS)
@State(Scope.Benchmark)
public class JsonDeserializeBenchmark {
/**
* 反序列化次数参数
*/
@Param({"1000", "10000", "100000"})
private int count;
private String jsonStr;
public static void main(String[] args) throws Exception {
Options opt = new OptionsBuilder()
.include(JsonDeserializeBenchmark.class.getSimpleName())
.forks(1)
.warmupIterations(0)
.build();
Collection<RunResult> results = new Runner(opt).run();
ResultExporter.exportResult("JSON反序列化性能", results, "count", "");
}
@Benchmark
public void JsonLib() {
for (int i = 0; i < count; i++) {
JsonLibUtil.json2Bean(jsonStr, Person.class);
}
}
@Benchmark
public void Gson() {
for (int i = 0; i < count; i++) {
GsonUtil.json2Bean(jsonStr, Person.class);
}
}
@Benchmark
public void FastJson() {
for (int i = 0; i < count; i++) {
FastJsonUtil.json2Bean(jsonStr, Person.class);
}
}
@Benchmark
public void Jackson() {
for (int i = 0; i < count; i++) {
JacksonUtil.json2Bean(jsonStr, Person.class);
}
}
@Setup
public void prepare() {
jsonStr="{\"name\":\"邵同学\",\"fullName\":{\"firstName\":\"zjj_first\",\"middleName\":\"zjj_middle\",\"lastName\":\"zjj_last\"},\"age\":24,\"birthday\":null,\"hobbies\":[\"篮球\",\"游泳\",\"coding\"],\"clothes\":{\"shoes\":\"安踏\",\"trousers\":\"adidas\",\"coat\":\"Nike\"},\"friends\":[{\"name\":\"小明\",\"fullName\":{\"firstName\":\"xxx_first\",\"middleName\":\"xxx_middle\",\"lastName\":\"xxx_last\"},\"age\":24,\"birthday\":null,\"hobbies\":[\"篮球\",\"游泳\",\"coding\"],\"clothes\":{\"shoes\":\"安踏\",\"trousers\":\"adidas\",\"coat\":\"Nike\"},\"friends\":null},{\"name\":\"Tony\",\"fullName\":{\"firstName\":\"xxx_first\",\"middleName\":\"xxx_middle\",\"lastName\":\"xxx_last\"},\"age\":24,\"birthday\":null,\"hobbies\":[\"篮球\",\"游泳\",\"coding\"],\"clothes\":{\"shoes\":\"安踏\",\"trousers\":\"adidas\",\"coat\":\"Nike\"},\"friends\":null},{\"name\":\"陈小二\",\"fullName\":{\"firstName\":\"xxx_first\",\"middleName\":\"xxx_middle\",\"lastName\":\"xxx_last\"},\"age\":24,\"birthday\":null,\"hobbies\":[\"篮球\",\"游泳\",\"coding\"],\"clothes\":{\"shoes\":\"安踏\",\"trousers\":\"adidas\",\"coat\":\"Nike\"},\"friends\":null}]}";
}
@TearDown
public void shutdown() {
}
}

View File

@ -0,0 +1,116 @@
package com.enzhico.benchmark.json;
import com.enzhico.benchmark.common.ResultExporter;
import com.enzhico.benchmark.json.model.FullName;
import com.enzhico.benchmark.json.model.Person;
import com.enzhico.benchmark.json.util.FastJsonUtil;
import com.enzhico.benchmark.json.util.GsonUtil;
import com.enzhico.benchmark.json.util.JacksonUtil;
import com.enzhico.benchmark.json.util.JsonLibUtil;
import com.enzhico.benchmark.sum.calc.impl.MultithreadCalculator;
import com.enzhico.benchmark.sum.calc.impl.SinglethreadCalculator;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.BenchmarkParams;
import org.openjdk.jmh.results.BenchmarkResult;
import org.openjdk.jmh.results.Result;
import org.openjdk.jmh.results.RunResult;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
import static com.enzhico.echarts.common.util.ExportPngUtil.generateOption;
import static com.enzhico.echarts.common.util.ExportPngUtil.postOption;
/**
* Json序列化基准测试
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/24
*/
@BenchmarkMode(Mode.SingleShotTime)
@OutputTimeUnit(TimeUnit.SECONDS)
@State(Scope.Benchmark)
public class JsonSerializeBenchmark {
/**
* 序列化次数参数
*/
@Param({"1000", "10000", "100000"})
private int count;
private Person p;
public static void main(String[] args) throws Exception {
Options opt = new OptionsBuilder()
.include(JsonSerializeBenchmark.class.getSimpleName())
.forks(1)
.warmupIterations(0)
.build();
Collection<RunResult> results = new Runner(opt).run();
ResultExporter.exportResult("JSON序列化性能", results, "count", "");
}
@Benchmark
public void JsonLib() {
for (int i = 0; i < count; i++) {
JsonLibUtil.bean2Json(p);
}
}
@Benchmark
public void Gson() {
for (int i = 0; i < count; i++) {
GsonUtil.bean2Json(p);
}
}
@Benchmark
public void FastJson() {
for (int i = 0; i < count; i++) {
FastJsonUtil.bean2Json(p);
}
}
@Benchmark
public void Jackson() {
for (int i = 0; i < count; i++) {
JacksonUtil.bean2Json(p);
}
}
@Setup
public void prepare() {
List<Person> friends=new ArrayList<Person>();
friends.add(createAPerson("小明",null));
friends.add(createAPerson("Tony",null));
friends.add(createAPerson("陈小二",null));
p=createAPerson("邵同学",friends);
}
@TearDown
public void shutdown() {
}
private Person createAPerson(String name,List<Person> friends) {
Person newPerson=new Person();
newPerson.setName(name);
newPerson.setFullName(new FullName("zjj_first", "zjj_middle", "zjj_last"));
newPerson.setAge(24);
List<String> hobbies=new ArrayList<String>();
hobbies.add("篮球");
hobbies.add("游泳");
hobbies.add("coding");
newPerson.setHobbies(hobbies);
Map<String,String> clothes=new HashMap<String, String>();
clothes.put("coat", "Nike");
clothes.put("trousers", "adidas");
clothes.put("shoes", "安踏");
newPerson.setClothes(clothes);
newPerson.setFriends(friends);
return newPerson;
}
}

View File

@ -0,0 +1,53 @@
package com.enzhico.benchmark.json.model;
/**
* FullName
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/24
*/
public class FullName {
private String firstName;
private String middleName;
private String lastName;
public FullName() {
}
public FullName(String firstName, String middleName, String lastName) {
this.firstName = firstName;
this.middleName = middleName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getMiddleName() {
return middleName;
}
public void setMiddleName(String middleName) {
this.middleName = middleName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@Override
public String toString() {
return "[firstName=" + firstName + ", middleName="
+ middleName + ", lastName=" + lastName + "]";
}
}

View File

@ -0,0 +1,93 @@
package com.enzhico.benchmark.json.model;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* Person
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/24
*/
public class Person {
private String name;
private FullName fullName;
private int age;
private Date birthday;
private List<String> hobbies;
private Map<String, String> clothes;
private List<Person> friends;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public FullName getFullName() {
return fullName;
}
public void setFullName(FullName fullName) {
this.fullName = fullName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public List<String> getHobbies() {
return hobbies;
}
public void setHobbies(List<String> hobbies) {
this.hobbies = hobbies;
}
public Map<String, String> getClothes() {
return clothes;
}
public void setClothes(Map<String, String> clothes) {
this.clothes = clothes;
}
public List<Person> getFriends() {
return friends;
}
public void setFriends(List<Person> friends) {
this.friends = friends;
}
@Override
public String toString() {
StringBuilder str = new StringBuilder("Person [name=" + name + ", fullName=" + fullName + ", age="
+ age + ", birthday=" + birthday + ", hobbies=" + hobbies
+ ", clothes=" + clothes + "]\n");
if (friends != null) {
str.append("Friends:\n");
for (Person f : friends) {
str.append("\t").append(f);
}
}
return str.toString();
}
}

View File

@ -0,0 +1,20 @@
package com.enzhico.benchmark.json.util;
import com.alibaba.fastjson.JSON;
/**
* FastJsonUtil
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/24
*/
public class FastJsonUtil {
public static String bean2Json(Object obj) {
return JSON.toJSONString(obj);
}
public static <T> T json2Bean(String jsonStr, Class<T> objClass) {
return JSON.parseObject(jsonStr, objClass);
}
}

View File

@ -0,0 +1,33 @@
package com.enzhico.benchmark.json.util;
/**
* GsonUtil
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/24
*/
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
public class GsonUtil {
private static Gson gson = new GsonBuilder().create();
public static String bean2Json(Object obj) {
return gson.toJson(obj);
}
public static <T> T json2Bean(String jsonStr, Class<T> objClass) {
return gson.fromJson(jsonStr, objClass);
}
public static String jsonFormatter(String uglyJsonStr) {
Gson gson = new GsonBuilder().setPrettyPrinting().create();
JsonParser jp = new JsonParser();
JsonElement je = jp.parse(uglyJsonStr);
return gson.toJson(je);
}
}

View File

@ -0,0 +1,35 @@
package com.enzhico.benchmark.json.util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
/**
* JacksonUtil
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/24
*/
public class JacksonUtil {
private static ObjectMapper mapper = new ObjectMapper();
public static String bean2Json(Object obj) {
try {
return mapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
e.printStackTrace();
return null;
}
}
public static <T> T json2Bean(String jsonStr, Class<T> objClass) {
try {
return mapper.readValue(jsonStr, objClass);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}

View File

@ -0,0 +1,25 @@
package com.enzhico.benchmark.json.util;
import net.sf.json.JSONObject;
/**
* JsonLibUtil
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/24
*/
public class JsonLibUtil {
public static String bean2Json(Object obj) {
JSONObject jsonObject = JSONObject.fromObject(obj);
return jsonObject.toString();
}
@SuppressWarnings("unchecked")
public static <T> T json2Bean(String jsonStr, Class<T> objClass) {
return (T) JSONObject.toBean(JSONObject.fromObject(jsonStr), objClass);
}
}

View File

@ -0,0 +1,55 @@
package com.enzhico.benchmark.string;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.TimeUnit;
/**
* 比较字符串直接相加和StringBuilder的效率
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/25
*/
@BenchmarkMode(Mode.Throughput)
@Warmup(iterations = 3)
@Measurement(iterations = 10, time = 5, timeUnit = TimeUnit.SECONDS)
@Threads(8)
@Fork(2)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class StringBuilderBenchmark {
@Benchmark
public void testStringAdd() {
String a = "";
for (int i = 0; i < 10; i++) {
a += i;
}
print(a);
}
@Benchmark
public void testStringBuilderAdd() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10; i++) {
sb.append(i);
}
print(sb.toString());
}
private void print(String a) {
}
public static void main(String[] args) throws RunnerException {
Options options = new OptionsBuilder()
.include(StringBuilderBenchmark.class.getSimpleName())
.output("E:/Benchmark.log")
.build();
new Runner(options).run();
}
}

View File

@ -0,0 +1,68 @@
package com.enzhico.benchmark.sum;
import com.enzhico.benchmark.common.ResultExporter;
import com.enzhico.benchmark.sum.calc.Calculator;
import com.enzhico.benchmark.sum.calc.impl.MultithreadCalculator;
import com.enzhico.benchmark.sum.calc.impl.SinglethreadCalculator;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.results.RunResult;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
/**
* SecondBenchmark
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/24
*/
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Benchmark)
public class SecondBenchmark {
@Param({"10000", "100000", "1000000"})
private int length;
private int[] numbers;
private Calculator singleThreadCalc;
private Calculator multiThreadCalc;
public static void main(String[] args) throws Exception {
Options opt = new OptionsBuilder()
.include(SecondBenchmark.class.getSimpleName())
.forks(1)
.warmupIterations(5)
.measurementIterations(2)
.build();
Collection<RunResult> results = new Runner(opt).run();
ResultExporter.exportResult("单线程与多线程求和性能", results, "length", "微秒");
}
@Benchmark
public long singleThreadBench() {
return singleThreadCalc.sum(numbers);
}
@Benchmark
public long multiThreadBench() {
return multiThreadCalc.sum(numbers);
}
@Setup
public void prepare() {
numbers = IntStream.rangeClosed(1, length).toArray();
singleThreadCalc = new SinglethreadCalculator();
multiThreadCalc = new MultithreadCalculator(Runtime.getRuntime().availableProcessors());
}
@TearDown
public void shutdown() {
singleThreadCalc.shutdown();
multiThreadCalc.shutdown();
}
}

View File

@ -0,0 +1,23 @@
package com.enzhico.benchmark.sum.calc;
/**
* Calculator
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/24
*/
public interface Calculator {
/**
* calculate sum of an integer array
*
* @param numbers
* @return
*/
public long sum(int[] numbers);
/**
* shutdown pool or reclaim any related resources
*/
public void shutdown();
}

View File

@ -0,0 +1,81 @@
package com.enzhico.benchmark.sum.calc.impl;
import com.enzhico.benchmark.sum.calc.Calculator;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* MultithreadCalculator
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/24
*/
public class MultithreadCalculator implements Calculator {
private final int nThreads;
private final ExecutorService pool;
public MultithreadCalculator(int nThreads) {
this.nThreads = nThreads;
this.pool = Executors.newFixedThreadPool(nThreads);
}
private class SumTask implements Callable<Long> {
private int[] numbers;
private int from;
private int to;
public SumTask(int[] numbers, int from, int to) {
this.numbers = numbers;
this.from = from;
this.to = to;
}
public Long call() throws Exception {
long total = 0L;
for (int i = from; i < to; i++) {
total += numbers[i];
}
return total;
}
}
public long sum(int[] numbers) {
int chunk = numbers.length / nThreads;
int from, to;
List<SumTask> tasks = new ArrayList<SumTask>();
for (int i = 1; i <= nThreads; i++) {
if (i == nThreads) {
from = (i - 1) * chunk;
to = numbers.length;
} else {
from = (i - 1) * chunk;
to = i * chunk;
}
tasks.add(new SumTask(numbers, from, to));
}
try {
List<Future<Long>> futures = pool.invokeAll(tasks);
long total = 0L;
for (Future<Long> future : futures) {
total += future.get();
}
return total;
} catch (Exception e) {
// ignore
return 0;
}
}
@Override
public void shutdown() {
pool.shutdown();
}
}

View File

@ -0,0 +1,25 @@
package com.enzhico.benchmark.sum.calc.impl;
import com.enzhico.benchmark.sum.calc.Calculator;
/**
* SinglethreadCalculator
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/24
*/
public class SinglethreadCalculator implements Calculator {
public long sum(int[] numbers) {
long total = 0L;
for (int i : numbers) {
total += i;
}
return total;
}
@Override
public void shutdown() {
// nothing to do
}
}

View File

@ -0,0 +1,11 @@
package com.enzhico.echarts;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@ -0,0 +1,146 @@
package com.enzhico.echarts.api;
import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.annotation.OnEvent;
import com.enzhico.echarts.api.model.BaseResponse;
import com.enzhico.echarts.api.model.EchartsData;
import com.enzhico.echarts.api.model.PicRequest;
import com.enzhico.echarts.service.ApiService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.Charset;
/**
* 对外API接口类
*/
@RestController
@RequestMapping(value = "/api/v1")
public class PublicController {
@Resource
private ApiService apiService;
private static final Logger _logger = LoggerFactory.getLogger(PublicController.class);
/**
* 数据上传接口body直接接受字符串不要转
* @return 结果
*
{
"title": {
"text": "天气预报"
},
"tooltip": {
"trigger": "axis",
"axisPointer": {
"type": "shadow"
}
},
"legend": {
"data": ["City Alpha", "City Beta", "City Gamma"]
},
"grid": {
"left": 100
},
"toolbox": {
"show": false,
"feature": {
"saveAsImage": {}
}
},
"xAxis": {
"type": "value",
"name": "Days"
},
"yAxis": {
"type": "category",
"inverse": false,
"data": ["Sunny", "Cloudy", "Showers"],
"axisLabel": {
"margin": 20
}
},
"series": [
{
"name": "City Alpha",
"type": "bar",
"label": {
"normal": {
"show": true,
"textBorderWidth": 2
}
},
"data": [165, 170, 30]
},
{
"name": "City Beta",
"type": "bar",
"label": {
"normal": {
"show": true,
"textBorderWidth": 2
}
},
"data": [150, 105, 110]
},
{
"name": "City Gamma",
"type": "bar",
"label": {
"normal": {
"show": true,
"textBorderWidth": 2
}
},
"data": [220, 82, 63]
}
]
}
*/
@RequestMapping(value = "/data", method = RequestMethod.POST)
public ResponseEntity<BaseResponse> doJoin(HttpServletRequest request) throws Exception {
_logger.info("数据上传消息push接口 start....");
String jsonBody = IOUtils.toString(request.getInputStream(), Charset.forName("UTF-8"));
EchartsData echartsData = new EchartsData("", jsonBody);
String jsonString = new ObjectMapper().writeValueAsString(echartsData);
apiService.pushMsg("notify", jsonString);
BaseResponse result = new BaseResponse<>(true, "数据上传消息push成功", null);
return new ResponseEntity<>(result, HttpStatus.OK);
}
// @RequestMapping(value = "/ask", method = RequestMethod.POST)
// public ResponseEntity<BaseResponse> doAsk() {
// ResponseEntity<BaseResponse> result;
// String jsonString = apiService.popJson();
// if (jsonString == null) {
// result = new ResponseEntity<>(new BaseResponse<>(false, "轮询没有查到数据", null), HttpStatus.OK);
// } else {
// _logger.info("轮询后查询到数据");
// result = new ResponseEntity<>(new BaseResponse<>(true, "轮询后查询到数据", jsonString), HttpStatus.OK);
// }
// return result;
// }
//
// /**
// * 保存客户端传来的图片数据
// *
// * @param picInfo 图片BASE64
// */
// @RequestMapping(value = "/savePic", method = RequestMethod.POST)
// public ResponseEntity<BaseResponse> onSavePic(@RequestParam("picInfo") String picInfo) {
// _logger.info("保存客户端传来的图片数据 start");
// String r = apiService.saveBase64Pic(picInfo);
// _logger.info("保存客户端传来的图片 = {}", r);
// return new ResponseEntity<>(new BaseResponse<>(true, "图片保存成功", null), HttpStatus.OK);
// }
}

View File

@ -0,0 +1,60 @@
package com.enzhico.echarts.api.model;
/**
* API接口的基础返回类
*
* @author XiongNeng
* @version 1.0
* @since 2018/1/7
*/
public class BaseResponse<T> {
/**
* 是否成功
*/
private boolean success;
/**
* 说明
*/
private String msg;
/**
* 返回数据
*/
private T data;
public BaseResponse() {
}
public BaseResponse(boolean success, String msg, T data) {
this.success = success;
this.msg = msg;
this.data = data;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}

View File

@ -0,0 +1,43 @@
package com.enzhico.echarts.api.model;
/**
* EchartsData
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/8
*/
public class EchartsData {
/**
* 图表类型
*/
private String msgType;
/**
* 图表数据
*/
private String option;
public EchartsData() {
}
public EchartsData(String msgType, String option) {
this.msgType = msgType;
this.option = option;
}
public String getMsgType() {
return msgType;
}
public void setMsgType(String msgType) {
this.msgType = msgType;
}
public String getOption() {
return option;
}
public void setOption(String option) {
this.option = option;
}
}

View File

@ -0,0 +1,23 @@
package com.enzhico.echarts.api.model;
/**
* PicRequest
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/8
*/
public class PicRequest {
/**
* Base64格式的图片
*/
private String picInfo;
public String getPicInfo() {
return picInfo;
}
public void setPicInfo(String picInfo) {
this.picInfo = picInfo;
}
}

View File

@ -0,0 +1,27 @@
package com.enzhico.echarts.api.model.jmh;
/**
* AxisPointer
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/9
*/
public class AxisPointer {
public AxisPointer() {
}
public AxisPointer(String type) {
this.type = type;
}
private String type;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}

View File

@ -0,0 +1,27 @@
package com.enzhico.echarts.api.model.jmh;
/**
* Feature
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/9
*/
public class Feature {
private SaveAsImage saveAsImage;
public Feature() {
}
public Feature(SaveAsImage saveAsImage) {
this.saveAsImage = saveAsImage;
}
public SaveAsImage getSaveAsImage() {
return saveAsImage;
}
public void setSaveAsImage(SaveAsImage saveAsImage) {
this.saveAsImage = saveAsImage;
}
}

View File

@ -0,0 +1,29 @@
package com.enzhico.echarts.api.model.jmh;
/**
* Grid
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/9
*/
public class Grid {
private Integer left;
public Grid(Integer left) {
this.left = left;
}
public Grid() {
}
public Integer getLeft() {
return left;
}
public void setLeft(Integer left) {
this.left = left;
}
}

View File

@ -0,0 +1,27 @@
package com.enzhico.echarts.api.model.jmh;
/**
* Label
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/9
*/
public class Label {
private Normal normal;
public Label() {
}
public Label(Normal normal) {
this.normal = normal;
}
public Normal getNormal() {
return normal;
}
public void setNormal(Normal normal) {
this.normal = normal;
}
}

View File

@ -0,0 +1,29 @@
package com.enzhico.echarts.api.model.jmh;
import java.util.List;
/**
* Legend
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/9
*/
public class Legend {
private List<String> data;
public Legend() {
}
public Legend(List<String> data) {
this.data = data;
}
public List<String> getData() {
return data;
}
public void setData(List<String> data) {
this.data = data;
}
}

View File

@ -0,0 +1,37 @@
package com.enzhico.echarts.api.model.jmh;
/**
* Normal
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/9
*/
public class Normal {
private boolean show;
private Integer textBorderWidth;
public Normal() {
}
public Normal(boolean show, Integer textBorderWidth) {
this.show = show;
this.textBorderWidth = textBorderWidth;
}
public boolean isShow() {
return show;
}
public void setShow(boolean show) {
this.show = show;
}
public Integer getTextBorderWidth() {
return textBorderWidth;
}
public void setTextBorderWidth(Integer textBorderWidth) {
this.textBorderWidth = textBorderWidth;
}
}

View File

@ -0,0 +1,85 @@
package com.enzhico.echarts.api.model.jmh;
import java.util.List;
/**
* 汇总Option
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/9
*/
public class Option {
private Title title;
private Tooltip tooltip;
private Legend legend;
private Grid grid;
private Toolbox toolbox;
private XAxis xAxis;
private YAxis yAxis;
private List<Serie> series;
public Title getTitle() {
return title;
}
public void setTitle(Title title) {
this.title = title;
}
public Tooltip getTooltip() {
return tooltip;
}
public void setTooltip(Tooltip tooltip) {
this.tooltip = tooltip;
}
public Legend getLegend() {
return legend;
}
public void setLegend(Legend legend) {
this.legend = legend;
}
public Grid getGrid() {
return grid;
}
public void setGrid(Grid grid) {
this.grid = grid;
}
public Toolbox getToolbox() {
return toolbox;
}
public void setToolbox(Toolbox toolbox) {
this.toolbox = toolbox;
}
public XAxis getxAxis() {
return xAxis;
}
public void setxAxis(XAxis xAxis) {
this.xAxis = xAxis;
}
public YAxis getyAxis() {
return yAxis;
}
public void setyAxis(YAxis yAxis) {
this.yAxis = yAxis;
}
public List<Serie> getSeries() {
return series;
}
public void setSeries(List<Serie> series) {
this.series = series;
}
}

View File

@ -0,0 +1,27 @@
package com.enzhico.echarts.api.model.jmh;
/**
* SaveAsImage
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/10
*/
public class SaveAsImage {
private String type;
public SaveAsImage() {
}
public SaveAsImage(String type) {
this.type = type;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}

View File

@ -0,0 +1,49 @@
package com.enzhico.echarts.api.model.jmh;
import java.util.List;
/**
* Serie
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/9
*/
public class Serie {
private String name;
private String type;
private List<Double> data;
private Label label;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public List<Double> getData() {
return data;
}
public void setData(List<Double> data) {
this.data = data;
}
public Label getLabel() {
return label;
}
public void setLabel(Label label) {
this.label = label;
}
}

View File

@ -0,0 +1,20 @@
package com.enzhico.echarts.api.model.jmh;
/**
* Title
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/9
*/
public class Title {
private String text;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}

View File

@ -0,0 +1,37 @@
package com.enzhico.echarts.api.model.jmh;
/**
* Toolbox
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/9
*/
public class Toolbox {
private boolean show;
private Feature feature;
public Toolbox() {
}
public Toolbox(boolean show, Feature feature) {
this.show = show;
this.feature = feature;
}
public boolean isShow() {
return show;
}
public void setShow(boolean show) {
this.show = show;
}
public Feature getFeature() {
return feature;
}
public void setFeature(Feature feature) {
this.feature = feature;
}
}

View File

@ -0,0 +1,37 @@
package com.enzhico.echarts.api.model.jmh;
/**
* Tooltip
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/9
*/
public class Tooltip {
private String trigger;
private AxisPointer axisPointer;
public Tooltip() {
}
public Tooltip(String trigger, AxisPointer axisPointer) {
this.trigger = trigger;
this.axisPointer = axisPointer;
}
public String getTrigger() {
return trigger;
}
public void setTrigger(String trigger) {
this.trigger = trigger;
}
public AxisPointer getAxisPointer() {
return axisPointer;
}
public void setAxisPointer(AxisPointer axisPointer) {
this.axisPointer = axisPointer;
}
}

View File

@ -0,0 +1,38 @@
package com.enzhico.echarts.api.model.jmh;
/**
* XAxis
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/9
*/
public class XAxis {
private String type;
private String name;
public XAxis() {
}
public XAxis(String type, String name) {
this.type = type;
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,59 @@
package com.enzhico.echarts.api.model.jmh;
import java.util.List;
/**
* YAxis
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/9
*/
public class YAxis {
private String type;
private boolean inverse;
private List<String> data;
private YAxisLabel axisLabel;
public YAxis() {
}
public YAxis(String type, boolean inverse, List<String> data, YAxisLabel axisLabel) {
this.type = type;
this.inverse = inverse;
this.data = data;
this.axisLabel = axisLabel;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public boolean isInverse() {
return inverse;
}
public void setInverse(boolean inverse) {
this.inverse = inverse;
}
public List<String> getData() {
return data;
}
public void setData(List<String> data) {
this.data = data;
}
public YAxisLabel getAxisLabel() {
return axisLabel;
}
public void setAxisLabel(YAxisLabel axisLabel) {
this.axisLabel = axisLabel;
}
}

View File

@ -0,0 +1,27 @@
package com.enzhico.echarts.api.model.jmh;
/**
* YAxisLabel
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/9
*/
public class YAxisLabel {
private Integer margin;
public YAxisLabel() {
}
public YAxisLabel(Integer margin) {
this.margin = margin;
}
public Integer getMargin() {
return margin;
}
public void setMargin(Integer margin) {
this.margin = margin;
}
}

View File

@ -0,0 +1,58 @@
package com.enzhico.echarts.common;
import com.corundumstudio.socketio.SocketIOServer;
import com.enzhico.echarts.config.properties.MyProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.IOException;
/**
* SpringBoot启动之后执行
*
* @author XiongNeng
* @version 1.0
* @since 2017/7/31
*/
@Component
@Order(1)
public class ServerRunner implements CommandLineRunner {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final SocketIOServer server;
// 下载地址版本2.1.1https://bitbucket.org/ariya/phantomjs/downloads/
private static final String PHANTOM_PATH = "phantomjs";
@Resource
private MyProperties p;
@Autowired
public ServerRunner(SocketIOServer server) {
this.server = server;
}
@Override
public void run(String... args) {
logger.info("ServerRunner 开始启动啦...");
server.start();
logger.info("SocketServer 启动成功!");
logger.info("点击打开首页: http://localhost:9075");
// 启动socker服务器后通过phantomjs启动浏览器网页客户端
// openHtml(p.getLoadJs());
// logger.info("Phantomjs 启动成功!");
}
// private void openHtml(String loadJs) {
// String cmdStr = PHANTOM_PATH + " " + loadJs + " " + "http://localhost:9075";
// logger.info("cmdStr=" + cmdStr);
// Runtime rt = Runtime.getRuntime();
// try {
// rt.exec(cmdStr);
// } catch (IOException e) {
// logger.error("执行phantomjs的指令失败PhantomJs详情参考这里:http://phantomjs.org", e);
// }
// }
}

View File

@ -0,0 +1,13 @@
package com.enzhico.echarts.common.constants;
/**
* 消息类型
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/8
*/
public class MsgType {
public static final String TYPE1 = "type1";
public static final String TYPE2 = "type2";
}

View File

@ -0,0 +1,41 @@
package com.enzhico.echarts.common.util;
/**
* 常用工具类,字符串、数字相关
*
* @author XiongNeng
* @version 1.0
* @since 2018/1/15
*/
public class CommonUtil {
/**
* 检查某版本是否比现在版本更大些
*
* @param version 某版本
* @param nowVersion 现在使用的版本
* @return 是否版本数更大
*/
public static boolean isNewer(String version, String nowVersion) {
try {
String[] versions = version.split("\\.");
String[] nowVersions = nowVersion.split("\\.");
if (versions.length != nowVersions.length) {
return false;
}
int sum = 0;
for (String v : versions) {
sum += sum * 10 + Integer.valueOf(v);
}
int nowSum = 0;
for (String nv : nowVersions) {
nowSum += nowSum * 10 + Integer.valueOf(nv);
}
return sum > nowSum;
} catch (NumberFormatException e) {
return false;
}
}
}

View File

@ -0,0 +1,85 @@
package com.enzhico.echarts.common.util;
import com.enzhico.echarts.api.model.jmh.*;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* ExportPngUtil
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/24
*/
public class ExportPngUtil {
public static void postOption(String optionStr, String url) throws Exception {
final MediaType TEXT = MediaType.parse("application/text; charset=utf-8");
OkHttpClient client = new OkHttpClient();
RequestBody body = RequestBody.create(TEXT, optionStr);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
System.out.println(response.body().string());
} else {
throw new IOException("Unexpected code " + response);
}
}
public static String generateOption(String titleStr, List<String> objects, List<String> dimensions,
List<List<Double>> allData, String xunit) {
Option option = new Option();
// "title"
Title title = new Title();
title.setText(titleStr);
// "tooltip"
Tooltip tooltip = new Tooltip("axis", new AxisPointer("shadow"));
// "legend"
Legend legend = new Legend(objects);
// "grid"
Grid grid = new Grid(100);
// "toolbox"
Toolbox toolbox = new Toolbox(false, new Feature(new SaveAsImage("png")));
// "xAxis"
XAxis xAxis = new XAxis("value", xunit);
// "yAxis"
YAxis yAxis = new YAxis("category", false, dimensions, new YAxisLabel(20));
// "series"
List<Serie> series = new ArrayList<>();
for (int i = 0; i < allData.size(); i++) {
Serie serie = new Serie();
serie.setName(objects.get(i));
serie.setType("bar");
serie.setLabel(new Label(new Normal(true, 2)));
serie.setData(allData.get(i));
series.add(serie);
}
// 开始设置option
option.setTitle(title);
option.setTooltip(tooltip);
option.setLegend(legend);
option.setGrid(grid);
option.setToolbox(toolbox);
option.setxAxis(xAxis);
option.setyAxis(yAxis);
option.setSeries(series);
String jsonString = null;
try {
jsonString = new ObjectMapper().writeValueAsString(option);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return jsonString;
}
}

View File

@ -0,0 +1,55 @@
package com.enzhico.echarts.config;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.SpringAnnotationScanner;
import com.enzhico.echarts.config.properties.MyProperties;
import com.enzhico.echarts.service.ApiService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
/**
* NettySocketConfig
*
* @author XiongNeng
* @version 1.0
* @since 2018/1/19
*/
@Configuration
public class NettySocketConfig {
@Resource
private MyProperties myProperties;
@Resource
private ApiService apiService;
private static final Logger logger = LoggerFactory.getLogger(NettySocketConfig.class);
@Bean
public SocketIOServer socketIOServer() {
/*
* 创建Socket并设置监听端口
*/
com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration();
// 设置主机名默认是0.0.0.0
// config.setHostname("localhost");
// 设置监听端口
config.setPort(myProperties.getSocketPort());
// 协议升级超时时间毫秒默认10000。HTTP握手升级为ws协议超时时间
config.setUpgradeTimeout(10000);
// Ping消息间隔毫秒默认25000。客户端向服务器发送一条心跳消息间隔
config.setPingInterval(myProperties.getPingInterval());
// Ping消息超时时间毫秒默认60000这个时间间隔内没有接收到心跳消息就会发送超时事件
config.setPingTimeout(myProperties.getPingTimeout());
return new SocketIOServer(config);
}
@Bean
public SpringAnnotationScanner springAnnotationScanner(SocketIOServer socketServer) {
return new SpringAnnotationScanner(socketServer);
}
}

View File

@ -0,0 +1,88 @@
package com.enzhico.echarts.config.properties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 本项目自定义配置
*
* @author xiongneng
* @since 2018/01/06 21:09
*/
@Component
@ConfigurationProperties(prefix = "enzhico")
public class MyProperties {
/**
* socket端口
*/
private Integer socketPort;
/**
* Ping消息间隔毫秒
*/
private Integer pingInterval;
/**
* Ping消息超时时间毫秒
*/
private Integer pingTimeout;
/**
* 图片保存路径
*/
private String imageDir;
/**
* Phantomjs加载文件
*/
private String loadJs;
/**
* 打开的HTMl文件
*/
private String indexHtml;
public Integer getSocketPort() {
return socketPort;
}
public void setSocketPort(Integer socketPort) {
this.socketPort = socketPort;
}
public Integer getPingInterval() {
return pingInterval;
}
public void setPingInterval(Integer pingInterval) {
this.pingInterval = pingInterval;
}
public Integer getPingTimeout() {
return pingTimeout;
}
public void setPingTimeout(Integer pingTimeout) {
this.pingTimeout = pingTimeout;
}
public String getImageDir() {
return imageDir;
}
public void setImageDir(String imageDir) {
this.imageDir = imageDir;
}
public String getLoadJs() {
return loadJs;
}
public void setLoadJs(String loadJs) {
this.loadJs = loadJs;
}
public String getIndexHtml() {
return indexHtml;
}
public void setIndexHtml(String indexHtml) {
this.indexHtml = indexHtml;
}
}

View File

@ -0,0 +1,22 @@
package com.enzhico.echarts.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* IndexController
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/24
*/
@Controller
public class IndexController {
/**
* 首页
*/
@RequestMapping(value = "/")
public String index() {
return "index";
}
}

View File

@ -0,0 +1,93 @@
package com.enzhico.echarts.handler;
import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.OnConnect;
import com.corundumstudio.socketio.annotation.OnDisconnect;
import com.corundumstudio.socketio.annotation.OnEvent;
import com.enzhico.echarts.common.constants.MsgType;
import com.enzhico.echarts.config.properties.MyProperties;
import com.enzhico.echarts.service.ApiService;
import io.socket.client.Socket;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
/**
* 消息事件处理器
*
* @author XiongNeng
* @version 1.0
* @since 2018/1/19
*/
@Component
public class MessageEventHandler {
@Resource
private MyProperties p;
private final SocketIOServer server;
private final ApiService apiService;
private static final Logger logger = LoggerFactory.getLogger(MessageEventHandler.class);
@Autowired
public MessageEventHandler(SocketIOServer server, ApiService apiService) {
this.server = server;
this.apiService = apiService;
}
/**
* 添加connect事件当客户端发起连接时调用
*
* @param client 客户端
*/
@OnConnect
public void onConnect(SocketIOClient client) {
if (client != null) {
final String sessionId = client.getSessionId().toString();
logger.info("连接成功, sessionId=" + sessionId);
// 赶紧保存这个sessionID呀
apiService.updateSessionId(sessionId);
} else {
logger.error("客户端为空");
}
}
/**
* 添加@OnDisconnect事件客户端断开连接时调用刷新客户端信息
*
* @param client 客户端
*/
@OnDisconnect
public void onDisconnect(SocketIOClient client) {
logger.info("客户端断开连接, sessionId=" + client.getSessionId().toString());
client.disconnect();
}
/**
* 保存客户端传来的图片数据
*
* @param client 客户端
* @param ackRequest 回执消息
* @param imgData Base64的图形数据
*/
@OnEvent(value = "savePic")
public void onSavePic(SocketIOClient client, AckRequest ackRequest, String imgData) {
logger.info("保存客户端传来的图片数据 start, sessionId=" + client.getSessionId().toString());
String r = apiService.saveBase64Pic(imgData);
logger.info("保存客户端传来的图片 = {}", r);
ackRequest.sendAckData("图片保存结果=" + r);
}
}

View File

@ -0,0 +1,105 @@
package com.enzhico.echarts.service;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import com.enzhico.echarts.config.properties.MyProperties;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* 专门用来服务对外接口用Service
*/
@Service
public class ApiService {
private static final Logger logger = LoggerFactory.getLogger(ApiService.class);
/**
* 保存最后一个连接上来的sessionID
*/
private String sessionId;
@Resource
private MyProperties p;
// private LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<>();
@Resource
private SocketIOServer server;
public synchronized void updateSessionId(String sid) {
sessionId = sid;
}
/**
* 服务器主动推送消息
*
* @param msgType 消息类型
* @param jsonData echarts图表数据
*/
public void pushMsg(String msgType, String jsonData) {
SocketIOClient targetClient = this.server.getClient(UUID.fromString(sessionId));
if (targetClient == null) {
logger.error("sessionId=" + sessionId + "在server中获取不到client");
} else {
targetClient.sendEvent(msgType, jsonData);
}
// queue.offer(jsonData);
}
// public String popJson() {
// try {
// return queue.poll(100L, TimeUnit.MILLISECONDS);
// } catch (InterruptedException e) {
// e.printStackTrace();
// return null;
// }
// }
/**
* 解析Base64位信息并输出到某个目录下面.
*
* @param base64Info base64串
* @return 文件地址
*/
public String saveBase64Pic(String base64Info) {
if (StringUtils.isEmpty(base64Info)) {
return "";
}
// 数据中data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABI4AAAEsCAYAAAClh/jbAAA ... 在"base64,"之后的才是图片信息
String[] arr = base64Info.split("base64,");
// 将图片输出到系统某目录.
OutputStream out = null;
String now = FastDateFormat.getInstance("yyyyMMddHHmmss").format(new Date());
String destFile = p.getImageDir() + now + ".png";
try {
// 使用了Apache commons codec的包来解析Base64
byte[] buffer = Base64.decodeBase64(arr[1]);
out = new FileOutputStream(destFile);
out.write(buffer);
} catch (IOException e) {
logger.error("解析Base64图片信息并保存到某目录下出错!", e);
return "";
} finally {
IOUtils.closeQuietly(out);
}
return destFile;
}
}

View File

@ -0,0 +1,70 @@
##########################################################
################## 所有profile共有的配置 #################
##########################################################
################### 自定义项目配置 ###################
enzhico:
socket-port: 9076 #socket端口
ping-interval: 60000 #Ping消息间隔毫秒
ping-timeout: 180000 #Ping消息超时时间毫秒
################### 项目启动端口 ###################
server:
port: 9075
jetty:
max-http-post-size: 20000000
################### spring配置 ###################
spring:
profiles:
active: dev
http:
multipart:
max-request-size: 100MB #最大请求大小
max-file-size: 100MB #最大文件大小
logging:
level:
org.springframework.web.servlet: ERROR
---
#####################################################################
######################## 开发环境profile ##########################
#####################################################################
spring:
profiles: dev
enzhico:
image-dir: E:/pics/
load-js: E:/pics/html/js/echarts-load.js
index-html: file:///E:/pics/html/index.html
logging:
level:
ROOT: INFO
com:
enzhico: DEBUG
file: E:/logs/sb-ehcharts.log
---
#####################################################################
######################## 测试环境profile ##########################
#####################################################################
spring:
profiles: test
enzhico:
image-dir: /var/pics/echarts/
load-js: /usr/share/nginx/html/echarts/js/echarts-load.js
index-html: file:///usr/share/nginx/html/echarts/index.html
logging:
level:
ROOT: INFO
com:
enzhico: DEBUG
file: /var/logs/sb-ehcharts.log

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,43 @@
var system = require('system');
var page = require('webpage').create();
// 获取第二个参数(即请求地址url).
var url = system.args[1];
console.log('url:' + url);
// 显示控制台日志.
page.onConsoleMessage = function (msg, lineNum, sourceId) {
console.log('console message ----> ' + msg);
};
page.onLoadStarted = function () {
console.log("load started");
};
page.onLoadFinished = function () {
console.log("load finished");
};
page.onUrlChanged = function () {
console.log("onUrlChanged");
};
//打开给定url的页面.
var start = new Date().getTime();
page.open(url, function (status) {
if (status === 'success') {
console.log('echarts页面加载完成,加载耗时:' + (new Date().getTime() - start) + ' ms');
// // 处理页面
// var pic_url = page.evaluate(function() {
// // DOM操作
// return document.getElementById('cp_image').getAttribute('src');
// });
// 由于echarts动画效果延迟500毫秒确保图片渲染完毕再调用下载图片方法.
// setTimeout(function () {
// page.evaluate(function () {
// console.log("调用了echarts的下载图片功能.");
// });
// }, 500);
} else {
console.log("页面加载失败 Page failed to load!");
}
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,122 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="renderer" content="webkit">
<title>Echarts Demo</title>
<link rel="shortcut icon" th:href="@{/favicon.ico}"/>
<link th:href="@{/static/css/bootstrap.css}" rel="stylesheet"/>
<style>
body {
padding: 20px;
}
#console {
height: 100px;
overflow: auto;
}
.connect-msg {
color: green;
}
.disconnect-msg {
color: red;
}
.send-msg {
color: #888
}
</style>
</head>
<body>
<h1>Netty-socketio Demo Chat</h1>
<br/>
<div id="console" class="well"></div>
<!-- 为 ECharts 准备一个具备大小(宽高)的 DOM -->
<div id="main" style="width:860px; height:470px;"></div>
<form class="well form-inline" onsubmit="return false;">
<input id="msg" class="input-xlarge" type="text" placeholder="Type something..."/>
<button type="button" onClick="sendDisconnect()" class="btn">Disconnect</button>
<button type="button" onClick="sendSavePic()" class="btn">Save Picture</button>
</form>
<script th:src="@{/static/js/jquery-1.10.1.min.js}"></script>
<script th:src="@{/static/js/socket.io.js}"></script>
<script th:src="@{/static/js/moment.min.js}"></script>
<script th:src="@{/static/js/echarts.common.min.js}" charset="utf-8"></script>
<script>
var socket;
var myChart;
function sendDisconnect() {
socket.disconnect();
}
function output(message) {
var currentTime = "<span class='time'>" + moment().format('HH:mm:ss.SSS') + "</span>";
var element = $("<div>" + currentTime + " " + message + "</div>");
$('#console').prepend(element);
}
function sendSavePic() {
socket.emit('savePic', myChart.getDataURL(), function (result) {
output('<span class="connect-msg">' + result + '</span>');
});
}
function initPage() {
console.log('this is index.html log...');
var userName = 'user' + Math.floor((Math.random() * 1000) + 1);
socket = io.connect('http://localhost:9076');
console.log('socket = ' + socket);
socket.on('connect', function () {
console.log('connect successful');
output('<span class="connect-msg">Client has connected to the server!</span>');
});
socket.on('ping', function () {
console.log('ping successful');
output('<span class="connect-msg">Client Ping msg to the server!</span>');
});
socket.on('pong', function () {
console.log('pong successful');
output('<span class="connect-msg">Client Pong msg from the server!</span>');
});
socket.on('disconnect', function () {
console.log('disconnect successful');
output('<span class="disconnect-msg">The client has disconnected!</span>');
});
socket.on('notify', function (jsonBody) {
console.log('get notify message from server...');
var msg = JSON.parse(jsonBody);
output('<span class="connect-msg">接收到notify消息, 去给我保存图片</span>');
var option = msg.option;
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(JSON.parse(option));
// 渲染完成之后再给服务器发送保存图片消息
sendSavePic();
});
/**************************************分割线***********************************/
// 基于准备好的dom初始化echarts实例
myChart = echarts.init(document.getElementById('main'));
console.log('index initPage finished!')
}
$(function () {
initPage();
});
</script>
</body>
</html>

View File

@ -0,0 +1,53 @@
package com.enzhico.echarts.common.util;
import com.enzhico.echarts.api.model.jmh.*;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import okhttp3.*;
import org.junit.Test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static com.enzhico.echarts.common.util.ExportPngUtil.generateOption;
import static com.enzhico.echarts.common.util.ExportPngUtil.postOption;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
public class CommonUtilTest {
@Test
public void isNewer() {
assertThat(CommonUtil.isNewer("1.2.1", "1.2.0"), is(true));
assertThat(CommonUtil.isNewer("1.2", "1.2.0"), is(false));
assertThat(CommonUtil.isNewer("2.1.9", "1.2.0"), is(true));
assertThat(CommonUtil.isNewer("adfa.1.3", "1.2.0"), is(false));
}
@Test
public void testTimestamp() {
System.out.println(System.currentTimeMillis());
}
@Test
public void testOption() throws Exception {
String titleStr = "对象序列化为JSON字符串";
// 几个测试对象
List<String> objects = Arrays.asList("FastJson", "Jackson", "Gson", "Json-lib");
// 测试维度输入值n
List<String> dimensions = Arrays.asList("10000次", "100000次", "1000000次");
// 有几个测试对象,就有几组测试数据,每组测试数据中对应几个维度的结果
List<List<Double>> allData = new ArrayList<List<Double>>(){{
add(Arrays.asList(2.17, 9.10, 21.70));
add(Arrays.asList(1.94, 8.94, 19.43));
add(Arrays.asList(4.88, 22.88, 48.89));
add(Arrays.asList(9.11, 58.14, 108.44));
}};
String optionStr = generateOption(titleStr, objects, dimensions, allData, "");
// POST到接口上
postOption(optionStr, "http://localhost:9075/api/v1/data");
}
}