Skip to content

Commit 29f89ad

Browse files
committed
JMH: 新增 rpc-benchmark 模块并优化 Kryo 序列化器以提高 Protobuf 性能和资源复用,然后进行了JMH性能测试
1 parent 36ae6c6 commit 29f89ad

12 files changed

Lines changed: 441 additions & 49 deletions

File tree

grpc-demo.iml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
<module version="4">
33
<component name="AdditionalModuleElements">
44
<content url="file://$MODULE_DIR$" dumb="true">
5-
<sourceFolder url="file://$MODULE_DIR$/target/generated-sources/protobuf/java" isTestSource="false" />
65
<sourceFolder url="file://$MODULE_DIR$/src/main/Test" isTestSource="true" />
6+
<sourceFolder url="file://$MODULE_DIR$/target/generated-sources/protobuf/java" isTestSource="false" />
77
<excludeFolder url="file://$MODULE_DIR$/python_client/venv/bin" />
88
<excludeFolder url="file://$MODULE_DIR$/python_client/venv/include" />
99
<excludeFolder url="file://$MODULE_DIR$/python_client/venv/lib" />

pom.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@
1616
<module>rpc-spring-boot-starter</module>
1717
<module>rpc-provider</module>
1818
<module>rpc-consumer</module>
19+
<module>rpc-benchmark</module>
1920
</modules>
2021

2122
<properties>
2223
<maven.compiler.source>17</maven.compiler.source>
2324
<maven.compiler.target>17</maven.compiler.target>
2425
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
2526
<netty.version>4.1.86.Final</netty.version>
26-
<lombok.version>1.18.42</lombok.version>
27+
<lombok.version>1.18.36</lombok.version>
2728
<logback.version>1.5.20</logback.version>
2829
<kryo.version>5.6.2</kryo.version>
2930
<protobuf.version>3.24.0</protobuf.version>
@@ -141,8 +142,7 @@
141142
<artifactId>maven-compiler-plugin</artifactId>
142143
<version>3.11.0</version>
143144
<configuration>
144-
<source>17</source>
145-
<target>17</target>
145+
<release>17</release>
146146
<annotationProcessorPaths>
147147
<path>
148148
<groupId>org.projectlombok</groupId>

