如何正确将go-micro wrapper作为独立服务使用?以authWrapper为例
我刚好在项目里落地过类似的需求——把原本进程内的go-micro wrapper改成独立RPC服务,既支持服务发现,又能做到修改认证逻辑只重启单个服务,同时解决context传递的问题。下面给你详细拆解实现步骤:
一、封装独立的Auth服务
首先要把原本的authWrapper逻辑抽成独立的go-micro服务,核心是定义标准的RPC接口,让所有API服务能通过服务发现调用它:
1. 定义Auth服务的Proto文件
先写一个auth.proto,定义认证检查的接口,预留元数据传递的能力:
syntax = "proto3"; package auth; service Auth { rpc CheckAuth(CheckAuthRequest) returns (CheckAuthResponse); } message CheckAuthRequest { // 可按需添加显式参数,或完全依赖请求元数据 } message CheckAuthResponse { bool allowed = 1; string error_msg = 2; // 可扩展返回认证后的用户ID、权限组等信息 }
用go-micro工具生成对应Go代码:protoc --go_out=. --micro_out=. auth.proto
2. 实现Auth服务的业务逻辑
在Auth服务中,通过context.Context获取API服务传递的元数据,逻辑和原进程内wrapper完全一致:
package main import ( "context" "log" "github.com/micro/go-micro/v2" "github.com/micro/go-micro/v2/metadata" "auth/proto/auth" ) type AuthService struct{} func (s *AuthService) CheckAuth(ctx context.Context, req *auth.CheckAuthRequest, rsp *auth.CheckAuthResponse) error { // 从context中提取API服务传递的元数据 md, ok := metadata.FromContext(ctx) if !ok { rsp.Allowed = false rsp.ErrorMsg = "no request metadata found" return nil } // 执行你的认证逻辑,比如从元数据中拿token验证 token := md["authorization"] if token == "" || !validateToken(token) { rsp.Allowed = false rsp.ErrorMsg = "invalid or missing token" return nil } rsp.Allowed = true return nil } func validateToken(token string) bool { // 替换为你的实际token校验逻辑(比如JWT解析) return len(token) > 10 } func main() { // 创建服务并注册到服务发现 service := micro.NewService( micro.Name("auth-service"), micro.Version("v1.0"), ) service.Init() // 注册Auth服务实现 auth.RegisterAuthHandler(service.Server(), new(AuthService)) // 启动服务 if err := service.Run(); err != nil { log.Fatal(err) } }
二、API服务侧的轻量Wrapper(调用独立Auth服务)
原进程内wrapper要改成一个轻量本地中间件,它不处理认证逻辑,只负责调用独立Auth服务并传递context:
1. 实现本地调用Wrapper
package middleware import ( "context" "fmt" "github.com/micro/go-micro/v2/client" "auth/proto/auth" ) // AuthWrapper 本地轻量中间件,转发认证请求到独立Auth服务 func AuthWrapper(fn client.HandlerFunc) client.HandlerFunc { return func(ctx context.Context, req client.Request, rsp interface{}) error { // 通过服务发现创建Auth服务客户端 authClient := auth.NewAuthService("auth-service", client.DefaultClient) // 直接传递当前context,go-micro会自动携带元数据到Auth服务 checkRsp, err := authClient.CheckAuth(ctx, &auth.CheckAuthRequest{}) if err != nil { return fmt.Errorf("auth service call failed: %w", err) } if !checkRsp.Allowed { return fmt.Errorf("authorization failed: %s", checkRsp.ErrorMsg) } // 认证通过,继续执行原业务请求 return fn(ctx, req, rsp) } }
2. 在API服务中启用Wrapper
在API服务初始化时,将这个本地Wrapper添加到客户端中间件链:
package main import ( "log" "github.com/micro/go-micro/v2" "your-project/middleware" ) func main() { service := micro.NewService( micro.Name("user-api-service"), ) // 注入Auth认证中间件 service.Init( micro.WrapClient(middleware.AuthWrapper), ) // 启动API服务 if err := service.Run(); err != nil { log.Fatal(err) } }
三、关键细节:Context元数据的传递
go-micro的RPC调用会自动携带context.Context中的metadata.MD元数据,你只需要确保API服务的请求context中包含认证所需信息:
比如在API服务的HTTP入口处,从请求头提取token并注入context:
// 示例:从HTTP请求头提取token,注入到context元数据 func injectAuthMetadata(ctx context.Context, r *http.Request) context.Context { token := r.Header.Get("Authorization") md := metadata.MD{ "authorization": []string{token}, } return metadata.NewContext(ctx, md) }
这样,当本地Wrapper调用独立Auth服务时,token会自动通过context传递过去,Auth服务就能正常获取并验证。
四、服务发现的自动支持
go-micro默认集成了服务发现(如mdns,也可配置consul、etcd等),只要Auth服务和API服务使用相同的服务发现配置,API服务就能通过服务名auth-service自动找到Auth服务实例,无需硬编码地址。
这样改造后,你修改Auth服务的逻辑时,只需要重启Auth服务即可,所有API服务会自动通过服务发现调用新实例,完全符合你的需求。
内容的提问来源于stack exchange,提问作者Alexandr

