spring-cloud-gateway icon indicating copy to clipboard operation
spring-cloud-gateway copied to clipboard

Recycling ManagedChannel to prevent creation on every call in JsonToGrpcGatewayFilterFactory

Open nsce9806q opened this issue 2 years ago • 0 comments

Is your feature request related to a problem? Please describe. JsonToGrpcGatewayFilter which converts a json to a gRPC creates ManagedChannel on every call.

package org.springframework.cloud.gateway.filter.factory;

public class JsonToGrpcGatewayFilterFactory
        extends AbstractGatewayFilterFactory<JsonToGrpcGatewayFilterFactory.Config> {
    ... ...
    class GRPCResponseDecorator extends ServerHttpResponseDecorator {
    ... ...
        // We are creating this on every call, should optimize?
        private ManagedChannel createChannelChannel(String host, int port) {
	    NettyChannelBuilder nettyChannelBuilder = NettyChannelBuilder.forAddress(host, port);
	    try {
		return grpcSslConfigurer.configureSsl(nettyChannelBuilder);
	    }
	    catch (SSLException e) {
		throw new RuntimeException(e);
	    }
        }
    }
}

Furthermore, there's no logic that call shutdown() ManagedChannel.

2023-11-03T12:12:05.152+09:00 ERROR 6277 --- [ctor-http-nio-3] i.g.i.ManagedChannelOrphanWrapper        : 
    *~*~*~ Previous channel ManagedChannelImpl{logId=7, target=localhost:8081} was not shutdown properly!!! ~*~*~*
        Make sure to call shutdown()/shutdownNow() and wait until awaitTermination() returns true.

Describe the solution you'd like So here below is my suggestion using ConcurrentHashMap to recycle ManagedChannels. (or add something like lifecycle to manages all ManageChannels?)

Describe alternatives you've considered

package org.springframework.cloud.gateway.filter.factory;

public class JsonToGrpcGatewayFilterFactory
        extends AbstractGatewayFilterFactory<JsonToGrpcGatewayFilterFactory.Config> {
    // a container for ManagedChannel. key = host + ":" + port
    private final ConcurrentHashMap<String, ManagedChannel> ManagedChannelContainer = new ConcurrentHashMap<>();
    ... ...
    class GRPCResponseDecorator extends ServerHttpResponseDecorator {

    ... ...
        // get ManageChannel from ManageChannelContainer and if it exists, return that value(ManageChannel);
        // if it doesn't exist, create ManageChannel and put it in ManageChannelContainer and return it.
        private ManagedChannel createChannelChannel(String host, int port) {
	    String key = host + ":" + port;
            ManagedChannel managedChannel =  ManagedChannelContainer.get(key);

            if (managedChannel == null) {
                NettyChannelBuilder nettyChannelBuilder = NettyChannelBuilder.forAddress(host, port);
                try {
                    managedChannel = grpcSslConfigurer.configureSsl(nettyChannelBuilder);
                    ManagedChannelContainer.put(key, managedChannel);
                    return managedChannel;
                }
                catch (SSLException e) {
                    throw new RuntimeException(e);
                }
            } else {
                return managedChannel;
            }
        }
    }
}

Additional context build.gradle

// includes spring-cloud-gateway-server:4.0.7
implementation 'org.springframework.cloud:spring-cloud-gateway-starter:4.0.7'

nsce9806q avatar Nov 03 '23 05:11 nsce9806q