Skip to content

Recycling ManagedChannel to prevent creation on every call in JsonToGrpcGatewayFilterFactory #3120

@nsce9806q

Description

@nsce9806q

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'

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions