티스토리 뷰

* 개인적으로 공부한 내용의 일부를 기록하기 위한 것이므로 중간 과정이 많이 생략되어 있습니다. *

 

작성중인 코드의 일부는 아래의 깃허브 저장소에서 확인하실 수 있습니다.

https://github.com/piatoss3612/go-grpc-todo

 

GitHub - piatoss3612/go-grpc-todo: example of grpc implementation

example of grpc implementation. Contribute to piatoss3612/go-grpc-todo development by creating an account on GitHub.

github.com

1. 메타데이터 읽기

메타데이터에 어떤 정보가 담겨있는지 먼저 확인해 봅니다.

package server

import (
	"context"
	"fmt"

	"golang.org/x/exp/slog"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata" // 메타데이터를 읽어오기 위해 사용하는 패키지
)

func TodoServerUnaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
	md, ok := metadata.FromIncomingContext(ctx) // 요청 context로부터 메타데이터 읽기
	
    // 메타데이터가 존재하는 경우
    if ok {
    	// 메타데이터 출력
		for k, v := range md {
			fmt.Println(k, v) 
		}
	}
    
    ...
}

gRPC 클라이언트에서 다이렉트로 요청한 경우의 메타데이터

 

프록시 서버를 통해 요청한 경우의 메타데이터

 

2. 메타데이터 추출 및 출력

어떤 정보들이 담겨있는지 확인했으니, 사용자 에이전트와 클라이언트 ip 정보만 가져와 보겠습니다.

func TodoServerUnaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
	var userAgent, clientIp string

	if md, ok := metadata.FromIncomingContext(ctx); ok {
    	// 사용자 에이전트 정보가 있는 경우에만
		if userAgents := md.Get("user-agent"); len(userAgents) > 0 {
			userAgent = userAgents[0]
		}
        
        // 게이트웨이 사용자 에이전트 정보가 있는 경우에는 사용자 에이전트 정보에 덮어쓴다
		if gwUserAgents := md.Get("grpcgateway-user-agent"); len(gwUserAgents) > 0 {
			userAgent = gwUserAgents[0]
		}

		// 클라이언트 ip 정보가 있는 경우에만
		if clientIps := md.Get("x-forwarded-for"); len(clientIps) > 0 {
			clientIp = clientIps[0]
		}
	}

	slog.Info("Request received", "method", info.FullMethod, "user-agent", userAgent, "client-ip", clientIp)

	...
}

 

gRPC 클라이언트에서 다이렉트로 요청한 경우의 메타데이터 출력
프록시 서버를 통해 요청한 경우의 메타데이터 출력

 

gRPC 클라이언트에서 직접 요청을 할 경우에는 메타데이터에 클라이언트의 ip주소가 포함되어 있지 않는데, 어디서 클라이언트의 ip주소를 가져와야 될까요?

 

3. gRPC 클라이언트의 IP 가져오기

package server

import (
	"context"
	"fmt"

	"golang.org/x/exp/slog"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/peer"
)

func TodoServerUnaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
	var userAgent, clientIp string

	if md, ok := metadata.FromIncomingContext(ctx); ok {
		if userAgents := md.Get("user-agent"); len(userAgents) > 0 {
			userAgent = userAgents[0]
		}

		// 클라이언트 정보 추출
		peer, ok := peer.FromContext(ctx)
		if ok {
			clientIp = peer.Addr.String()
		}

		if gwUserAgents := md.Get("grpcgateway-user-agent"); len(gwUserAgents) > 0 {
			userAgent = gwUserAgents[0]
		}

		if clientIps := md.Get("x-forwarded-for"); len(clientIps) > 0 {
			clientIp = clientIps[0]
		}
	}

	slog.Info("Request received", "method", info.FullMethod, "user-agent", userAgent, "client-ip", clientIp)

	resp, err := handler(ctx, req)
	if err != nil {
		slog.Error("Request failed", "method", info.FullMethod, "error", err)
		return resp, err
	}

	slog.Info("Send a message", "type", fmt.Sprintf("%T", resp))
	return resp, nil
}

gRPC 클라이언트에서 다이렉트로 요청한 경우의 메타데이터 출력

 

peer 패키지의 FromContext 함수를 사용해서 gRPC 요청 컨텍스트로부터 클라이언트의 ip 정보를 추출해낼 수 있습니다.

 

4. 메타데이터 추출 함수 정의

메타데이터를 추출하는 과정이 상당히 기므로 별개의 타입과 함수로 정의해 보았습니다.

package server

import (
	"context"

	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/peer"
)

type todoServerMetadata struct {
	userAgent string
	clientIp  string
}

func extractMetadata(ctx context.Context) todoServerMetadata {
	var userAgent, clientIp string

	if md, ok := metadata.FromIncomingContext(ctx); ok {
		if userAgents := md.Get("user-agent"); len(userAgents) > 0 {
			userAgent = userAgents[0]
		}

		peer, ok := peer.FromContext(ctx)
		if ok {
			clientIp = peer.Addr.String()
		}

		if gwUserAgents := md.Get("grpcgateway-user-agent"); len(gwUserAgents) > 0 {
			userAgent = gwUserAgents[0]
		}

		if clientIps := md.Get("x-forwarded-for"); len(clientIps) > 0 {
			clientIp = clientIps[0]
		}
	}

	return todoServerMetadata{userAgent: userAgent, clientIp: clientIp}
}

Reference

[Backend #44] How to extract info from gRPC metadata

'개발 부스러기' 카테고리의 다른 글

WSL에서 Go 사용하기  (0) 2023.12.26
Chainlink - Data Feeds  (0) 2023.10.31
바빌로니아 법 (The Babylonian Method)  (0) 2023.04.10
[Nginx] Reverse Proxy 설정  (0) 2023.04.03
[Trouble Shooting] net::ERR_NAME_NOT_RESOLVED  (0) 2023.04.02
최근에 올라온 글
최근에 달린 댓글
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Total
Today
Yesterday
글 보관함