前言:
grpc-go提供了peer库可以获取客户端地址,如果无网关及代理的情况下,通过peer是可以直接拿到对端的ip地址,反之有网关代理,那么拿到的地址非真实客户端,而是对端的直连地址。
实现:
无网关代理时,可使用GetPeerAddr()获取。如有代理需要在代理转发时把代理对端的ip给塞到header的x-real-ip头里,这个过程其实跟nginx操作X-Real-IP和X-Forwarded-For一样的。可做grpc网关的服务有很多,比如nginx、traefik、envoy。下面拿nginx grpc配置为例。
// xiaorui.cc
server {
listen 9099 http2;
access_log /var/log/nginx/xiaorui.cc.log;
location / {
grpc_pass grpc://127.0.0.1:9091;
grpc_set_header X-Real-IP $remote_addr;
}
}
使用metadata从header里获取x-real-ip。
// xiaorui.cc
// GetRealAddr get real client ip
func GetRealAddr(ctx context.Context) string {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return ""
}
rips := md.Get("x-real-ip")
if len(rips) == 0 {
return ""
}
return rips[0]
}
// GetPeerAddr get peer addr
func GetPeerAddr(ctx context.Context) string {
var addr string
if pr, ok := peer.FromContext(ctx); ok {
if tcpAddr, ok := pr.Addr.(*net.TCPAddr); ok {
addr = tcpAddr.IP.String()
} else {
addr = pr.Addr.String()
}
}
return addr
}
总结:
x-forwared-for在web场景下很常见,grpc下想不到场景。 😅 通常为了优化web服务,加入多级缓存及负载均衡,这些节点都有可能追加x-forwared-for头。