Go Micro从3.x后商业化后很多开发者转向asim个人开源版,asim/micro 服务间默认的传输协议transport为http。
// 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
// 使用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中
// 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方法:
err := cc.Invoke(ctx, methodToGRPC(req.Service(), req.Endpoint()), req.Body(), rsp, grpcCallOptions...)
// 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])
}
// 调整代码,问题解决
return fmt.Sprintf("/%s/%s.%s", service, mParts[0], mParts[1])
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。