Search code examples
javagrpchttp2

How to ping http2 server port for liveness (grpc server)?


I'm trying to ping a grpc http2 server port for liveness. In Http1.1 we would use url.openConnection and check the response status code.

What is the equivalent in http2?

The below code doesn't seem to error but I can't quite tell how to use it to check to see if the request succeeded?

import org.apache.commons.io.IOUtils;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.api.server.ServerSessionListener;
import org.eclipse.jetty.http2.client.HTTP2Client;
import org.eclipse.jetty.http2.frames.DataFrame;
import org.eclipse.jetty.http2.frames.HeadersFrame;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.FuturePromise;
import org.eclipse.jetty.util.Jetty;
import org.eclipse.jetty.util.ssl.SslContextFactory;

import javax.net.ssl.HttpsURLConnection;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.URL;
import java.util.concurrent.TimeUnit;

public class HeartbeatCheckHttp2 {
    public static void main(String [] args) throws Exception {
        for (int i=0; i<50000; ++i) {
            Thread.sleep(400L);

            HTTP2Client client = new HTTP2Client();
            client.start();

            // Connect to host.
            String host = "localhost";
            int port = 50000;

            FuturePromise<Session> sessionPromise = new FuturePromise<>();
            client.connect(new InetSocketAddress(host, port), new ServerSessionListener.Adapter(), sessionPromise);


            // Obtain the client Session object.
            Session session = sessionPromise.get(5, TimeUnit.SECONDS);

            // Prepare the HTTP request headers.
            HttpFields requestFields = new HttpFields();
            requestFields.put("User-Agent", client.getClass().getName() + "/" + Jetty.VERSION);
            requestFields.put("Content-Type", "application/grpc");
            // Prepare the HTTP request object.
            MetaData.Request request = new MetaData.Request("POST", new HttpURI("http://" + host + ":" + port + "/"), HttpVersion.HTTP_2, requestFields);
            // Create the HTTP/2 HEADERS frame representing the HTTP request.
            HeadersFrame headersFrame = new HeadersFrame(request, null, true);

            // Prepare the listener to receive the HTTP response frames.
            Stream.Listener responseListener = new Stream.Listener.Adapter()
            {
                @Override
                public void onData(Stream stream, DataFrame frame, Callback callback)
                {
                    byte[] bytes = new byte[frame.getData().remaining()];
                    frame.getData().get(bytes);
                    callback.succeeded();
                }
            };

            session.newStream(headersFrame, new FuturePromise<>(), responseListener);
            session.newStream(headersFrame, new FuturePromise<>(), responseListener);
            session.newStream(headersFrame, new FuturePromise<>(), responseListener);

            Thread.sleep(TimeUnit.SECONDS.toMillis(20));


            client.stop();
        }
    }
}

I can use SocketAddress to ping the Tcp liveness, but I want to make an actual Http2 call to do the liveness check.


Solution

  • The HTTP/2 protocol has built-in heartbeat frames that can be use to just "ping" the other peer for liveness.

    HTTP2Client client = new HTTP2Client();
    client.start();
    
    FuturePromise<Session> sessionPromise = new FuturePromise<>();
    client.connect(new InetSocketAddress(host, port), new ServerSessionListener.Adapter() {
        @Override
        public void onPing(Session session, PingFrame frame) {
            // The other peer replies to our pings.
        }
    }, sessionPromise);
    Session session = sessionPromise.get(5, TimeUnit.SECONDS);
    
    session.ping(new PingFrame(System.nanoTime(), false), Callback.NOOP);
    

    Having said about the built-in ping facilities, if you want to check the response status of a HTTP request, you should implement onHeaders():

    Stream.Listener responseListener = new Stream.Listener.Adapter() {
        @Override
        public void onHeaders(Stream stream, HeadersFrame frame) {
            MetaData metaData = frame.getMetaData();
            if (metaData.isResponse()) {
                MetaData.Response response = (MetaData.Response)metaData;
                int status = response.getStatus();
                ...
            }
        }
    
        @Override
        public void onData(Stream stream, DataFrame frame, Callback callback) {
            ...
        }
    };
    

    This is how you would do it with a server that speaks HTTP via HTTP/2 framing.

    With gRPC, things may be different, as the gRPC protocol is transported by HTTP/2 frames, but it's not HTTP.