grpc服务必须用clusterip + headless service:需设spec.clusterip: none、appprotocol: grpc,pod监听0.0.0.0,用grpc_health_probe做readiness探针,客户端使用dns:///前缀+round_robin策略,并收紧keepalive参数。

gRPC服务必须用ClusterIP + Headless Service
Kubernetes原生Service不支持gRPC健康探针和DNS直连,直接用NodePort或LoadBalancer会触发TLS协商失败或ALPN不匹配,导致客户端连接被重置。关键点是:type: ClusterIP + headless: true。Headless让DNS解析返回Pod IP列表,绕过kube-proxy的iptables/ipvs转发,避免HTTP/2连接复用中断;ClusterIP则确保服务不暴露到集群外,符合gRPC内部通信定位。
- Service YAML里必须显式写
spec.clusterIP: None来启用headless - 别在
ports里漏掉appProtocol: grpc字段,否则K8s无法识别协议类型,影响Ingress或Service Mesh路由 - Pod内gRPC server监听地址必须是
0.0.0.0:9000,不能是127.0.0.1:9000,否则kubelet从宿主机发起的readiness probe会超时
readinessProbe必须用grpc_health_v1.Health.Check
HTTP探针(比如GET /healthz)对gRPC服务无效:它只验证端口通不通,不校验gRPC server是否真正就绪。用HTTP探针会导致新Pod刚启动就被加入流量池,结果大量UNAVAILABLE错误。
- 服务端需注册
grpc_health_v1.NewHealthServer,并绑定到gRPC Server - Deployment中
readinessProbe.exec.command应调用grpc_health_probe二进制(如["/bin/grpc_health_probe", "-addr=:9000"]),而非curl - probe配置里
initialDelaySeconds至少设为5,给gRPC server留出TLS握手、中间件初始化时间
客户端连接必须禁用pick_first,改用dns:///前缀+round_robin
默认resolver策略pick_first只连DNS解析出的第一个IP,完全绕过Sidecar(如Istio)的负载均衡能力,在Service Mesh环境下等于单点部署。
Colly 是一个用于 Go 语言的快速开源爬取和爬虫框架。它适用于从简单的页面提取到异步爬虫处理大量页面集合,支持请求回调和结构化解析。
- 客户端Dial时必须用
dns:///service-name.namespace.svc.cluster.local:9000作为target - 显式设置
grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy":"round_robin"}`) - 若用
dns://(两个斜杠),gRPC会忽略round_robin配置,必须是dns:///(三个斜杠) - 别在Addrs里硬编码Pod IP——DNS解析失效后连接直接挂死,必须依赖Service名称做服务发现
KeepAlive参数必须收紧,否则TCP连接静默断开
默认keepalive.Time=2h太松,K8s节点重启、kube-proxy刷新规则、云LB空闲超时(常见300s)都会导致底层TCP连接被中间设备静默关闭,而gRPC没感知,后续请求抛connection reset by peer或UNAVAILABLE。
- 服务端加
grpc.KeepaliveParams(keepalive.ServerParameters{Time: 60 * time.Second, Timeout: 10 * time.Second}) - 客户端加
grpc.WithKeepaliveParams(keepalive.ClientParameters{Time: 60 * time.Second, Timeout: 10 * time.Second, PermitWithoutStream: true}),PermitWithoutStream必须为true,否则空闲连接不发ping - 别在
WithBlock()里卡住dial——它会阻塞整个goroutine,改用context.WithTimeout(ctx, 5*time.Second)控制建连超时
真正的坑不在“怎么写”,而在“谁在管连接生命周期”:gRPC的连接池、K8s的Pod驱逐、Sidecar的劫持、云LB的空闲超时,四层叠加下,任何一个环节没对齐,都会表现为偶发性连接失败。你得同时盯着代码里的grpc.Dial、YAML里的readinessProbe、Istio的DestinationRule和云厂商文档里的“TCP idle timeout”值。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










