前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >asim-micro更改服务协议transport grpc后,使用rpc调用出错解决

asim-micro更改服务协议transport grpc后,使用rpc调用出错解决

原创
作者头像
code happy
发布2022-02-22 16:01:21
5850
发布2022-02-22 16:01:21
举报
文章被收录于专栏:技术live-yongjian

Go Micro从3.x后商业化后很多开发者转向asim个人开源版,asim/micro 服务间默认的传输协议transport为http。

代码语言:go
复制
// filepath:option.go

func newOptions(opts ...Option) Options {
	opt := Options{
		Auth:      auth.DefaultAuth,
		Broker:    broker.DefaultBroker,
		Cmd:       cmd.DefaultCmd,
		Config:    config.DefaultConfig,
		Client:    client.DefaultClient,
		Server:    server.DefaultServer,
		Store:     store.DefaultStore,
		Registry:  registry.DefaultRegistry,
		Runtime:   runtime.DefaultRuntime,
		Transport: transport.DefaultTransport, //默认使用http
		Context:   context.Background(),
		Signal:    true,
	}

	for _, o := range opts {
		o(&opt)
	}

	return opt
}

// filepath: transport/transport.go

var (
	DefaultTransport Transport = NewHTTPTransport()

	DefaultDialTimeout = time.Second * 5
)

如果采用其他协议如grpc则需要通过以Plugins加载使用。在微服务通讯中,grpc使用二进制消息格式protobuf进行序列化,性能优于http,建议使用grpc代替http

代码语言:go
复制
// 使用grpc代替http

import grpcT "github.com/asim/go-micro/plugins/transport/grpc/v3"

micro.Transport(grpcT.NewTransport()),

  假设我们的servie:go.micro.demo.ds, endpoint:Ds.DeleteCode

   通过micro call命令调用后出现错误 unkown service  go.micro.demo.ds.Ds ,刚开始以为是micro版本问题,而micro列出的服务名,端点等信息都是正确的,于是将问题的根源定位至 grpcClient call中

代码语言:go
复制
// filepath:plugins/client/grpc/grpc.go

func (g *grpcClient) call(ctx context.Context, node *registry.Node, req client.Request, rsp interface{}, opts client.CallOptions) error {
	var header map[string]string

	address := node.Address

	header = make(map[string]string)
	if md, ok := metadata.FromContext(ctx); ok {
		header = make(map[string]string, len(md))
		for k, v := range md {
			header[strings.ToLower(k)] = v
		}
	} else {
		header = make(map[string]string)
	}

	// set timeout in nanoseconds
	header["timeout"] = fmt.Sprintf("%d", opts.RequestTimeout)
	// set the content type for the request
	header["x-content-type"] = req.ContentType()

	md := gmetadata.New(header)
	ctx = gmetadata.NewOutgoingContext(ctx, md)

	cf, err := g.newGRPCCodec(req.ContentType())
	if err != nil {
		return errors.InternalServerError("go.micro.client", err.Error())
	}

	maxRecvMsgSize := g.maxRecvMsgSizeValue()
	maxSendMsgSize := g.maxSendMsgSizeValue()

	var grr error

	grpcDialOptions := []grpc.DialOption{
		grpc.WithTimeout(opts.DialTimeout),
		g.secure(address),
		grpc.WithDefaultCallOptions(
			grpc.MaxCallRecvMsgSize(maxRecvMsgSize),
			grpc.MaxCallSendMsgSize(maxSendMsgSize),
		),
	}

	if opts := g.getGrpcDialOptions(); opts != nil {
		grpcDialOptions = append(grpcDialOptions, opts...)
	}

	cc, err := g.pool.getConn(address, grpcDialOptions...)
	if err != nil {
		return errors.InternalServerError("go.micro.client", fmt.Sprintf("Error sending request: %v", err))
	}
	defer func() {
		// defer execution of release
		g.pool.release(address, cc, grr)
	}()

	ch := make(chan error, 1)

	go func() {
		grpcCallOptions := []grpc.CallOption{
			grpc.ForceCodec(cf),
			grpc.CallContentSubtype(cf.Name())}
		if opts := g.getGrpcCallOptions(); opts != nil {
			grpcCallOptions = append(grpcCallOptions, opts...)
		}
		err := cc.Invoke(ctx, methodToGRPC(req.Service(), req.Endpoint()), req.Body(), rsp, grpcCallOptions...)
		ch <- microError(err)
	}()

	select {
	case err := <-ch:
		grr = err
	case <-ctx.Done():
		grr = errors.Timeout("go.micro.client", "%v", ctx.Err())
	}

	return grr
}

找到关键代码methodToGRPC方法:

代码语言:go
复制
err := cc.Invoke(ctx, methodToGRPC(req.Service(), req.Endpoint()), req.Body(), rsp, grpcCallOptions...)
代码语言:go
复制
// service Struct.Method /service.Struct/Method
func methodToGRPC(service, method string) string {
	// no method or already grpc method
	if len(method) == 0 || method[0] == '/' {
		return method
	}

	// assume method is Foo.Bar
	mParts := strings.Split(method, ".")
	if len(mParts) != 2 {
		return method
	}

	if len(service) == 0 {
		return fmt.Sprintf("/%s/%s", mParts[0], mParts[1])
	}

	// return /pkg.Foo/Bar
        // 从这里可以看到 将endpoint按.分割,第一个参数连接servicename
        return fmt.Sprintf("/%s.%s/%s", service, mParts[0], mParts[1])

 }
代码语言:go
复制
// 调整代码,问题解决
return fmt.Sprintf("/%s/%s.%s", service, mParts[0], mParts[1])

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档