go-zero开发入门之网关往rpc服务传递数据2

news/2025/1/12 7:53:55/

go-zero 的网关服务实际是个 go-zero 的 API 服务,也就是一个 http 服务,或者说 rest 服务。http 转 grpc 使用了开源的 grpcurl 库,当网关需要往 rpc 服务传递额外的数据,比如鉴权数据的时候,通过 http 的 header 进行:

func AuthMiddleware(next http.HandlerFunc, w http.ResponseWriter, r *http.Request) {authResp, err := authClient.Authenticate(r.Context(), &authReq) // 调用鉴权服务r.Header.Set("Grpc-Metadata-myuid", authResp.UserId) // 往 rpc 服务传递额外数据next.ServeHTTP(w, r)
}

rpc 服务端从 metadata 取出数据:

func (l *QueryUserLogic) QueryUser(in *user.UserReq) (*user.UserResp, error) {vals := metadata.ValueFromIncomingContext(l.ctx, "gateway-myuid")uid = vals[0]
}

这里有两个需要注意的地方,在网关侧的名必须以“Grpc-Metadata-”打头,而 rpc 服务端必须以“gateway-”打头,这是 go-zero 的 gateway/internal/headerprocessor.go 写死的规则:

const (metadataHeaderPrefix = "Grpc-Metadata-"metadataPrefix       = "gateway-"
)// ProcessHeaders builds the headers for the gateway from HTTP headers.
func ProcessHeaders(header http.Header) []string {var headers []stringfor k, v := range header {if !strings.HasPrefix(k, metadataHeaderPrefix) { // 判断是否以“Grpc-Metadata-”打头(网关侧传递的)continue // 非以“Grpc-Metadata-”打头的都会被丢弃掉}key := fmt.Sprintf("%s%s", metadataPrefix, strings.TrimPrefix(k, metadataHeaderPrefix)) // 替换为新的前缀“gateway-”(rpc 服务端看到的)for _, vv := range v {headers = append(headers, key+":"+vv)}}return headers
}

调用栈:

(dlv) bt0  0x00000000019da092 in github.com/zeromicro/go-zero/gateway/internal.ProcessHeadersat ./go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/gateway/internal/headerprocessor.go:151  0x00000000019dc40a in github.com/zeromicro/go-zero/gateway.(*Server).prepareMetadataat ./go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/gateway/server.go:1752  0x00000000019dbf69 in github.com/zeromicro/go-zero/gateway.(*Server).buildHandler.func1at ./go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/gateway/server.go:1323  0x000000000089f52f in net/http.HandlerFunc.ServeHTTPat /usr/local/go/src/net/http/server.go:21224  0x00000000008ca162 in net/http.Handler.ServeHTTP-fmat <autogenerated>:15  0x000000000195adc5 in net/http.HandlerFunc.ServeHTTPat /usr/local/go/src/net/http/server.go:21226  0x000000000195adc5 in gateway/middleware.AuthMiddleware // 网关代码at ./Getting-Started-with-Go-zero/gateway_login/gateway/middleware/login_and_auth.go:987  0x000000000195a325 in gateway/middleware.LoginAndAuthMiddleware.func1at ./Getting-Started-with-Go-zero/gateway_login/gateway/middleware/login_and_auth.go:378  0x000000000089f52f in net/http.HandlerFunc.ServeHTTPat /usr/local/go/src/net/http/server.go:21229  0x0000000001969169 in github.com/zeromicro/go-zero/rest/handler.GunzipHandler.func1at ./go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/rest/handler/gunziphandler.go:26
10  0x000000000089f52f in net/http.HandlerFunc.ServeHTTPat /usr/local/go/src/net/http/server.go:2122
11  0x000000000196b3bf in github.com/zeromicro/go-zero/rest/handler.MaxBytesHandler.func2.1at ./go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/rest/handler/maxbyteshandler.go:24
12  0x000000000089f52f in net/http.HandlerFunc.ServeHTTPat /usr/local/go/src/net/http/server.go:2122
13  0x000000000196ba15 in github.com/zeromicro/go-zero/rest/handler.MetricHandler.func1.1at ./go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/rest/handler/metrichandler.go:21
14  0x000000000089f52f in net/http.HandlerFunc.ServeHTTPat /usr/local/go/src/net/http/server.go:2122
15  0x000000000196c243 in github.com/zeromicro/go-zero/rest/handler.RecoverHandler.func1at ./go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/rest/handler/recoverhandler.go:21
16  0x000000000089f52f in net/http.HandlerFunc.ServeHTTPat /usr/local/go/src/net/http/server.go:2122
17  0x000000000196d45c in github.com/zeromicro/go-zero/rest/handler.(*timeoutHandler).ServeHTTP.func1at ./go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/rest/handler/timeouthandler.go:82
18  0x0000000000471921 in runtime.goexitat /usr/local/go/src/runtime/asm_amd64.s:1598(dlv) bt0  0x00000000019d636a in google.golang.org/grpc/metadata.NewOutgoingContextat ./go/pkg/mod/google.golang.org/grpc@v1.59.0/metadata/metadata.go:1651  0x00000000019d636a in github.com/fullstorydev/grpcurl.InvokeRPCat ./go/pkg/mod/github.com/fullstorydev/grpcurl@v1.8.9/invoke.go:1362  0x00000000019dc058 in github.com/zeromicro/go-zero/gateway.(*Server).buildHandler.func1at ./go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/gateway/server.go:1323  0x000000000089f52f in net/http.HandlerFunc.ServeHTTPat /usr/local/go/src/net/http/server.go:21224  0x00000000008ca162 in net/http.Handler.ServeHTTP-fmat <autogenerated>:15  0x000000000195adc5 in net/http.HandlerFunc.ServeHTTPat /usr/local/go/src/net/http/server.go:21226  0x000000000195adc5 in gateway/middleware.AuthMiddleware // 网关代码at ./Getting-Started-with-Go-zero/gateway_login/gateway/middleware/login_and_auth.go:987  0x000000000195a325 in gateway/middleware.LoginAndAuthMiddleware.func1at ./Getting-Started-with-Go-zero/gateway_login/gateway/middleware/login_and_auth.go:378  0x000000000089f52f in net/http.HandlerFunc.ServeHTTPat /usr/local/go/src/net/http/server.go:21229  0x0000000001969169 in github.com/zeromicro/go-zero/rest/handler.GunzipHandler.func1at ./go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/rest/handler/gunziphandler.go:26
10  0x000000000089f52f in net/http.HandlerFunc.ServeHTTPat /usr/local/go/src/net/http/server.go:2122
11  0x000000000196b3bf in github.com/zeromicro/go-zero/rest/handler.MaxBytesHandler.func2.1at ./go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/rest/handler/maxbyteshandler.go:24
12  0x000000000089f52f in net/http.HandlerFunc.ServeHTTPat /usr/local/go/src/net/http/server.go:2122
13  0x000000000196ba15 in github.com/zeromicro/go-zero/rest/handler.MetricHandler.func1.1at ./go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/rest/handler/metrichandler.go:21
14  0x000000000089f52f in net/http.HandlerFunc.ServeHTTPat /usr/local/go/src/net/http/server.go:2122
15  0x000000000196c243 in github.com/zeromicro/go-zero/rest/handler.RecoverHandler.func1at ./go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/rest/handler/recoverhandler.go:21
16  0x000000000089f52f in net/http.HandlerFunc.ServeHTTPat /usr/local/go/src/net/http/server.go:2122
17  0x000000000196d45c in github.com/zeromicro/go-zero/rest/handler.(*timeoutHandler).ServeHTTP.func1at ./go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/rest/handler/timeouthandler.go:82
18  0x0000000000471921 in runtime.goexitat /usr/local/go/src/runtime/asm_amd64.s:1598

在文件 zrpc/internal/clientinterceptors/tracinginterceptor.go 中调用了 metadata.NewOutgoingContext:

func startSpan(ctx context.Context, method, target string) (context.Context, trace.Span) {md, ok := metadata.FromOutgoingContext(ctx)if !ok {md = metadata.MD{}}tr := otel.Tracer(ztrace.TraceName)name, attr := ztrace.SpanInfo(method, target)ctx, span := tr.Start(ctx, name, trace.WithSpanKind(trace.SpanKindClient),trace.WithAttributes(attr...))ztrace.Inject(ctx, otel.GetTextMapPropagator(), &md)ctx = metadata.NewOutgoingContext(ctx, md)return ctx, span
}// UnaryTracingInterceptor returns a grpc.UnaryClientInterceptor for opentelemetry.
func UnaryTracingInterceptor(ctx context.Context, method string, req, reply any,cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {ctx, span := startSpan(ctx, method, cc.Target())defer span.End()ztrace.MessageSent.Event(ctx, 1, req)err := invoker(ctx, method, req, reply, cc, opts...)ztrace.MessageReceived.Event(ctx, 1, reply)if err != nil {s, ok := status.FromError(err)if ok {span.SetStatus(codes.Error, s.Message())span.SetAttributes(ztrace.StatusCodeAttr(s.Code()))} else {span.SetStatus(codes.Error, err.Error())}return err}span.SetAttributes(ztrace.StatusCodeAttr(gcodes.OK))return nil
}

拦截器:

./zrpc/internal/rpcserver.go:		interceptors = append(interceptors, serverinterceptors.UnaryTracingInterceptor)
./zrpc/internal/client.go:		interceptors = append(interceptors, clientinterceptors.UnaryTracingInterceptor)

服务端代码:

//zrpc/internal/rpcserver.go
func (s *rpcServer) buildUnaryInterceptors() []grpc.UnaryServerInterceptor {var interceptors []grpc.UnaryServerInterceptorif s.middlewares.Trace {interceptors = append(interceptors, serverinterceptors.UnaryTracingInterceptor)}if s.middlewares.Recover {interceptors = append(interceptors, serverinterceptors.UnaryRecoverInterceptor)}if s.middlewares.Stat {interceptors = append(interceptors,serverinterceptors.UnaryStatInterceptor(s.metrics, s.middlewares.StatConf))}if s.middlewares.Prometheus {interceptors = append(interceptors, serverinterceptors.UnaryPrometheusInterceptor)}if s.middlewares.Breaker {interceptors = append(interceptors, serverinterceptors.UnaryBreakerInterceptor)}return append(interceptors, s.unaryInterceptors...)
}func (s *rpcServer) Start(register RegisterFn) error {lis, err := net.Listen("tcp", s.address)if err != nil {return err}unaryInterceptorOption := grpc.ChainUnaryInterceptor(s.buildUnaryInterceptors()...)streamInterceptorOption := grpc.ChainStreamInterceptor(s.buildStreamInterceptors()...)options := append(s.options, unaryInterceptorOption, streamInterceptorOption)server := grpc.NewServer(options...)register(server)// register the health check serviceif s.health != nil {grpc_health_v1.RegisterHealthServer(server, s.health)s.health.Resume()}s.healthManager.MarkReady()health.AddProbe(s.healthManager)// we need to make sure all others are wrapped up,// so we do graceful stop at shutdown phase instead of wrap up phasewaitForCalled := proc.AddShutdownListener(func() {if s.health != nil {s.health.Shutdown()}server.GracefulStop()})defer waitForCalled()return server.Serve(lis)
}

客户端代码:

//zrpc/internal/client.go
func (c *client) buildUnaryInterceptors(timeout time.Duration) []grpc.UnaryClientInterceptor {var interceptors []grpc.UnaryClientInterceptorif c.middlewares.Trace {interceptors = append(interceptors, clientinterceptors.UnaryTracingInterceptor)}if c.middlewares.Duration {interceptors = append(interceptors, clientinterceptors.DurationInterceptor)}if c.middlewares.Prometheus {interceptors = append(interceptors, clientinterceptors.PrometheusInterceptor)}if c.middlewares.Breaker {interceptors = append(interceptors, clientinterceptors.BreakerInterceptor)}if c.middlewares.Timeout {interceptors = append(interceptors, clientinterceptors.TimeoutInterceptor(timeout))}return interceptors
}func (c *client) buildDialOptions(opts ...ClientOption) []grpc.DialOption {var cliOpts ClientOptionsfor _, opt := range opts {opt(&cliOpts)}var options []grpc.DialOptionif !cliOpts.Secure {options = append([]grpc.DialOption(nil),grpc.WithTransportCredentials(insecure.NewCredentials()))}if !cliOpts.NonBlock {options = append(options, grpc.WithBlock())}options = append(options,grpc.WithChainUnaryInterceptor(c.buildUnaryInterceptors(cliOpts.Timeout)...),grpc.WithChainStreamInterceptor(c.buildStreamInterceptors()...),)return append(options, cliOpts.DialOptions...)
}func (c *client) dial(server string, opts ...ClientOption) error {options := c.buildDialOptions(opts...)timeCtx, cancel := context.WithTimeout(context.Background(), dialTimeout)defer cancel()conn, err := grpc.DialContext(timeCtx, server, options...)if err != nil {service := serverif errors.Is(err, context.DeadlineExceeded) {pos := strings.LastIndexByte(server, separator)// len(server) - 1 is the index of last charif 0 < pos && pos < len(server)-1 {service = server[pos+1:]}}return fmt.Errorf("rpc dial: %s, error: %s, make sure rpc service %q is already started",server, err.Error(), service)}c.conn = connreturn nil
}// NewClient returns a Client.
func NewClient(target string, middlewares ClientMiddlewaresConf, opts ...ClientOption) (Client, error) {cli := client{middlewares: middlewares,}svcCfg := fmt.Sprintf(`{"loadBalancingPolicy":"%s"}`, p2c.Name)balancerOpt := WithDialOption(grpc.WithDefaultServiceConfig(svcCfg))opts = append([]ClientOption{balancerOpt}, opts...)if err := cli.dial(target, opts...); err != nil {return nil, err}return &cli, nil
}

http://www.ppmy.cn/news/1270822.html

相关文章

Vue 组件传参 emit

emit 属性&#xff1a;用于创建自定义事件&#xff0c;接收子组件传递过来的数据。 注意&#xff1a;如果自定义事件的名称&#xff0c;和原生事件的名称一样&#xff0c;那么只会触发自定义事件。 setup 语法糖写法请见&#xff1a;《Vue3 子传父 组件传参 defineEmits》 语…

计算机网络中的通信子网主要有哪些功能?

计算机网络中的通信子网主要具有以下功能&#xff1a; 负责全网的数据通信&#xff1a;通信子网通过使用各种通信协议和传输控制功能&#xff0c;能够确保数据从一台主机安全、准确地传输到另一台主机。这包括数据的封装、解封装、传输控制、差错控制等过程。 完成各种网络数据…

YOLOv8改进《目标对象计数》多任务实验:深度集成版来了!支持自定义数据集训练自定义模型

💡该教程为改进YOLO专栏,属于《芒果书》📚系列,包含大量的原创改进方式🚀 💡🚀🚀🚀内含改进源代码 按步骤操作运行改进后的代码即可💡更方便的统计更多实验数据,方便写作 YOLOv8改进《目标对象计数》多任务实验:深度集成版来了!支持自定义数据集训练自定…

11、ble_mesh_provisioner 配网器

1、初始化流程&#xff0c;存储初始化&#xff0c;nvs擦除&#xff0c; 2、bluetooth_init();ble协议栈初始化 3、ble_mesh_get_dev_uuid(dev_uuid);//获取设备uuid加载到mac. 4、ble_mesh_init();//ble mesh协议栈初始化 4.1配置网络钥匙&#xff0c;索引&#xff0c;赋…

提取视频光流成帧并写入视频中

修改一下配置文件就可以运行了 配置文件 config.py video_path xxxx/dataset/data/huaping/BXDQ05-花屏-1.mp4#要处理的视频路径 frame_path xxxx/dataset/frame#处理成帧之后保存的路径 flow_path xxxx/dataset/flow#处理成光流之后保存的路径 save_video_path xxxx/fe…

微服务学习:Nacos微服务架构中的服务注册、服务发现和动态配置Nacos下载

Nacos的主要用途包括&#xff1a; 服务注册与发现&#xff1a;Nacos提供了服务注册和发现的功能&#xff0c;服务提供者可以将自己的服务注册到Nacos服务器上&#xff0c;服务消费者则可以通过Nacos来发现可用的服务实例&#xff0c;从而实现服务调用。 动态配置管理&#xff…

Java8新特性:Lambda表达式

我是南城余&#xff01;阿里云开发者平台专家博士证书获得者&#xff01; 欢迎关注我的博客&#xff01;一同成长&#xff01; 一名从事运维开发的worker&#xff0c;记录分享学习。 专注于AI&#xff0c;运维开发&#xff0c;windows Linux 系统领域的分享&#xff01; 本…

物联网网关

物联网网关是连接物联网设备和互联网的重要桥梁。 它负责将物联网设备采集到的数据进行处理、存储和转发&#xff0c;使其能够与云端或其它设备进行通信。 物联网网关的作用是实现物联网设备与云端的无缝连接和数据交互。 物联网网关功能 数据采集&#xff1a;物联网网关可以从…