gRPC服务Cannot assign requested address问题解决及优化
原创
©著作权归作者所有:来自51CTO博客作者lemon时雨的原创作品,谢绝转载,否则将追究法律责任
新上线的grpc服务在上线几天后突然出现大量Cannot assign requested address错误,导致服务无法访问
问题原因
由于客户端频繁的连服务器,由于每次连接都在很短的时间内结束,导致很多的TIME_WAIT,以至于用光了服务端可用的端口号,所以新的连接没办法绑定端口,即“Cannot assign requested address”。
解决方案
同时重启客户端和服务端。
如果只重启客户端或服务端,很可能会因为一端重启时产生链接超时,进而出现context deadline exceeded报错。
优化思路
1.系统层面
1.通过修改以下系统参数来提高tcp链接的回收效率(/etc/sysctl.conf):
##调低端口释放后的等待时间,默认为60s,修改为15~30s:
sysctl -w net.ipv4.tcp_fin_timeout=30
##修改tcp/ip协议配置, 通过配置/proc/sys/net/ipv4/tcp_tw_resue, 默认为0,修改为1,释放TIME_WAIT端口给新连接使用:
sysctl -w net.ipv4.tcp_timestamps=1
##修改tcp/ip协议配置,快速回收socket资源,默认为0,修改为1:
sysctl -w net.ipv4.tcp_tw_recycle=1
##允许端口重用:
sysctl -w net.ipv4.tcp_tw_reuse = 1
2.通过修改以下系统参数来提高系统可分配端口数量:
查看可用端口数
cat /proc/sys/net/ipv4/ip_local_port_range
修改端口范围(vim /etc/sysctl.conf文件),添加如下内容
执行sysctl -p命令使其生效
2.代码层面
通过检查代码发现,客户端与服务端建立链接时是每一个请求创建一个client,导致请求量过大时出现端口耗尽的问题
func NewDeployService() (*DeployService, error) {
//todo complete service discovery
matrixPipelineConfig, err := conf.GlobalConf.Value(MatrixPipelineConfig).Map()
if err != nil {
return nil, tracerr.Wrap(err)
}
endpoint, err := matrixPipelineConfig[MatrixPipelineEndpoint].String()
if err != nil {
return nil, tracerr.Wrap(err)
}
timeout, err := matrixPipelineConfig[MatrixPipelineTimeout].Int()
if err != nil {
return nil, tracerr.Wrap(err)
}
cc, err := grpc.DialInsecure(context.Background(), grpc.WithEndpoint(endpoint), grpc.WithTimeout(time.Duration(timeout*1000*1000*1000)))
if err != nil {
return nil, err
}
//cc, err := grpc.DialInsecure(context.Background(), grpc.WithEndpoint("127.0.0.1:9000"), grpc.WithTimeout(time.Duration(10*1000*1000*1000)))
//if err != nil {
// return nil, err
//}
return &DeployService{
MatrixPipelineClient: pipeline.NewPipelineClient(cc),
}, nil
}
针对此问题,可以在发起请求时,创建一个全局的client,因为gRPC底层使用的是http2协议,天然支持tcp多路复用,从而大大减少因频繁创建client导致端口耗尽的问题
var GlobalDeployService *DeployService
func NewDeployService() (*DeployService, error) {
if GlobalDeployService != nil {
return GlobalDeployService, nil
}
//todo complete service discovery
matrixPipelineConfig, err := conf.GlobalConf.Value(MatrixPipelineConfig).Map()
if err != nil {
return nil, tracerr.Wrap(err)
}
endpoint, err := matrixPipelineConfig[MatrixPipelineEndpoint].String()
if err != nil {
return nil, tracerr.Wrap(err)
}
timeout, err := matrixPipelineConfig[MatrixPipelineTimeout].Int()
if err != nil {
return nil, tracerr.Wrap(err)
}
cc, err := grpc.DialInsecure(context.Background(), grpc.WithEndpoint(endpoint), grpc.WithTimeout(time.Duration(timeout*1000*1000*1000)))
if err != nil {
return nil, err
}
//cc, err := grpc.DialInsecure(context.Background(), grpc.WithEndpoint("127.0.0.1:9000"), grpc.WithTimeout(time.Duration(10*1000*1000*1000)))
//if err != nil {
// return nil, err
//}
GlobalDeployService = &DeployService{
MatrixPipelineClient: pipeline.NewPipelineClient(cc),
}
return GlobalDeployService, nil
}