endpoints = new ConcurrentHashMap<>();
48 | private ScheduledExecutorService executor;
49 |
50 | @Override
51 | public ServerStreamTracer newServerStreamTracer(String fullMethodName, Metadata headers) {
52 |
53 | return new ServerStreamTracer() {
54 | final long start = System.nanoTime();
55 | final Recorder endpoint = endpoints.computeIfAbsent(fullMethodName, k -> newRecorder());
56 |
57 | @Override
58 | public void outboundWireSize(long bytes) {
59 | bytesOut.add(bytes);
60 | }
61 |
62 | @Override
63 | public void inboundWireSize(long bytes) {
64 | bytesIn.add(bytes);
65 | }
66 |
67 | @Override
68 | public void streamClosed(Status status) {
69 | final double duration = (System.nanoTime() - start) * 1e-9;
70 | LOGGER.debug(
71 | Markers.append("grpc_method_name", fullMethodName)
72 | .and(Markers.append("status", status))
73 | .and(Markers.append("duration", duration)),
74 | "request handled");
75 | all.record(start);
76 | endpoint.record(start);
77 | }
78 | };
79 | }
80 |
81 | public void start() {
82 | executor = Executors.newSingleThreadScheduledExecutor();
83 | executor.scheduleAtFixedRate(this::report, 1, 1, TimeUnit.SECONDS);
84 | }
85 |
86 | public void stop() {
87 | executor.shutdown();
88 | }
89 |
90 | /**
91 | * Calculate and report the three parameters of Little's Law and some latency percentiles.
92 | *
93 | * This just writes them to stdout, but presumably we'd be reporting them to a centralized
94 | * service.
95 | */
96 | private void report() {
97 | LogstashMarker marker =
98 | Markers.append("all", all.interval())
99 | .and(Markers.append("bytes_in", bytesIn.interval()))
100 | .and(Markers.append("bytes_out", bytesOut.interval()));
101 | for (Entry entry : endpoints.entrySet()) {
102 | marker = marker.and(Markers.append(entry.getKey(), entry.getValue().interval()));
103 | }
104 | LOGGER.info(marker, "stats");
105 | }
106 |
107 | private Recorder newRecorder() {
108 | return new Recorder(MIN_DURATION, MAX_DURATION, GOAL_DURATION, TimeUnit.MICROSECONDS);
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/main/java/com/codahale/grpcproxy/util/TlsContext.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 |
15 | package com.codahale.grpcproxy.util;
16 |
17 | import io.grpc.netty.GrpcSslContexts;
18 | import io.netty.handler.ssl.ClientAuth;
19 | import io.netty.handler.ssl.SslContext;
20 | import io.netty.handler.ssl.SslContextBuilder;
21 | import io.netty.handler.ssl.SslProvider;
22 | import java.io.File;
23 | import javax.net.ssl.SSLException;
24 |
25 | public class TlsContext {
26 |
27 | private final File trustedCerts, cert, key;
28 |
29 | public TlsContext(String trustedCertsPath, String certPath, String keyPath) {
30 | this.trustedCerts = new File(trustedCertsPath);
31 | if (!trustedCerts.exists()) {
32 | throw new IllegalArgumentException("Can't find " + trustedCertsPath);
33 | }
34 |
35 | this.cert = new File(certPath);
36 | if (!cert.exists()) {
37 | throw new IllegalArgumentException("Can't find " + certPath);
38 | }
39 |
40 | this.key = new File(keyPath);
41 | if (!key.exists()) {
42 | throw new IllegalArgumentException("Can't find " + keyPath);
43 | }
44 | }
45 |
46 | public SslContext toClientContext() throws SSLException {
47 | return GrpcSslContexts.configure(SslContextBuilder.forClient(), SslProvider.OPENSSL)
48 | .trustManager(trustedCerts)
49 | .keyManager(cert, key)
50 | .build();
51 | }
52 |
53 | public SslContext toServerContext() throws SSLException {
54 | return GrpcSslContexts.configure(SslContextBuilder.forServer(cert, key), SslProvider.OPENSSL)
55 | .trustManager(trustedCerts)
56 | .clientAuth(ClientAuth.REQUIRE)
57 | .build();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/proto/helloworld.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | option java_multiple_files = true;
4 | option java_package = "com.codahale.grpcproxy.helloworld";
5 | option java_outer_classname = "HelloWorldProto";
6 |
7 | package helloworld;
8 |
9 | // The greeting service definition.
10 | service Greeter {
11 | // Sends a greeting
12 | rpc SayHello (HelloRequest) returns (HelloReply) {
13 | }
14 | }
15 |
16 | // The request message containing the user's name.
17 | message HelloRequest {
18 | string name = 1;
19 | }
20 |
21 | // The response message containing the greetings
22 | message HelloReply {
23 | string message = 1;
24 | }
--------------------------------------------------------------------------------
/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------