1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.http;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.buffer.ByteBufUtil;
20 import io.netty.buffer.Unpooled;
21 import io.netty.microbench.util.AbstractMicrobenchmark;
22 import io.netty.util.AsciiString;
23 import io.netty.util.CharsetUtil;
24 import org.openjdk.jmh.annotations.Benchmark;
25 import org.openjdk.jmh.annotations.Measurement;
26 import org.openjdk.jmh.annotations.Param;
27 import org.openjdk.jmh.annotations.Scope;
28 import org.openjdk.jmh.annotations.Setup;
29 import org.openjdk.jmh.annotations.State;
30 import org.openjdk.jmh.annotations.Warmup;
31
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.List;
35 import java.util.SplittableRandom;
36
37 import static io.netty.handler.codec.http.HttpConstants.CR;
38 import static io.netty.handler.codec.http.HttpConstants.LF;
39 import static io.netty.handler.codec.http.HttpConstants.SP;
40
41 @State(Scope.Benchmark)
42 @Warmup(iterations = 10)
43 @Measurement(iterations = 20)
44 public class HttpRequestEncoderInsertBenchmark extends AbstractMicrobenchmark {
45
46 private static final String[] PARAMS = {
47 "eventType=CRITICAL",
48 "from=0",
49 "to=1497437160327",
50 "limit=10",
51 "offset=0"
52 };
53 @Param({"1024", "128000"})
54 private int samples;
55
56 private String[] uris;
57 private int index;
58 private final OldHttpRequestEncoder encoderOld = new OldHttpRequestEncoder();
59 private final HttpRequestEncoder encoderNew = new HttpRequestEncoder();
60
61 @Setup
62 public void setup() {
63 List<String[]> permutations = new ArrayList<>();
64 permute(PARAMS.clone(), 0, permutations);
65
66 String[] allCombinations = new String[permutations.size()];
67 String base = "http://localhost?";
68 for (int i = 0; i < permutations.size(); i++) {
69 StringBuilder sb = new StringBuilder(base);
70 String[] p = permutations.get(i);
71 for (int j = 0; j < p.length; j++) {
72 if (j != 0) {
73 sb.append('&');
74 }
75 sb.append(p[j]);
76 }
77 allCombinations[i] = sb.toString();
78 }
79 uris = new String[samples];
80 SplittableRandom rand = new SplittableRandom(42);
81 for (int i = 0; i < uris.length; i++) {
82 uris[i] = allCombinations[rand.nextInt(allCombinations.length)];
83 }
84 index = 0;
85 }
86
87 private static void permute(String[] arr, int start, List<String[]> out) {
88 if (start == arr.length - 1) {
89 out.add(Arrays.copyOf(arr, arr.length));
90 return;
91 }
92 for (int i = start; i < arr.length; i++) {
93 swap(arr, start, i);
94 permute(arr, start + 1, out);
95 swap(arr, start, i);
96 }
97 }
98
99 private static void swap(String[] a, int i, int j) {
100 String t = a[i];
101 a[i] = a[j];
102 a[j] = t;
103 }
104
105 private String nextUri() {
106 if (index >= uris.length) {
107 index = 0;
108 }
109 return uris[index++];
110 }
111
112 @Benchmark
113 public ByteBuf oldEncoder() throws Exception {
114 ByteBuf buffer = Unpooled.buffer(100);
115 try {
116 encoderOld.encodeInitialLine(buffer, new DefaultHttpRequest(HttpVersion.HTTP_1_1,
117 HttpMethod.GET, nextUri()));
118 return buffer;
119 } finally {
120 buffer.release();
121 }
122 }
123
124 @Benchmark
125 public ByteBuf newEncoder() throws Exception {
126 ByteBuf buffer = Unpooled.buffer(100);
127 try {
128 encoderNew.encodeInitialLine(buffer, new DefaultHttpRequest(HttpVersion.HTTP_1_1,
129 HttpMethod.GET, nextUri()));
130 return buffer;
131 } finally {
132 buffer.release();
133 }
134 }
135
136 private static class OldHttpRequestEncoder extends HttpObjectEncoder<HttpRequest> {
137 private static final byte[] CRLF = {CR, LF};
138 private static final char SLASH = '/';
139 private static final char QUESTION_MARK = '?';
140
141 @Override
142 public boolean acceptOutboundMessage(Object msg) throws Exception {
143 return super.acceptOutboundMessage(msg) && !(msg instanceof HttpResponse);
144 }
145
146 @Override
147 protected void encodeInitialLine(ByteBuf buf, HttpRequest request) throws Exception {
148 AsciiString method = request.method().asciiName();
149 ByteBufUtil.copy(method, method.arrayOffset(), buf, method.length());
150 buf.writeByte(SP);
151
152
153
154 String uri = request.uri();
155
156 if (uri.isEmpty()) {
157 uri += SLASH;
158 } else {
159 int start = uri.indexOf("://");
160 if (start != -1 && uri.charAt(0) != SLASH) {
161 int startIndex = start + 3;
162
163
164 int index = uri.indexOf(QUESTION_MARK, startIndex);
165 if (index == -1) {
166 if (uri.lastIndexOf(SLASH) <= startIndex) {
167 uri += SLASH;
168 }
169 } else {
170 if (uri.lastIndexOf(SLASH, index) <= startIndex) {
171 int len = uri.length();
172 StringBuilder sb = new StringBuilder(len + 1);
173 sb.append(uri, 0, index)
174 .append(SLASH)
175 .append(uri, index, len);
176 uri = sb.toString();
177 }
178 }
179 }
180 }
181
182 buf.writeBytes(uri.getBytes(CharsetUtil.UTF_8));
183
184 buf.writeByte(SP);
185 request.protocolVersion().encode(buf);
186 buf.writeBytes(CRLF);
187 }
188 }
189 }