线上问题
有没有使用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测试
连接与执行状态相对稳定,不会有持续增长情况。
对比restTemplate方式性能略有提升
测试为复杂对象传输,在简单对象传入,推荐restTemplate。
如果需要进行大数据传输、高并发请求和多语言通信,可以选择使用 gRPC,如果需要对 RESTful 服务进行简单的调用和数据传输,则可以选择使用 RestTemplate。
也可以用:
@GrpcClient("rule")
private RuleGrpc.RuleBlockingStub stub;
本质是一样。