You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确将go-micro wrapper作为独立服务使用?以authWrapper为例

将go-micro 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:59:40