Documentation
¶
Overview ¶
Package invocation 定义面向 service-to-service 调用模型的核心错误。
Index ¶
- Constants
- Variables
- func DefaultDialFunc(_ context.Context, target Target, options []grpc.DialOption) (*grpc.ClientConn, error)
- func ParseMetaKey(md metadata.MD, key string) (string, error)
- func WithUserContext(ctx context.Context, meta *UserContextMeta) context.Context
- type Authorizer
- type AuthzContext
- type Caller
- type ConnectionManager
- type ConnectionManagerOptions
- type ContextBuilder
- type DNSConfig
- type DNSManager
- type DialFunc
- type Dialer
- type InvocationContext
- type InvokeOption
- type InvokeOptions
- type Invoker
- type RemoteServiceCaller
- type ServiceDNS
- func (s ServiceDNS) ClusterDomainName() string
- func (s ServiceDNS) EffectivePort(defaultPort uint16) (uint16, error)
- func (s ServiceDNS) NamespaceName() string
- func (s ServiceDNS) ServiceName() string
- func (s ServiceDNS) ServiceTypeName() string
- func (s ServiceDNS) Validate() error
- func (s ServiceDNS) WithPort(port uint16) ServiceDNS
- type Target
- type UnaryInvokeFunc
- type UnaryInvoker
- type UserContextMeta
Constants ¶
const ( // DefaultServiceType 是 Kubernetes Service FQDN 中的固定服务类型片段。 DefaultServiceType = "svc" // DefaultClusterDomain 是 Kubernetes 集群默认的 Cluster Domain。 DefaultClusterDomain = "cluster.local" // DefaultResolverScheme 是 gRPC 默认推荐使用的 DNS resolver scheme。 DefaultResolverScheme = "dns" // DefaultServicePort 是业务服务默认使用的 gRPC 端口。 DefaultServicePort = 9090 )
const MetaKeyParseErrorFormat = "%s 解析失败"
Variables ¶
var ( // ErrServiceNameEmpty 表示服务名为空,无法构造逻辑服务身份。 ErrServiceNameEmpty = errors.New("service name is empty") // ErrNamespaceEmpty 表示命名空间为空。 ErrNamespaceEmpty = errors.New("namespace is empty") // ErrTargetHostEmpty 表示目标主机为空,无法生成最终拨号地址。 ErrTargetHostEmpty = errors.New("target host is empty") // ErrTargetPortInvalid 表示端口既未显式提供,也无法从默认值中补齐。 ErrTargetPortInvalid = errors.New("target port is invalid") // ErrDNSManagerIsNil 表示 DNS 管理器为空。 ErrDNSManagerIsNil = errors.New("dns manager is nil") // ErrDialFnIsNil 表示底层拨号函数为空。 ErrDialFnIsNil = errors.New("dial function is nil") // ErrConnectionManagerClosed 表示连接管理器已经关闭,不能再创建新连接。 ErrConnectionManagerClosed = errors.New("connection manager is closed") // ErrInvokerDialerIsNil 表示调用器缺少拨号器依赖。 ErrInvokerDialerIsNil = errors.New("invoker dialer is nil") // ErrInvokeMethodEmpty 表示调用方法名为空。 ErrInvokeMethodEmpty = errors.New("invoke method is empty") )
Functions ¶
func DefaultDialFunc ¶
func DefaultDialFunc(_ context.Context, target Target, options []grpc.DialOption) (*grpc.ClientConn, error)
DefaultDialFunc 是默认的 grpc.ClientConn 创建逻辑。
当前实现采用 grpc.NewClient,并默认启用: - insecure credentials:便于在内部受控网络中快速起步; - otelgrpc client handler:保证调用链路自动接入 OTel。
后续若需要在具体实现中启用 mTLS、自定义 resolver 或更多 dial option, 可以通过 ConnectionManagerOptions 覆盖该行为。
func WithUserContext ¶ added in v1.3.4
func WithUserContext(ctx context.Context, meta *UserContextMeta) context.Context
WithUserContext 将 UserContextMeta 存入 context。
通常在 gRPC server interceptor 中调用,解析一次后存入 context, 后续 handler 可以直接通过 UserContextFromContext 获取,无需重复解析。
示例:
func UserContextInterceptor() grpc.UnaryServerInterceptor {
return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
md, ok := metadata.FromIncomingContext(ctx)
if ok {
userMeta, err := invocation.ParseUserContextMeta(md)
if err == nil {
ctx = invocation.WithUserContext(ctx, userMeta)
}
}
return handler(ctx, req)
}
}
Types ¶
type Authorizer ¶
type Authorizer interface {
Authorize(ctx context.Context, input *AuthzContext) error
}
Authorizer 定义外挂 Authz 的最小能力集合。
返回 nil 表示允许调用; 返回非 nil error 表示拒绝调用或 Authz 本身发生错误。
type AuthzContext ¶
type AuthzContext struct {
// Service 表示目标业务服务的 DNS 身份。
Service *ServiceDNS `json:"service"`
// FullMethod 表示完整 gRPC 方法,例如 /acme.user.v1.UserService/GetUser。
FullMethod string `json:"full_method"`
// TraceID 表示链路 ID。
TraceId string `json:"trace_id"`
// Caller 表示调用方身份。
Caller Caller `json:"caller"`
// Metadata 表示构造判定时附带的完整 metadata 副本。
Metadata metadata.MD `json:"-"`
}
AuthzContext 表示外挂 Authz 所需的标准化输入。
该对象不直接绑定某个 Authz 实现, 只表达“做权限判断时必须稳定得到的字段”。
func NewAuthzContext ¶
func NewAuthzContext(service *ServiceDNS, method string, invocation *InvocationContext) *AuthzContext
NewAuthzContext 根据 ServiceDNS、方法名和 InvocationContext 生成标准 AuthzContext。
type Caller ¶
type Caller struct {
// UserId 表示当前用户身份,可为空。
UserId string `json:"user_id"`
// AppId 表示当前应用身份,可为空。
AppId string `json:"app_id"`
// TenantId 表示当前租户身份,可为空。
TenantId string `json:"tenant_id"`
// OrgIds 表示当前组织范围,可为空。
OrgIds []string `json:"org_ids"`
// RoleIds 表示当前角色范围,可为空。
RoleIds []string `json:"role_ids"`
}
Caller 表示一次服务调用的发起方身份。
这里聚焦的是调用治理与 Authz 所需的统一字段, 避免让每个服务都手工拼装一套不同的 metadata。
type ConnectionManager ¶
type ConnectionManager struct {
// contains filtered or unexported fields
}
ConnectionManager 负责缓存基于 ServiceDNS 创建出的 grpc.ClientConn。
它把“业务服务 DNS -> 目标解析 -> 连接缓存”统一收敛在一处, 让业务层无需关心: - target 拼装; - resolver scheme; - 拨号选项; - 多次调用的连接复用。
func NewConnectionManager ¶
func NewConnectionManager(options ConnectionManagerOptions) (*ConnectionManager, error)
NewConnectionManager 创建连接管理器。
func (*ConnectionManager) Close ¶
func (m *ConnectionManager) Close() error
Close 关闭连接管理器及其持有的全部 grpc.ClientConn。
Close 会尽最大努力关闭所有连接; 若中途出现错误,当前实现返回第一条错误并继续关闭剩余连接。
func (*ConnectionManager) Dial ¶
func (m *ConnectionManager) Dial(ctx context.Context, service *ServiceDNS) (*grpc.ClientConn, error)
Dial 根据 ServiceDNS 获取或创建对应的 grpc.ClientConn。
连接缓存键采用最终 gRPC target,而不是 ServiceDNS 原始字段, 这样可以保证: - 逻辑上等价的服务身份只会生成一条连接; - 端口覆盖、cluster domain、resolver scheme 的变化都能体现在缓存键上。
type ConnectionManagerOptions ¶
type ConnectionManagerOptions struct {
// DNSManager 用于把业务服务 DNS 描述解析为最终 Target。
DNSManager *DNSManager
// DialFunc 用于创建新的 grpc.ClientConn。
// 若为空,则使用默认拨号实现。
DialFunc DialFunc
// DialOptions 表示创建 grpc.ClientConn 时使用的附加选项。
DialOptions []grpc.DialOption
}
ConnectionManagerOptions 定义连接管理器的配置。
type ContextBuilder ¶ added in v1.3.8
type ContextBuilder func(context.Context) *InvocationContext
ContextBuilder 定义“如何为一次远程业务服务调用构造 InvocationContext”。
之所以把它抽成函数,是为了让业务侧可以把: - metadata 透传 - caller 信息提取 - timeout 补齐
收敛到一个统一入口,而不是每个 repo 方法里重复写一遍。
type DNSConfig ¶ added in v1.3.7
type DNSConfig struct {
// DefaultNamespace 表示默认命名空间。
DefaultNamespace string
// DefaultServiceType 表示默认服务类型片段。
DefaultServiceType string
// DefaultClusterDomain 表示默认集群域名。
DefaultClusterDomain string
// DefaultPort 表示默认端口。
DefaultPort uint16
// ResolverScheme 表示默认 gRPC resolver scheme。
ResolverScheme string
}
DNSConfig 定义标准 DNS 管理器的默认行为。
type DNSManager ¶ added in v1.3.7
type DNSManager struct {
// contains filtered or unexported fields
}
DNSManager 负责把结构化的 ServiceDNS 转成最终 Target。
它只做一件事:组装标准 DNS。 它不做实例发现、不做节点选择,也不做后端适配。
func NewDNSManager ¶ added in v1.3.7
func NewDNSManager(config *DNSConfig) *DNSManager
NewDNSManager 创建一个标准 DNS 管理器。
func (*DNSManager) Build ¶ added in v1.3.7
func (m *DNSManager) Build(service *ServiceDNS) (*Target, error)
Build 根据 ServiceDNS 构造最终 Target。
func (*DNSManager) Config ¶ added in v1.3.7
func (m *DNSManager) Config() *DNSConfig
Config 返回当前管理器的规范化配置副本。
func (*DNSManager) Normalize ¶ added in v1.3.7
func (m *DNSManager) Normalize(service *ServiceDNS) *ServiceDNS
Normalize 用默认配置补齐业务服务 DNS。
type DialFunc ¶
type DialFunc func(ctx context.Context, target Target, options []grpc.DialOption) (*grpc.ClientConn, error)
DialFunc 表示底层拨号函数。
把拨号过程抽象成函数有两个目的: 1. 让 ConnectionManager 在不依赖具体后端的情况下复用连接; 2. 让单元测试可以替换真实拨号逻辑,避免触发真实网络连接。
type Dialer ¶
type Dialer interface {
Dial(ctx context.Context, service *ServiceDNS) (*grpc.ClientConn, error)
Close() error
}
Dialer 定义“如何把业务服务 DNS 变成 grpc.ClientConn”。
在新模型中,Dialer 只关心: - 标准 DNS target 组装结果; - 连接复用; - gRPC 连接创建。
type InvocationContext ¶
type InvocationContext struct {
// TraceId 表示当前调用使用的链路 ID。
TraceId string `json:"trace_id"`
// Caller 表示调用者身份。
Caller Caller `json:"caller"`
// Metadata 表示调用方额外附带的 metadata。
// 这里允许业务侧做补充,但核心库仍会统一注入标准字段。
Metadata metadata.MD `json:"-"`
// Timeout 表示本次调用的超时时间。
Timeout time.Duration `json:"timeout"`
}
InvocationContext 表示一次调用附带的统一上下文。
它的职责不是替代 context.Context, 而是把“应当被稳定传递和审计的调用元信息”沉淀为一个结构化对象。
func BuildInvocationContextFromContext ¶ added in v1.3.9
func BuildInvocationContextFromContext(ctx context.Context) *InvocationContext
BuildInvocationContextFromContext 从当前 context 构造一份标准 InvocationContext。
这是当前推荐的默认上下文构造逻辑: - 透传 incoming/outgoing metadata; - 透传已解析好的用户上下文; - 为后续 trace / request-id 收口保留统一入口。
若业务侧没有特殊定制需求,可以直接把该函数作为 RemoteServiceCaller 的 BuildContext。
func (InvocationContext) BuildMetadata ¶
func (c InvocationContext) BuildMetadata() metadata.MD
BuildMetadata 将 InvocationContext 转换为标准 gRPC metadata。
约定: - 调用方显式提供的 Metadata 会被保留; - 标准字段由核心库统一写入,避免上层重复设置; - 对于切片字段,会整体覆盖为当前上下文中的值。
func (InvocationContext) Clone ¶
func (c InvocationContext) Clone() InvocationContext
Clone 返回 InvocationContext 的深拷贝。 该方法用于避免调用方复用同一个 metadata 引起数据串扰。
func (InvocationContext) NewOutgoingContext ¶
func (c InvocationContext) NewOutgoingContext(parent context.Context) (context.Context, context.CancelFunc)
NewOutgoingContext 基于父上下文构造新的 gRPC 出站上下文。
行为说明: - 若 parent 为 nil,则使用 Background; - metadata 会统一写入 outgoing context; - 若 Timeout > 0,则自动附加超时控制。
type InvokeOption ¶
type InvokeOption func(*InvokeOptions)
InvokeOption 表示单个调用选项。
func WithCallOptions ¶
func WithCallOptions(callOptions ...grpc.CallOption) InvokeOption
WithCallOptions 追加底层 gRPC CallOption。
func WithInvocationContext ¶
func WithInvocationContext(invocation *InvocationContext) InvokeOption
WithInvocationContext 设置本次调用使用的 InvocationContext。
type InvokeOptions ¶
type InvokeOptions struct {
// InvocationContext 表示本次调用要附带的统一上下文。
InvocationContext *InvocationContext
// CallOptions 表示附加的 gRPC CallOption。
CallOptions []grpc.CallOption
}
InvokeOptions 表示一次调用的附加配置。
type Invoker ¶
type Invoker interface {
Invoke(ctx context.Context, service *ServiceDNS, method string, req any, resp any, options ...InvokeOption) error
}
Invoker 定义统一调用入口。
与业务代码直接操作 grpc.ClientConn 不同, Invoker 允许框架在调用前统一完成: - 业务服务 DNS 目标解析; - metadata 注入; - Authz 预检查; - 底层连接复用。
type RemoteServiceCaller ¶ added in v1.3.8
type RemoteServiceCaller struct {
// Service 表示当前远程业务服务的标准 DNS 身份。
Service *ServiceDNS
// Invoker 负责统一的连接获取、metadata 注入、Authz 预检查和实际调用。
Invoker *UnaryInvoker
// BuildContext 用于把业务侧 context 收敛成标准 InvocationContext。
// 若为空,则默认使用零值 InvocationContext。
BuildContext ContextBuilder
}
RemoteServiceCaller 表示一个远程业务服务的通用调用入口。
这个对象绑定的是“远程业务服务”而不是“某个 proto 子服务”。 因此一个业务服务下多个 proto 子服务,应共用同一个 RemoteServiceCaller。
例如: - auth 业务服务
- AuthAppService
- AuthUserService
- AuthPermissionService
这些子服务都应共用一份: - ServiceDNS - ConnectionManager - UnaryInvoker
func NewRemoteServiceCaller ¶ added in v1.3.9
func NewRemoteServiceCaller(invoker *UnaryInvoker, service *ServiceDNS, buildContext ContextBuilder) *RemoteServiceCaller
NewRemoteServiceCaller 创建一个标准的远程业务服务调用器。
这个构造函数的目标是把业务侧最常见的装配模板统一收口: - 指定远程业务服务 DNS; - 指定统一复用的 UnaryInvoker; - 指定默认的 InvocationContext 构造逻辑。
func (*RemoteServiceCaller) Invoke ¶ added in v1.3.8
func (c *RemoteServiceCaller) Invoke(ctx context.Context, method string, req any, resp any, options ...InvokeOption) error
Invoke 对当前绑定的远程业务服务发起一次 unary 调用。
调用方只需要提供: - full method - request - response
其余通用逻辑由: - ServiceDNS - UnaryInvoker - ContextBuilder
统一处理。
type ServiceDNS ¶ added in v1.3.7
type ServiceDNS struct {
// Service 表示业务服务名,例如 auth。
Service string `json:"service"`
// Namespace 表示命名空间,例如 default。
Namespace string `json:"namespace"`
// ServiceType 表示服务类型片段,默认值为 svc。
ServiceType string `json:"service_type"`
// ClusterDomain 表示集群域,默认值为 cluster.local。
ClusterDomain string `json:"cluster_domain"`
// Port 表示业务服务监听端口,默认值为 9090。
Port uint16 `json:"port"`
}
ServiceDNS 表示一个业务服务的标准 DNS 配置。
这里表达的是“这个业务服务在网络上的稳定入口”, 而不是“这个服务当前有哪些实例”。
例如: - service: auth - namespace: default - service_type: svc - cluster_domain: cluster.local - port: 9090
最终会得到: - host: auth.default.svc.cluster.local - address: auth.default.svc.cluster.local:9090
func (ServiceDNS) ClusterDomainName ¶ added in v1.3.7
func (s ServiceDNS) ClusterDomainName() string
ClusterDomainName 返回清理空格后的集群域名。
func (ServiceDNS) EffectivePort ¶ added in v1.3.7
func (s ServiceDNS) EffectivePort(defaultPort uint16) (uint16, error)
EffectivePort 返回当前 ServiceDNS 的最终端口。
func (ServiceDNS) NamespaceName ¶ added in v1.3.7
func (s ServiceDNS) NamespaceName() string
NamespaceName 返回清理空格后的命名空间。
func (ServiceDNS) ServiceName ¶ added in v1.3.7
func (s ServiceDNS) ServiceName() string
ServiceName 返回清理空格后的服务名。
func (ServiceDNS) ServiceTypeName ¶ added in v1.3.7
func (s ServiceDNS) ServiceTypeName() string
ServiceTypeName 返回清理空格后的服务类型。
func (ServiceDNS) Validate ¶ added in v1.3.7
func (s ServiceDNS) Validate() error
Validate 检查 ServiceDNS 是否具备生成稳定 DNS 的最小字段。
func (ServiceDNS) WithPort ¶ added in v1.3.7
func (s ServiceDNS) WithPort(port uint16) ServiceDNS
WithPort 返回带显式端口的新 ServiceDNS。
type Target ¶
type Target struct {
// ResolverScheme 表示 gRPC resolver scheme,例如 dns。
ResolverScheme string `json:"resolver_scheme"`
// Host 表示服务的标准主机名。
Host string `json:"host"`
// Port 表示服务端口。
Port uint16 `json:"port"`
}
Target 表示最终可拨号的 gRPC 服务目标。
它已经不表达实例列表,只表达一个稳定的服务入口。
func BuildTarget ¶
func BuildTarget(service *ServiceDNS, config *DNSConfig) (*Target, error)
BuildTarget 是兼容保留的辅助函数。
新代码建议直接使用 DNSManager.Build。
func (Target) GRPCTarget ¶
GRPCTarget 返回适合 grpc.NewClient 使用的 target 字符串。
type UnaryInvokeFunc ¶
type UnaryInvokeFunc func(ctx context.Context, conn *grpc.ClientConn, method string, req any, resp any, options ...grpc.CallOption) error
UnaryInvokeFunc 表示底层 unary 调用函数。
通过把真实调用抽象成函数, 可以让 UnaryInvoker 在测试中替换掉真实的 grpc.ClientConn.Invoke。
type UnaryInvoker ¶
type UnaryInvoker struct {
// Dialer 负责连接获取与复用。
Dialer Dialer
// Authorizer 是可选依赖;若为空,则跳过 Authz 预检查。
Authorizer Authorizer
// InvokeFunc 是可选依赖;若为空,则默认调用 grpc.ClientConn.Invoke。
InvokeFunc UnaryInvokeFunc
}
UnaryInvoker 是默认的 Invoker 实现。
它的执行流程非常明确: 1. 基于 ServiceDNS 获取连接; 2. 基于 InvocationContext 构造统一 metadata; 3. 在真正发起 gRPC 调用前执行 Authz 判定; 4. 使用 grpc.ClientConn.Invoke 发起 unary 调用。
func (*UnaryInvoker) Invoke ¶
func (u *UnaryInvoker) Invoke(ctx context.Context, service *ServiceDNS, method string, req any, resp any, options ...InvokeOption) error
Invoke 执行一次标准 unary 调用。
type UserContextMeta ¶ added in v1.3.4
type UserContextMeta struct {
Session string `json:"session"`
ClientIp string `json:"client_ip"`
UserId string `json:"user_id"`
AppId string `json:"app_id"`
TenantId string `json:"tenant_id"`
RoleIds []string `json:"role_ids"`
OrgIds []string `json:"org_ids"`
}
func MustUserContextFromContext ¶ added in v1.3.4
func MustUserContextFromContext(ctx context.Context) *UserContextMeta
MustUserContextFromContext 从 context 获取 UserContextMeta。
如果 context 中不存在 UserContextMeta,则 panic。 适用于明确知道 context 中一定有 UserContextMeta 的场景。
示例:
func Handler(ctx context.Context) error {
userMeta := invocation.MustUserContextFromContext(ctx)
log.Info("user request", "user_id", userMeta.UserId)
return nil
}
func ParseUserContextMeta ¶ added in v1.3.4
func ParseUserContextMeta(md metadata.MD) (raw *UserContextMeta, err error)
ParseUserContextMeta 从 metadata 解析用户上下文元信息。
推荐使用方式:
- 在 gRPC server interceptor 中调用 ParseUserContextMeta 解析一次
- 使用 WithUserContext 存入 context
- 后续使用 UserContextFromContext 直接获取
不推荐在每个 handler 中重复调用此函数。
示例:
// 在 interceptor 中解析一次
md, _ := metadata.FromIncomingContext(ctx)
userMeta, err := invocation.ParseUserContextMeta(md)
if err == nil {
ctx = invocation.WithUserContext(ctx, userMeta)
}
// 在 handler 中直接获取
userMeta, ok := invocation.UserContextFromContext(ctx)
func UserContextFromContext ¶ added in v1.3.4
func UserContextFromContext(ctx context.Context) (*UserContextMeta, bool)
UserContextFromContext 从 context 获取 UserContextMeta。
返回值:
- meta: UserContextMeta 指针,如果不存在则返回 nil
- ok: 是否成功获取
示例:
func Handler(ctx context.Context) error {
userMeta, ok := invocation.UserContextFromContext(ctx)
if !ok {
return errors.New("user context not found")
}
log.Info("user request", "user_id", userMeta.UserId)
return nil
}