新上线的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文件),添加如下内容

/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
}