Simplest Java code for custom dynamic headers?

130 views
Skip to first unread message

Edvard Fagerholm

unread,
Jun 21, 2023, 1:05:46 PM6/21/23
to grpc.io
Hi there,

I have a load balancer that routes based on a shard ID to the correct gRPC server. The code is essentially the following:

   public void stateUpdate(StateUpdateRequest req, StreamObserver<Empty> responseObserver) {
        // Run on Virtual Thread
        executor.submit(() -> {
            if (isAuthorized(req)) {
                responseObserver.onError(Status.PERMISSION_DENIED.asException());
                return;
            }

            try {
                int shard = calculateShard(req.getAccount());
                // How to add "X-Shard" header with above value to request?
                clientStub
                    .update(createUpdateRequest(req));
            } catch (Exception e) {
                log.error("forwarding state update", e);
                responseObserver.onError(Status.INTERNAL.asException());
                return;
            }

            responseObserver.onNext(EMPTY);
            responseObserver.onCompleted();
        });
    }

I would need to add the calculated shard as the "X-Shard" header value to every request made by the stub. The single stub is shared by all the threads.

What is the easiest way to make this happen? I've previously created stubs with interceptors that always attach a fixed header value, e.g. a JWT token. However, the header value is dynamically calculated in this case. If I use withOptions to pass data to the interceptor, then on the surface it doesn't seem thread-safe unless there's something smart being done under the hood?

My question: What is the cleanest way to accomplish this?

Best,
Edvard

sanjay...@google.com

unread,
Jun 21, 2023, 1:50:39 PM6/21/23
to grpc.io
>             if (isAuthorized(req)) {

You probably meant 

               if (!isAuthorized(req))

You can use MetadataUtils.newAttachHeadersInterceptor to get an interceptor to add your "X-shard" header and then use withInterceptors() to get a stub that will use the interceptor. Use that stub in place of your clientStub after you calculate the shard. 

Edvard Fagerholm

unread,
Jun 21, 2023, 2:10:04 PM6/21/23
to grpc.io
Are you saying that I basically need to dynamically create a new interceptor + stub on every request? I thought of that option, but assumed it would be expensive and there was a more efficient option.

Best,
Edvard

sanjay...@google.com

unread,
Jun 21, 2023, 3:41:57 PM6/21/23
to grpc.io
No, create the interceptor and stub just once and save it somewhere. You also will have to save the Metadata instance passed to MetadataUtils.newAttachHeadersInterceptor  and keep that instance up to date with the latest shard value: so remove the old header and add the new one.
Reply all
Reply to author
Forward
0 new messages