文章目录

  • 线上问题
  • channelStatePool(配置工厂)
  • 业务调用
  • Postman测试


线上问题

有没有使用Grpc连接池的必要?
排查线上网页环境突然变卡原因:
1、k8s集群环境 cpu内存资源
2、查看jvm有没有相关大出现大量full gc
3、排查网络连接是否在TIME_WAIT中出现大量连接阻塞问题(半连接队列阻塞)http1.1为应用头阻塞,在没有收到response时tcp不能被复用
http2.0避免应用头阻塞,无法避免tcp层头阻塞,在socket buff中有容量限制。

channelStatePool(配置工厂)

@Component
public class ChannelStatePool {
    private ObjectPool<ManagedChannel> connectionPool;

    /**
     * 创建连接池并配置相关属性
     */
    public ChannelStatePool() {
        // 构建连接池工厂类
        BasePooledObjectFactory<ManagedChannel> factory = new BasePooledObjectFactory<ManagedChannel>() {

            @Override
            public ManagedChannel create() throws Exception {
                // 创建GRPC客户端连接对象
                return ManagedChannelBuilder.forAddress("localhost", 9090)
                        .usePlaintext()
                        .build();
            }

            @Override
            public PooledObject<ManagedChannel> wrap(ManagedChannel channel) {
                return new DefaultPooledObject<>(channel);
            }

            @Override
            public void destroyObject(PooledObject<ManagedChannel> p) throws Exception {
                // 关闭连接对象
                p.getObject().shutdown();
            }

            @Override
            public boolean validateObject(PooledObject<ManagedChannel> p) {
                // 检查连接对象是否可用
                return !p.getObject().isShutdown();
            }
        };
        final GenericObjectPoolConfig objectPoolConfig = new GenericObjectPoolConfig();
        // 连接池的配置
        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
        // 池中的最大连接数
        poolConfig.setMaxTotal(50);
        // 最少的空闲连接数
        poolConfig.setMinIdle(1);
        // 最多的空闲连接数
        poolConfig.setMaxIdle(4);
        // 当连接池资源耗尽时,调用者最大阻塞的时间,超时时抛出异常 单位:毫秒数
        poolConfig.setMaxWaitMillis(5000);
        // 连接池存放池化对象方式,true放在空闲队列最前面,false放在空闲队列最后
        poolConfig.setLifo(true);
        // 连接空闲的最小时间,达到此值后空闲连接可能会被移除,默认即为30分钟
        poolConfig.setMinEvictableIdleTimeMillis(1000L * 60L * 30L);
        // 连接耗尽时是否阻塞,默认为true
        poolConfig.setBlockWhenExhausted(true);
        this.connectionPool = new GenericObjectPool<>(factory, objectPoolConfig);
    }

    public ManagedChannel borrowConnect() throws Exception {
        // 从连接池中获取连接对象
        return connectionPool.borrowObject();
    }

    public void returnConnect(ManagedChannel channel) throws Exception {
        // 将连接对象归还到连接池中
        connectionPool.returnObject(channel);
    }

    /**
     * 关闭连接池
     */
    public void close() throws Exception {
        connectionPool.close();
    }
}

交给spring管理后续只需调用borrowConnect()取连接复用即可。

业务调用

ManagedChannel channel = null;
        try {
            channel = channelStatePool.borrowConnect();
            RuleGrpc.RuleBlockingStub stub1 = RuleGrpc.newBlockingStub(channel);
            // 调用GRPC API方法
            double fee = stub1.charge(request).getFee();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 归还连接对象
            if (channel != null) {
                channelStatePool.returnConnect(channel);
            }
        }

Postman测试

java服务端grpc设置连接池开启并发处理_压力测试


连接与执行状态相对稳定,不会有持续增长情况。对比restTemplate方式性能略有提升

java服务端grpc设置连接池开启并发处理_压力测试_02


测试为复杂对象传输,在简单对象传入,推荐restTemplate。

如果需要进行大数据传输、高并发请求和多语言通信,可以选择使用 gRPC,如果需要对 RESTful 服务进行简单的调用和数据传输,则可以选择使用 RestTemplate。

也可以用:

@GrpcClient("rule")
private RuleGrpc.RuleBlockingStub stub;

本质是一样。