v1.9.3
릴리스 날짜: 2025-11-16 — GitHub 릴리스
go-zero v1.9.3 릴리스를 소개합니다. 이번 릴리스에는 framework의 안정성, 성능, 업계 권장 방식과의 정합성을 높이는 여러 중요한 개선과 버그 수정이 포함되어 있습니다.
🎉 주요 내용
섹션 제목: “🎉 주요 내용”- Consistent Hash 부하 분산: 세션 고정을 위한 새로운 gRPC 부하 분산기를 추가했습니다.
- gRPC 권장 방식 반영: gRPC 권장 사항에 맞춰
NonBlock기본값을 변경했습니다. - 분산 추적 개선: gateway trace header 전파 문제를 수정했습니다.
- ORM 개선: pointer 대상에 zero value를 scan할 때의 문제를 수정했습니다.
✨ 새 기능
섹션 제목: “✨ 새 기능”Consistent Hash Balancer 지원 (#5246)
섹션 제목: “Consistent Hash Balancer 지원 (#5246)”기여자: @zhoushuguang
zRPC 패키지에 새로운 consistent hash 부하 분산기를 추가했습니다. 이를 통해 gRPC 서비스에서 세션 고정을 사용할 수 있습니다.
주요 기능:
- 세션 고정을 유지하기 위한 hash 기반 요청 라우팅
- context의 hash key를 기준으로 요청 분산
- node 변경 시 request 재분배 최소화
- go-zero의 기존
core/hash/ConsistentHash구현 기반
사용 예제:
// Set hash key in contextctx := zrpc.SetHashKey(ctx, "user_123")
// Requests with the same key will be routed to the same backendresp, err := client.SomeMethod(ctx, req)설정:
c := zrpc.RpcClientConf{ Endpoints: []string{"localhost:8080", "localhost:8081"}, BalancerName: "consistent_hash", // Use consistent hash balancer}장점:
- stateful service interaction을 사용할 수 있습니다.
- backend service의 cache hit rate를 높입니다.
- session data synchronization overhead를 줄입니다.
- affinity를 지원하면서도 load distribution을 유지합니다.
🐛 버그 수정
섹션 제목: “🐛 버그 수정”Gateway Trace Header 수정 (#5256, #5248)
섹션 제목: “Gateway Trace Header 수정 (#5256, #5248)”기여자: @kevwan
gateway를 통해 upstream gRPC 서비스로 전달되어야 하는 OpenTelemetry 추적 전파 header가 제대로 전달되지 않아 분산 추적이 끊기던 문제를 수정했습니다.
문제:
gateway가 중요한 W3C Trace Context header(traceparent, tracestate, baggage)를 gRPC metadata로 전달하지 않아, gateway 경계에서 trace context가 유실되었습니다.
해결:
ProcessHeaders함수를 개선해 trace propagation header를 전달하도록 했습니다.- gRPC metadata 규칙에 맞춰 header를 올바르게 lowercase로 변환합니다.
- HTTP → gRPC 경계를 넘어 distributed tracing을 유지합니다.
영향:
- gateway를 통과하는 end-to-end tracing이 올바르게 동작합니다.
- microservice architecture의 관측 가능성이 향상됩니다.
- debugging과 performance analysis가 더 쉬워집니다.
기술 세부 사항:
// Trace headers now properly forwardedvar traceHeaders = map[string]bool{ "traceparent": true, // W3C Trace Context "tracestate": true, // Additional trace state "baggage": true, // W3C Baggage propagation}Trace 중복 초기화 수정 (#5244)
섹션 제목: “Trace 중복 초기화 수정 (#5244)”기여자: @kevwan
문제: 같은 process 안에서 여러 서비스(예: REST + RPC)를 실행할 때 trace agent가 여러 번 초기화될 수 있었습니다. 이로 인해 resource leak이나 예기치 않은 동작이 발생할 수 있었습니다.
해결:
sync.Once를 사용해 trace agent가 한 번만 초기화되도록 했습니다.prometheus.StartAgent,logx.SetUp에서 사용하는 방식과 일관성을 맞췄습니다.- shutdown에도
sync.OnceFunc를 추가해 cleanup이 중복 실행되지 않도록 했습니다.
코드 변경:
var ( once sync.Once shutdownOnceFn = sync.OnceFunc(func() { if tp != nil { _ = tp.Shutdown(context.Background()) } }))
func StartAgent(c Config) { if c.Disabled { return }
once.Do(func() { if err := startAgent(c); err != nil { logx.Error(err) } })}장점:
- multi-server process에서 resource conflict를 방지합니다.
- global tracer provider instance가 하나만 생성되도록 보장합니다.
- concurrent initialization이 더 안전해집니다.
- shutdown 시 cleanup이 올바르게 수행됩니다.
ORM pointer 대상의 zero value scanning 수정 (#5270)
섹션 제목: “ORM pointer 대상의 zero value scanning 수정 (#5270)”기여자: @lerity-yao(첫 기여입니다! 🎊)
문제: database 결과를 pointer type의 struct field로 scan할 때 zero value(0, false, 빈 문자열)와 NULL 값을 제대로 구분하지 못했습니다. 그 결과 nil pointer가 잘못 zero value로 설정되는 문제가 있었습니다.
해결:
getValueInterface 함수를 개선해 scan 전에 nil pointer를 올바르게 초기화하도록 했습니다. 이를 통해 SQL driver가 zero value와 non-zero value를 정확히 채울 수 있습니다.
코드 변경:
func getValueInterface(value reflect.Value) (any, error) { if !value.CanAddr() || !value.Addr().CanInterface() { return nil, ErrNotReadableValue }
// Initialize nil pointer before scanning if value.Kind() == reflect.Pointer && value.IsNil() { baseValueType := mapping.Deref(value.Type()) value.Set(reflect.New(baseValueType)) }
return value.Addr().Interface(), nil}영향:
type User struct { Name string `db:"name"` // Always set Age *int `db:"age"` // Can distinguish NULL vs 0 Active *bool `db:"active"` // Can distinguish NULL vs false}
// Before: age=0 and age=NULL both resulted in nil pointer// After: age=0 → *int(0), age=NULL → nil pointer ✓장점:
- NULL과 zero value를 올바르게 구분합니다.
- optional field의 의미를 더 정확하게 표현합니다.
- 예기치 않은 nil pointer dereference를 방지합니다.
- Go의 SQL scanning 권장 방식과 맞아집니다.
🔄 호환성에 영향을 줄 수 있는 변경(하위 호환성 유지)
섹션 제목: “🔄 호환성에 영향을 줄 수 있는 변경(하위 호환성 유지)”NonBlock 기본값을 true로 변경 (#5259)
섹션 제목: “NonBlock 기본값을 true로 변경 (#5259)”기여자: @kevwan
동기: blocking dial을 권장하지 않는 gRPC 공식 권장 사항에 맞추기 위한 변경입니다.
변경 사항:
type RpcClientConf struct { // Before: NonBlock bool `json:",optional"` // After: NonBlock bool `json:",default=true"` // Now defaults to true}중요한 이유:
- Blocking dial은 deprecated된 방식입니다:
grpc.WithBlock()은 anti-pattern입니다. - Connection state는 동적입니다: dial 시점에 연결되어 있어도 이후 연결성이 보장되지는 않습니다.
- RPC가 대기를 처리합니다: 모든 RPC는 연결되거나 deadline에 도달할 때까지 자동으로 기다립니다.
- 코드가 단순해집니다: 호출 전에 “ready” 상태를 확인할 필요가 없습니다.
마이그레이션 가이드:
대부분의 사용자는 별도 조치가 필요 없습니다. 새로운 기본값이 권장 동작입니다.
명시적으로 blocking 동작이 필요한 경우(권장하지 않음):
// Option 1: Configurationc := zrpc.RpcClientConf{ NonBlock: false, // Explicit blocking}
// Option 2: Client option (deprecated)client := zrpc.MustNewClient(c, zrpc.WithBlock())하위 호환성:
NonBlock: false가 들어 있는 기존 설정은 계속 동작합니다.- 새로운
WithBlock()옵션을 사용할 수 있습니다(deprecated 표시). - 이미
NonBlock: true를 사용하는 서비스에는 변경이 필요 없습니다.
문서: 자세한 migration guide와 변경 이유는 gRPC non-block 변경 PR을 참고하세요.
👥 새로운 기여자
섹션 제목: “👥 새로운 기여자”go-zero community에 새로 합류한 기여자들을 환영합니다! 🎉
- @JackGod001 님이 #4343에서 첫 기여를 해 주셨습니다.
- @stemlaud 님이 #5245에서 첫 기여를 해 주셨습니다.
- @gfischer7 님이 #5254에서 첫 기여를 해 주셨습니다.
- @lerity-yao 님이 #5270에서 첫 기여를 해 주셨습니다(ORM 수정).
기여해 주셔서 감사합니다. 앞으로도 프로젝트에서 계속 함께하기를 기대합니다.
📦 설치 및 업그레이드
섹션 제목: “📦 설치 및 업그레이드”go get -u github.com/zeromicro/go-zero@v1.9.3업데이트
섹션 제목: “업데이트”# Update go.modgo get -u github.com/zeromicro/go-zero@v1.9.3go mod tidy🔗 링크
섹션 제목: “🔗 링크”- 전체 변경 로그: https://github.com/zeromicro/go-zero/compare/v1.9.2…v1.9.3
- 문서: https://go-zero.dev
- GitHub 저장소: https://github.com/zeromicro/go-zero
📝 상세 변경 사항
섹션 제목: “📝 상세 변경 사항”Core 개선
섹션 제목: “Core 개선”부하 분산
섹션 제목: “부하 분산”- gRPC용 consistent hash balancer를 추가했습니다(
zrpc/internal/balancer/consistenthash). - context 기반 hash key API인
SetHashKey()와GetHashKey()를 추가했습니다. - replica count와 hash function을 설정할 수 있습니다.
- 포괄적인 test coverage를 추가했습니다.
분산 추적
섹션 제목: “분산 추적”- gateway에서 trace header propagation을 수정했습니다.
- W3C Trace Context header를 올바르게 처리합니다.
- gRPC 규칙에 맞춰 header를 대소문자 구분 없이 matching합니다.
sync.Oncepattern으로 단일 초기화를 보장합니다.
ORM/Database
섹션 제목: “ORM/Database”- pointer field에 zero value를 scan할 때의 문제를 수정했습니다.
- NULL과 zero value를 올바르게 구분합니다.
- nil pointer initialization을 포함하도록
getValueInterface()를 개선했습니다. sql.Null*type을 지원합니다.
gRPC Client
섹션 제목: “gRPC Client”NonBlock기본값을true로 변경했습니다.- 호환성을 위해 deprecated된
WithBlock()옵션을 추가했습니다. - blocking mode와 non-blocking mode를 명시적으로 처리합니다.
- client initialization logic을 업데이트했습니다.
Test 및 품질
섹션 제목: “Test 및 품질”- 모든 변경 사항에 대해 포괄적인 test coverage를 추가했습니다.
- ORM test에 edge case 처리를 추가했습니다.
- gateway trace header test case를 추가했습니다.
- consistent hash balancer benchmark를 추가했습니다.
🙏 감사 인사
섹션 제목: “🙏 감사 인사”이번 릴리스를 가능하게 해 준 모든 기여자, issue 제보자, community member에게 감사드립니다. 여러분의 feedback과 contribution 덕분에 go-zero가 계속 좋아지고 있습니다.
💬 Feedback
섹션 제목: “💬 Feedback”문제가 있거나 다음 릴리스를 위한 제안이 있다면 다음 경로로 알려 주세요.
go-zero와 함께 즐겁게 개발하세요! 🚀