rpc-benchmark/pom.xml

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
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">
4+
<parent>
5+
<groupId>com.xiaoyu.rpc</groupId>
6+
<artifactId>grpc-demo</artifactId>
7+
<version>1.0-SNAPSHOT</version>
8+
</parent>
9+
<modelVersion>4.0.0</modelVersion>
10+
11+
<artifactId>rpc-benchmark</artifactId>
12+
13+
<properties>
14+
<jmh.version>1.37</jmh.version>
15+
</properties>
16+
17+
<dependencies>
18+
<dependency>
19+
<groupId>org.openjdk.jmh</groupId>
20+
<artifactId>jmh-core</artifactId>
21+
<version>${jmh.version}</version>
22+
</dependency>
23+
<dependency>
24+
<groupId>org.openjdk.jmh</groupId>
25+
<artifactId>jmh-generator-annprocess</artifactId>
26+
<version>${jmh.version}</version>
27+
<scope>provided</scope>
28+
</dependency>
29+
30+
<dependency>
31+
<groupId>com.xiaoyu.rpc</groupId>
32+
<artifactId>rpc-core</artifactId>
33+
<version>${project.version}</version>
34+
</dependency>
35+
<dependency>
36+
<groupId>com.xiaoyu.rpc</groupId>
37+
<artifactId>rpc-api</artifactId>
38+
<version>${project.version}</version>
39+
</dependency>
40+
<dependency>
41+
<groupId>com.xiaoyu.rpc</groupId>
42+
<artifactId>rpc-transport-netty</artifactId>
43+
<version>${project.version}</version>
44+
</dependency>
45+
46+
<!-- Logging -->
47+
<dependency>
48+
<groupId>ch.qos.logback</groupId>
49+
<artifactId>logback-classic</artifactId>
50+
</dependency>
51+
</dependencies>
52+
53+
<build>
54+
<plugins>
55+
<plugin>
56+
<groupId>org.apache.maven.plugins</groupId>
57+
<artifactId>maven-compiler-plugin</artifactId>
58+
<version>3.11.0</version>
59+
<configuration>
60+
<release>17</release>
61+
<annotationProcessorPaths>
62+
<path>
63+
<groupId>org.openjdk.jmh</groupId>
64+
<artifactId>jmh-generator-annprocess</artifactId>
65+
<version>${jmh.version}</version>
66+
</path>
67+
</annotationProcessorPaths>
68+
</configuration>
69+
</plugin>
70+
<plugin>
71+
<groupId>org.apache.maven.plugins</groupId>
72+
<artifactId>maven-shade-plugin</artifactId>
73+
<version>3.2.1</version>
74+
<executions>
75+
<execution>
76+
<phase>package</phase>
77+
<goals>
78+
<goal>shade</goal>
79+
</goals>
80+
<configuration>
81+
<finalName>benchmarks</finalName>
82+
<transformers>
83+
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
84+
<mainClass>org.openjdk.jmh.Main</mainClass>
85+
</transformer>
86+
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
87+
</transformers>
88+
<filters>
89+
<filter>
90+
<artifact>*:*</artifact>
91+
<excludes>
92+
<exclude>META-INF/*.SF</exclude>
93+
<exclude>META-INF/*.DSA</exclude>
94+
<exclude>META-INF/*.RSA</exclude>
95+
</excludes>
96+
</filter>
97+
</filters>
98+
</configuration>
99+
</execution>
100+
</executions>
101+
</plugin>
102+
</plugins>
103+
</build>
104+
105+
</project>
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package com.xiaoyu.rpc.benchmark;
2+
3+
import com.google.protobuf.ByteString;
4+
import com.xiaoyu.rpc.api.HelloService;
5+
import com.xiaoyu.rpc.common.extension.ExtensionLoader;
6+
import com.xiaoyu.rpc.common.serialization.Serializer;
7+
import com.xiaoyu.rpc.common.vo.RpcRequest;
8+
import com.xiaoyu.rpc.common.vo.RpcResponse;
9+
import com.xiaoyu.rpc.core.config.RpcConfig;
10+
import com.xiaoyu.rpc.core.server.ServiceRepository;
11+
import com.xiaoyu.rpc.core.transport.TransportClient;
12+
import com.xiaoyu.rpc.core.transport.netty.NettyTransportClient;
13+
import com.xiaoyu.rpc.core.transport.netty.NettyTransportServer;
14+
import org.openjdk.jmh.annotations.*;
15+
import org.openjdk.jmh.runner.Runner;
16+
import org.openjdk.jmh.runner.RunnerException;
17+
import org.openjdk.jmh.runner.options.Options;
18+
import org.openjdk.jmh.runner.options.OptionsBuilder;
19+
20+
import java.lang.reflect.Field;
21+
import java.net.InetSocketAddress;
22+
import java.util.concurrent.TimeUnit;
23+
24+
@BenchmarkMode({ Mode.Throughput, Mode.AverageTime })
25+
@OutputTimeUnit(TimeUnit.MILLISECONDS)
26+
@State(Scope.Benchmark)
27+
@Fork(value = 1, warmups = 0)
28+
@Warmup(iterations = 1, time = 1)
29+
@Measurement(iterations = 2, time = 1)
30+
public class ProtocolBenchmark {
31+
32+
@Param({ "netty", "http", "http2" })
33+
private String protocol;
34+
35+
private NettyTransportServer server;
36+
private TransportClient client;
37+
private InetSocketAddress address;
38+
private RpcRequest request;
39+
private int port = 9091;
40+
41+
@Setup
42+
public void setup() throws Exception {
43+
// Use a random port to avoid conflicts (Address already in use / TIME_WAIT)
44+
this.port = 10000 + new java.util.Random().nextInt(50000);
45+
46+
// 1. Set Protocol via Reflection
47+
RpcConfig config = RpcConfig.getInstance();
48+
Field protocolField = RpcConfig.class.getDeclaredField("protocol");
49+
protocolField.setAccessible(true);
50+
protocolField.set(config, protocol);
51+
52+
// Ensure Serializer is set to something known, e.g., "java" for arg
53+
// serialization
54+
Field serializerField = RpcConfig.class.getDeclaredField("serializerType");
55+
serializerField.setAccessible(true);
56+
serializerField.set(config, "java");
57+
58+
// 2. Register Service
59+
ServiceRepository.registerService(HelloService.class.getName(), new HelloServiceImpl());
60+
61+
// 3. Start Server
62+
server = new NettyTransportServer(port);
63+
Thread serverThread = new Thread(() -> {
64+
try {
65+
server.start();
66+
} catch (Exception e) {
67+
e.printStackTrace(); // Log server startup errors
68+
}
69+
});
70+
serverThread.setDaemon(true);
71+
serverThread.start();
72+
73+
// Wait for server to start
74+
TimeUnit.SECONDS.sleep(2);
75+
76+
// 4. Setup Client
77+
client = new NettyTransportClient();
78+
address = new InetSocketAddress("127.0.0.1", port);
79+
80+
// 5. Build Request
81+
Serializer serializer = ExtensionLoader.getExtensionLoader(Serializer.class).getExtension("java");
82+
byte[] argBytes = serializer.serialize("Benchmark");
83+
84+
request = RpcRequest.newBuilder()
85+
.setInterfaceName(HelloService.class.getName())
86+
.setMethodName("sayHello")
87+
.addParamTypes("java.lang.String")
88+
.addParameters(ByteString.copyFrom(argBytes))
89+
.build();
90+
}
91+
92+
@TearDown
93+
public void teardown() {
94+
if (server != null) {
95+
server.stop();
96+
}
97+
}
98+
99+
@Benchmark
100+
public Object benchmarkCall() {
101+
return client.sendRequest(request, address);
102+
}
103+
104+
public static void main(String[] args) throws RunnerException {
105+
Options opt = new OptionsBuilder()
106+
.include(ProtocolBenchmark.class.getSimpleName())
107+
.build();
108+
new Runner(opt).run();
109+
}
110+
111+
public static class HelloServiceImpl implements HelloService {
112+
@Override
113+
public String sayHello(String name) {
114+
return "Hello, " + name;
115+
}
116+
}
117+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package com.xiaoyu.rpc.benchmark;
2+
3+
import com.xiaoyu.rpc.common.extension.ExtensionLoader;
4+
import com.xiaoyu.rpc.common.serialization.Serializer;
5+
import org.openjdk.jmh.annotations.*;
6+
import org.openjdk.jmh.runner.Runner;
7+
import org.openjdk.jmh.runner.RunnerException;
8+
import org.openjdk.jmh.runner.options.Options;
9+
import org.openjdk.jmh.runner.options.OptionsBuilder;
10+
11+
import com.xiaoyu.rpc.common.vo.RpcRequest;
12+
import java.io.Serializable;
13+
import java.util.Objects;
14+
import java.util.concurrent.TimeUnit;
15+
16+
@BenchmarkMode({ Mode.AverageTime, Mode.Throughput })
17+
@OutputTimeUnit(TimeUnit.MICROSECONDS)
18+
@State(Scope.Benchmark)
19+
@Fork(value = 1, warmups = 1)
20+
@Warmup(iterations = 1, time = 1)
21+
@Measurement(iterations = 2, time = 1)
22+
public class SerializationBenchmark {
23+
24+
@Param({ "java", "kryo", "json", "protobuf" })
25+
private String serializerName;
26+
27+
private Serializer serializer;
28+
private RpcRequest rpcRequest;
29+
private String testString;
30+
31+
private byte[] serializedRequestBytes;
32+
private byte[] serializedStringBytes;
33+
34+
@Setup
35+
public void setup() {
36+
serializer = ExtensionLoader.getExtensionLoader(Serializer.class).getExtension(serializerName);
37+
38+
// Setup String
39+
testString = "Hello, Benchmark! This is a test string for RPC serialization comparison.";
40+
try {
41+
serializedStringBytes = serializer.serialize(testString);
42+
} catch (Exception e) {
43+
System.err.println("Serializer [" + serializerName + "] failed to serialize String: " + e.getMessage());
44+
}
45+
46+
// Setup RpcRequest (Protobuf Message)
47+
// Note: Java/Kryo/Json can also serialize this since it implements Serializable
48+
// (via GeneratedMessageV3)
49+
// or effectively acts as a POJO for them.
50+
RpcRequest.Builder builder = RpcRequest.newBuilder()
51+
.setInterfaceName("com.example.HelloService")
52+
.setMethodName("sayHello")
53+
.addParamTypes("java.lang.String");
54+
55+
// Add dummy bytes parameter
56+
builder.addParameters(com.google.protobuf.ByteString.copyFromUtf8("Benchmark"));
57+
58+
rpcRequest = builder.build();
59+
60+
try {
61+
serializedRequestBytes = serializer.serialize(rpcRequest);
62+
System.out.println(
63+
"Serializer [" + serializerName + "] POJO (RpcRequest) Size: " + serializedRequestBytes.length
64+
+ " bytes");
65+
} catch (Exception e) {
66+
System.err.println("Serializer [" + serializerName + "] failed to serialize POJO: " + e.getMessage());
67+
}
68+
}
69+
70+
@Benchmark
71+
public void serializePojo(org.openjdk.jmh.infra.Blackhole bh) {
72+
bh.consume(serializer.serialize(rpcRequest));
73+
}
74+
75+
@Benchmark
76+
public void deserializePojo(org.openjdk.jmh.infra.Blackhole bh) {
77+
bh.consume(serializer.deserialize(serializedRequestBytes, RpcRequest.class));
78+
}
79+
80+
@Benchmark
81+
public void serializeString(org.openjdk.jmh.infra.Blackhole bh) {
82+
bh.consume(serializer.serialize(testString));
83+
}
84+
85+
@Benchmark
86+
public void deserializeString(org.openjdk.jmh.infra.Blackhole bh) {
87+
bh.consume(serializer.deserialize(serializedStringBytes, String.class));
88+
}
89+
90+
public static void main(String[] args) throws RunnerException {
91+
Options opt = new OptionsBuilder()
92+
.include(SerializationBenchmark.class.getSimpleName())
93+
.build();
94+
new Runner(opt).run();
95+
}
96+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<configuration>
3+
<!--
4+
JMH Benchmark Logging Configuration
5+
Set root level to ERROR to minimize performance impact during benchmarks.
6+
-->
7+
<root level="ERROR">
8+
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
9+
<encoder>
10+
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
11+
</encoder>
12+
</appender>
13+
</root>
14+
</configuration>

rpc-core/src/main/java/com/xiaoyu/rpc/core/serialization/JsonSerializerImpl.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,25 @@ public class JsonSerializerImpl implements Serializer {
1212

1313
public JsonSerializerImpl() {
1414
this.gson = new GsonBuilder()
15+
.registerTypeHierarchyAdapter(com.google.protobuf.ByteString.class,
16+
new TypeAdapter<com.google.protobuf.ByteString>() {
17+
@Override
18+
public void write(com.google.gson.stream.JsonWriter out,
19+
com.google.protobuf.ByteString value) throws java.io.IOException {
20+
if (value == null) {
21+
out.nullValue();
22+
return;
23+
}
24+
out.value(java.util.Base64.getEncoder().encodeToString(value.toByteArray()));
25+
}
26+
27+
@Override
28+
public com.google.protobuf.ByteString read(com.google.gson.stream.JsonReader in)
29+
throws java.io.IOException {
30+
String s = in.nextString();
31+
return com.google.protobuf.ByteString.copyFrom(java.util.Base64.getDecoder().decode(s));
32+
}
33+
})
1534
.setDateFormat("yyyy-MM-dd HH:mm:ss")
1635
.create();
1736
}

0 commit comments

Comments
 (0)