Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
9526345cd6 | |||
e3fe27105b |
64
codec.go
64
codec.go
@@ -1,7 +1,9 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"go.unistack.org/micro/v4/codec"
|
||||
"io"
|
||||
|
||||
"go.unistack.org/micro/v3/codec"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/encoding"
|
||||
)
|
||||
@@ -63,3 +65,63 @@ func (w *wrapGrpcCodec) Unmarshal(d []byte, v interface{}, opts ...codec.Option)
|
||||
}
|
||||
return w.Codec.Unmarshal(d, v)
|
||||
}
|
||||
|
||||
/*
|
||||
type grpcCodec struct {
|
||||
grpc.ServerStream
|
||||
// headers
|
||||
id string
|
||||
target string
|
||||
method string
|
||||
endpoint string
|
||||
|
||||
c encoding.Codec
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
func (w *wrapGrpcCodec) ReadHeader(conn io.Reader, m *codec.Message, mt codec.MessageType) error {
|
||||
/*
|
||||
if m == nil {
|
||||
m = codec.NewMessage(codec.Request)
|
||||
}
|
||||
|
||||
if md, ok := metadata.FromIncomingContext(g.ServerStream.Context()); ok {
|
||||
if m.Header == nil {
|
||||
m.Header = meta.New(len(md))
|
||||
}
|
||||
for k, v := range md {
|
||||
m.Header[k] = strings.Join(v, ",")
|
||||
}
|
||||
}
|
||||
|
||||
m.Id = g.id
|
||||
m.Target = g.target
|
||||
m.Method = g.method
|
||||
m.Endpoint = g.endpoint
|
||||
*/
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *wrapGrpcCodec) ReadBody(conn io.Reader, v interface{}) error {
|
||||
// caller has requested a frame
|
||||
if m, ok := v.(*codec.Frame); ok {
|
||||
_, err := conn.Read(m.Data)
|
||||
return err
|
||||
}
|
||||
return codec.ErrInvalidMessage
|
||||
}
|
||||
|
||||
func (w *wrapGrpcCodec) Write(conn io.Writer, m *codec.Message, v interface{}) error {
|
||||
// if we don't have a body
|
||||
if v != nil {
|
||||
b, err := w.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.Body = b
|
||||
}
|
||||
// write the body using the framing codec
|
||||
_, err := conn.Write(m.Body)
|
||||
return err
|
||||
}
|
||||
|
@@ -1,67 +0,0 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"go.unistack.org/micro/v4/codec"
|
||||
gmetadata "google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
type mockStream struct {
|
||||
msg any
|
||||
}
|
||||
|
||||
func (m mockStream) Header() (gmetadata.MD, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m mockStream) Trailer() gmetadata.MD {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m mockStream) CloseSend() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m mockStream) Context() context.Context {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockStream) SendMsg(msg any) error {
|
||||
m.msg = msg
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockStream) RecvMsg(msg any) error {
|
||||
|
||||
c := msg.(*codec.Frame)
|
||||
c.Data = m.msg.(*codec.Frame).Data
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Test_ReadWrap(t *testing.T) {
|
||||
|
||||
wp := wrapStream{
|
||||
&mockStream{},
|
||||
}
|
||||
|
||||
write, err := wp.Write([]byte("test_data"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if write != 9 {
|
||||
t.Error("uncorrected number wrote bytes")
|
||||
}
|
||||
|
||||
b := make([]byte, write)
|
||||
read, err := wp.Read(b)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if read != 9 || string(b) != "test_data" {
|
||||
t.Error("uncorrected number wrote bytes or data")
|
||||
}
|
||||
}
|
2
error.go
2
error.go
@@ -1,7 +1,7 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"go.unistack.org/micro/v4/errors"
|
||||
"go.unistack.org/micro/v3/errors"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
|
24
go.mod
24
go.mod
@@ -1,24 +1,8 @@
|
||||
module go.unistack.org/micro-client-grpc/v4
|
||||
module go.unistack.org/micro-client-grpc/v3
|
||||
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.23.3
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
go.unistack.org/micro/v4 v4.1.2
|
||||
google.golang.org/grpc v1.70.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/ash3in/uuidv8 v1.2.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/matoous/go-nanoid v1.5.1 // indirect
|
||||
github.com/spf13/cast v1.7.1 // indirect
|
||||
go.unistack.org/micro-proto/v4 v4.1.0 // indirect
|
||||
golang.org/x/net v0.35.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250224174004-546df14abb99 // indirect
|
||||
google.golang.org/protobuf v1.36.5 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
go.unistack.org/micro/v3 v3.10.22
|
||||
google.golang.org/grpc v1.52.3
|
||||
)
|
||||
|
260
grpc.go
260
grpc.go
@@ -1,32 +1,28 @@
|
||||
// Package grpc provides a gRPC client for micro framework
|
||||
package grpc
|
||||
// Package grpc provides a gRPC client
|
||||
package grpc // import "go.unistack.org/micro-client-grpc/v3"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.unistack.org/micro/v4/client"
|
||||
"go.unistack.org/micro/v4/codec"
|
||||
"go.unistack.org/micro/v4/errors"
|
||||
"go.unistack.org/micro/v4/metadata"
|
||||
"go.unistack.org/micro/v4/options"
|
||||
"go.unistack.org/micro/v4/selector"
|
||||
"go.unistack.org/micro/v4/semconv"
|
||||
"go.unistack.org/micro/v4/tracer"
|
||||
"go.unistack.org/micro/v3/broker"
|
||||
"go.unistack.org/micro/v3/client"
|
||||
"go.unistack.org/micro/v3/codec"
|
||||
"go.unistack.org/micro/v3/errors"
|
||||
"go.unistack.org/micro/v3/metadata"
|
||||
"go.unistack.org/micro/v3/selector"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/encoding"
|
||||
gmetadata "google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -34,8 +30,6 @@ const (
|
||||
)
|
||||
|
||||
type grpcClient struct {
|
||||
funcCall client.FuncCall
|
||||
funcStream client.FuncStream
|
||||
pool *ConnPool
|
||||
opts client.Options
|
||||
sync.RWMutex
|
||||
@@ -74,12 +68,15 @@ func (g *grpcClient) secure(addr string) grpc.DialOption {
|
||||
}
|
||||
|
||||
func (g *grpcClient) call(ctx context.Context, addr string, req client.Request, rsp interface{}, opts client.CallOptions) error {
|
||||
var header map[string][]string
|
||||
var header map[string]string
|
||||
|
||||
if md, ok := metadata.FromOutgoingContext(ctx); ok {
|
||||
header = metadata.Copy(md)
|
||||
header = make(map[string]string, len(md))
|
||||
for k, v := range md {
|
||||
header[strings.ToLower(k)] = v
|
||||
}
|
||||
} else {
|
||||
header = make(map[string][]string, 2)
|
||||
header = make(map[string]string, 2)
|
||||
}
|
||||
if opts.RequestMetadata != nil {
|
||||
for k, v := range opts.RequestMetadata {
|
||||
@@ -87,15 +84,16 @@ func (g *grpcClient) call(ctx context.Context, addr string, req client.Request,
|
||||
}
|
||||
}
|
||||
// set timeout in nanoseconds
|
||||
header["grpc-timeout"] = append(header["grpc-timeout"], fmt.Sprintf("%dn", opts.RequestTimeout))
|
||||
header["timeout"] = append(header["timeout"], fmt.Sprintf("%dn", opts.RequestTimeout))
|
||||
header["content-type"] = append(header["content-type"], req.ContentType())
|
||||
header["Grpc-Timeout"] = fmt.Sprintf("%dn", opts.RequestTimeout)
|
||||
header["timeout"] = fmt.Sprintf("%dn", opts.RequestTimeout)
|
||||
header["content-type"] = req.ContentType()
|
||||
|
||||
ctx = gmetadata.NewOutgoingContext(ctx, header)
|
||||
md := gmetadata.New(header)
|
||||
ctx = gmetadata.NewOutgoingContext(ctx, md)
|
||||
|
||||
cf, err := g.newCodec(req.ContentType())
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", "%+v", err)
|
||||
return errors.InternalServerError("go.micro.client", err.Error())
|
||||
}
|
||||
|
||||
maxRecvMsgSize := g.maxRecvMsgSizeValue()
|
||||
@@ -122,9 +120,6 @@ func (g *grpcClient) call(ctx context.Context, addr string, req client.Request,
|
||||
grpc.WithDefaultServiceConfig(cfgService),
|
||||
}
|
||||
|
||||
if opts := g.getGrpcDialOptions(g.opts.Context); opts != nil {
|
||||
grpcDialOptions = append(grpcDialOptions, opts...)
|
||||
}
|
||||
if opts := g.getGrpcDialOptions(opts.Context); opts != nil {
|
||||
grpcDialOptions = append(grpcDialOptions, opts...)
|
||||
}
|
||||
@@ -139,7 +134,7 @@ func (g *grpcClient) call(ctx context.Context, addr string, req client.Request,
|
||||
|
||||
cc, err := g.pool.Get(dialCtx, addr, grpcDialOptions...)
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", "Error sending request: %v", err)
|
||||
return errors.InternalServerError("go.micro.client", fmt.Sprintf("Error sending request: %v", err))
|
||||
}
|
||||
defer func() {
|
||||
// defer execution of release
|
||||
@@ -177,7 +172,7 @@ func (g *grpcClient) call(ctx context.Context, addr string, req client.Request,
|
||||
if opts.ResponseMetadata != nil {
|
||||
*opts.ResponseMetadata = metadata.New(gmd.Len())
|
||||
for k, v := range gmd {
|
||||
opts.ResponseMetadata.Append(k, v...)
|
||||
opts.ResponseMetadata.Set(k, strings.Join(v, ","))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,27 +180,31 @@ func (g *grpcClient) call(ctx context.Context, addr string, req client.Request,
|
||||
}
|
||||
|
||||
func (g *grpcClient) stream(ctx context.Context, addr string, req client.Request, rsp interface{}, opts client.CallOptions) error {
|
||||
var header map[string][]string
|
||||
var header map[string]string
|
||||
|
||||
if md, ok := metadata.FromOutgoingContext(ctx); ok {
|
||||
header = metadata.Copy(md)
|
||||
header = make(map[string]string, len(md))
|
||||
for k, v := range md {
|
||||
header[k] = v
|
||||
}
|
||||
} else {
|
||||
header = make(map[string][]string)
|
||||
header = make(map[string]string)
|
||||
}
|
||||
|
||||
// set timeout in nanoseconds
|
||||
if opts.StreamTimeout > time.Duration(0) {
|
||||
header["grpc-timeout"] = append(header["grpc-timeout"], fmt.Sprintf("%dn", opts.RequestTimeout))
|
||||
header["timeout"] = append(header["timeout"], fmt.Sprintf("%dn", opts.RequestTimeout))
|
||||
header["Grpc-Timeout"] = fmt.Sprintf("%dn", opts.StreamTimeout)
|
||||
header["timeout"] = fmt.Sprintf("%dn", opts.StreamTimeout)
|
||||
}
|
||||
// set the content type for the request
|
||||
header["content-type"] = append(header["content-type"], req.ContentType())
|
||||
header["content-type"] = req.ContentType()
|
||||
|
||||
ctx = gmetadata.NewOutgoingContext(ctx, header)
|
||||
md := gmetadata.New(header)
|
||||
ctx = gmetadata.NewOutgoingContext(ctx, md)
|
||||
|
||||
cf, err := g.newCodec(req.ContentType())
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", "%+v", err)
|
||||
return errors.InternalServerError("go.micro.client", err.Error())
|
||||
}
|
||||
|
||||
var dialCtx context.Context
|
||||
@@ -246,7 +245,7 @@ func (g *grpcClient) stream(ctx context.Context, addr string, req client.Request
|
||||
|
||||
cc, err := g.pool.Get(dialCtx, addr, grpcDialOptions...)
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", "Error sending request: %v", err)
|
||||
return errors.InternalServerError("go.micro.client", fmt.Sprintf("Error sending request: %v", err))
|
||||
}
|
||||
|
||||
desc := &grpc.StreamDesc{
|
||||
@@ -279,7 +278,7 @@ func (g *grpcClient) stream(ctx context.Context, addr string, req client.Request
|
||||
// release the connection
|
||||
g.pool.Put(cc, err)
|
||||
// now return the error
|
||||
return errors.InternalServerError("go.micro.client", "Error creating stream: %v", err)
|
||||
return errors.InternalServerError("go.micro.client", fmt.Sprintf("Error creating stream: %v", err))
|
||||
}
|
||||
|
||||
// set request codec
|
||||
@@ -404,17 +403,25 @@ func (g *grpcClient) Init(opts ...client.Option) error {
|
||||
g.pool.Unlock()
|
||||
}
|
||||
|
||||
g.funcCall = g.fnCall
|
||||
g.funcStream = g.fnStream
|
||||
|
||||
g.opts.Hooks.EachPrev(func(hook options.Hook) {
|
||||
switch h := hook.(type) {
|
||||
case client.HookCall:
|
||||
g.funcCall = h(g.funcCall)
|
||||
case client.HookStream:
|
||||
g.funcStream = h(g.funcStream)
|
||||
if err := g.opts.Broker.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
})
|
||||
if err := g.opts.Tracer.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := g.opts.Router.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := g.opts.Logger.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := g.opts.Meter.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := g.opts.Transport.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
g.init = true
|
||||
|
||||
return nil
|
||||
@@ -424,6 +431,10 @@ func (g *grpcClient) Options() client.Options {
|
||||
return g.opts
|
||||
}
|
||||
|
||||
func (g *grpcClient) NewMessage(topic string, msg interface{}, opts ...client.MessageOption) client.Message {
|
||||
return newGRPCEvent(topic, msg, g.opts.ContentType, opts...)
|
||||
}
|
||||
|
||||
func (g *grpcClient) NewRequest(service, method string, req interface{}, reqOpts ...client.RequestOption) client.Request {
|
||||
return newGRPCRequest(service, method, req, g.opts.ContentType, reqOpts...)
|
||||
}
|
||||
@@ -434,32 +445,6 @@ func (g *grpcClient) Call(ctx context.Context, req client.Request, rsp interface
|
||||
} else if rsp == nil {
|
||||
return errors.InternalServerError("go.micro.client", "rsp is nil")
|
||||
}
|
||||
|
||||
ts := time.Now()
|
||||
g.opts.Meter.Counter(semconv.ClientRequestInflight, "endpoint", req.Endpoint()).Inc()
|
||||
var sp tracer.Span
|
||||
ctx, sp = g.opts.Tracer.Start(ctx, req.Endpoint()+" rpc-client",
|
||||
tracer.WithSpanKind(tracer.SpanKindClient),
|
||||
tracer.WithSpanLabels("endpoint", req.Endpoint()),
|
||||
)
|
||||
err := g.funcCall(ctx, req, rsp, opts...)
|
||||
g.opts.Meter.Counter(semconv.ClientRequestInflight, "endpoint", req.Endpoint()).Dec()
|
||||
te := time.Since(ts)
|
||||
g.opts.Meter.Summary(semconv.ClientRequestLatencyMicroseconds, "endpoint", req.Endpoint()).Update(te.Seconds())
|
||||
g.opts.Meter.Histogram(semconv.ClientRequestDurationSeconds, "endpoint", req.Endpoint()).Update(te.Seconds())
|
||||
|
||||
if me := errors.FromError(err); me == nil {
|
||||
sp.Finish()
|
||||
g.opts.Meter.Counter(semconv.ClientRequestTotal, "endpoint", req.Endpoint(), "status", "success", "code", strconv.Itoa(int(200))).Inc()
|
||||
} else {
|
||||
sp.SetStatus(tracer.SpanStatusError, err.Error())
|
||||
g.opts.Meter.Counter(semconv.ClientRequestTotal, "endpoint", req.Endpoint(), "status", "failure", "code", strconv.Itoa(int(me.Code))).Inc()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (g *grpcClient) fnCall(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {
|
||||
// make a copy of call opts
|
||||
callOpts := g.opts.CallOptions
|
||||
|
||||
@@ -491,6 +476,11 @@ func (g *grpcClient) fnCall(ctx context.Context, req client.Request, rsp interfa
|
||||
// make copy of call method
|
||||
gcall := g.call
|
||||
|
||||
// wrap the call in reverse
|
||||
for i := len(callOpts.CallWrappers); i > 0; i-- {
|
||||
gcall = callOpts.CallWrappers[i-1](gcall)
|
||||
}
|
||||
|
||||
// use the router passed as a call option, or fallback to the rpc clients router
|
||||
if callOpts.Router == nil {
|
||||
callOpts.Router = g.opts.Router
|
||||
@@ -512,7 +502,7 @@ func (g *grpcClient) fnCall(ctx context.Context, req client.Request, rsp interfa
|
||||
// call backoff first. Someone may want an initial start delay
|
||||
t, err := callOpts.Backoff(ctx, req, i)
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", "%+v", err)
|
||||
return errors.InternalServerError("go.micro.client", err.Error())
|
||||
}
|
||||
|
||||
// only sleep if greater than 0
|
||||
@@ -527,7 +517,7 @@ func (g *grpcClient) fnCall(ctx context.Context, req client.Request, rsp interfa
|
||||
// TODO apply any filtering here
|
||||
routes, err = g.opts.Lookup(ctx, req, callOpts)
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", "%+v", err)
|
||||
return errors.InternalServerError("go.micro.client", err.Error())
|
||||
}
|
||||
|
||||
// balance the list of nodes
|
||||
@@ -590,31 +580,6 @@ func (g *grpcClient) fnCall(ctx context.Context, req client.Request, rsp interfa
|
||||
}
|
||||
|
||||
func (g *grpcClient) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Stream, error) {
|
||||
ts := time.Now()
|
||||
g.opts.Meter.Counter(semconv.ClientRequestInflight, "endpoint", req.Endpoint()).Inc()
|
||||
var sp tracer.Span
|
||||
ctx, sp = g.opts.Tracer.Start(ctx, req.Endpoint()+" rpc-client",
|
||||
tracer.WithSpanKind(tracer.SpanKindClient),
|
||||
tracer.WithSpanLabels("endpoint", req.Endpoint()),
|
||||
)
|
||||
stream, err := g.funcStream(ctx, req, opts...)
|
||||
g.opts.Meter.Counter(semconv.ClientRequestInflight, "endpoint", req.Endpoint()).Dec()
|
||||
te := time.Since(ts)
|
||||
g.opts.Meter.Summary(semconv.ClientRequestLatencyMicroseconds, "endpoint", req.Endpoint()).Update(te.Seconds())
|
||||
g.opts.Meter.Histogram(semconv.ClientRequestDurationSeconds, "endpoint", req.Endpoint()).Update(te.Seconds())
|
||||
|
||||
if me := status.Convert(err); me == nil {
|
||||
sp.Finish()
|
||||
g.opts.Meter.Counter(semconv.ClientRequestTotal, "endpoint", req.Endpoint(), "status", "success", "code", strconv.Itoa(int(codes.OK))).Inc()
|
||||
} else {
|
||||
sp.SetStatus(tracer.SpanStatusError, err.Error())
|
||||
g.opts.Meter.Counter(semconv.ClientRequestTotal, "endpoint", req.Endpoint(), "status", "failure", "code", strconv.Itoa(int(me.Code()))).Inc()
|
||||
}
|
||||
|
||||
return stream, err
|
||||
}
|
||||
|
||||
func (g *grpcClient) fnStream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Stream, error) {
|
||||
// make a copy of call opts
|
||||
callOpts := g.opts.CallOptions
|
||||
for _, opt := range opts {
|
||||
@@ -633,6 +598,11 @@ func (g *grpcClient) fnStream(ctx context.Context, req client.Request, opts ...c
|
||||
// make a copy of stream
|
||||
gstream := g.stream
|
||||
|
||||
// wrap the call in reverse
|
||||
for i := len(callOpts.CallWrappers); i > 0; i-- {
|
||||
gstream = callOpts.CallWrappers[i-1](gstream)
|
||||
}
|
||||
|
||||
// use the router passed as a call option, or fallback to the rpc clients router
|
||||
if callOpts.Router == nil {
|
||||
callOpts.Router = g.opts.Router
|
||||
@@ -654,7 +624,7 @@ func (g *grpcClient) fnStream(ctx context.Context, req client.Request, opts ...c
|
||||
// call backoff first. Someone may want an initial start delay
|
||||
t, err := callOpts.Backoff(ctx, req, i)
|
||||
if err != nil {
|
||||
return nil, errors.InternalServerError("go.micro.client", "%+v", err)
|
||||
return nil, errors.InternalServerError("go.micro.client", err.Error())
|
||||
}
|
||||
|
||||
// only sleep if greater than 0
|
||||
@@ -669,7 +639,7 @@ func (g *grpcClient) fnStream(ctx context.Context, req client.Request, opts ...c
|
||||
// TODO apply any filtering here
|
||||
routes, err = g.opts.Lookup(ctx, req, callOpts)
|
||||
if err != nil {
|
||||
return nil, errors.InternalServerError("go.micro.client", "%+v", err)
|
||||
return nil, errors.InternalServerError("go.micro.client", err.Error())
|
||||
}
|
||||
|
||||
// balance the list of nodes
|
||||
@@ -742,6 +712,71 @@ func (g *grpcClient) fnStream(ctx context.Context, req client.Request, opts ...c
|
||||
return nil, grr
|
||||
}
|
||||
|
||||
func (g *grpcClient) BatchPublish(ctx context.Context, ps []client.Message, opts ...client.PublishOption) error {
|
||||
return g.publish(ctx, ps, opts...)
|
||||
}
|
||||
|
||||
func (g *grpcClient) Publish(ctx context.Context, p client.Message, opts ...client.PublishOption) error {
|
||||
return g.publish(ctx, []client.Message{p}, opts...)
|
||||
}
|
||||
|
||||
func (g *grpcClient) publish(ctx context.Context, ps []client.Message, opts ...client.PublishOption) error {
|
||||
var body []byte
|
||||
|
||||
options := client.NewPublishOptions(opts...)
|
||||
|
||||
// get proxy
|
||||
exchange := ""
|
||||
if v, ok := os.LookupEnv("MICRO_PROXY"); ok {
|
||||
exchange = v
|
||||
}
|
||||
|
||||
msgs := make([]*broker.Message, 0, len(ps))
|
||||
|
||||
omd, ok := metadata.FromOutgoingContext(ctx)
|
||||
if !ok {
|
||||
omd = metadata.New(2)
|
||||
}
|
||||
|
||||
for _, p := range ps {
|
||||
md := metadata.Copy(omd)
|
||||
md[metadata.HeaderContentType] = p.ContentType()
|
||||
|
||||
// passed in raw data
|
||||
if d, ok := p.Payload().(*codec.Frame); ok {
|
||||
body = d.Data
|
||||
} else {
|
||||
// use codec for payload
|
||||
cf, err := g.newCodec(p.ContentType())
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", err.Error())
|
||||
}
|
||||
// set the body
|
||||
b, err := cf.Marshal(p.Payload())
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", err.Error())
|
||||
}
|
||||
body = b
|
||||
}
|
||||
|
||||
topic := p.Topic()
|
||||
if len(exchange) > 0 {
|
||||
topic = exchange
|
||||
}
|
||||
|
||||
for k, v := range p.Metadata() {
|
||||
md.Set(k, v)
|
||||
}
|
||||
md.Set(metadata.HeaderTopic, topic)
|
||||
msgs = append(msgs, &broker.Message{Header: md, Body: body})
|
||||
}
|
||||
|
||||
return g.opts.Broker.BatchPublish(ctx, msgs,
|
||||
broker.PublishContext(options.Context),
|
||||
broker.PublishBodyOnly(options.BodyOnly),
|
||||
)
|
||||
}
|
||||
|
||||
func (g *grpcClient) String() string {
|
||||
return "grpc"
|
||||
}
|
||||
@@ -801,16 +836,22 @@ func NewClient(opts ...client.Option) client.Client {
|
||||
options.ContentType = DefaultContentType
|
||||
}
|
||||
|
||||
c := &grpcClient{
|
||||
rc := &grpcClient{
|
||||
opts: options,
|
||||
}
|
||||
|
||||
c.pool = NewConnPool(options.PoolSize, options.PoolTTL, c.poolMaxIdle(), c.poolMaxStreams())
|
||||
rc.pool = NewConnPool(options.PoolSize, options.PoolTTL, rc.poolMaxIdle(), rc.poolMaxStreams())
|
||||
c := client.Client(rc)
|
||||
|
||||
if c.opts.Context != nil {
|
||||
if codecs, ok := c.opts.Context.Value(codecsKey{}).(map[string]encoding.Codec); ok && codecs != nil {
|
||||
// wrap in reverse
|
||||
for i := len(options.Wrappers); i > 0; i-- {
|
||||
c = options.Wrappers[i-1](c)
|
||||
}
|
||||
|
||||
if rc.opts.Context != nil {
|
||||
if codecs, ok := rc.opts.Context.Value(codecsKey{}).(map[string]encoding.Codec); ok && codecs != nil {
|
||||
for k, v := range codecs {
|
||||
c.opts.Codecs[k] = &wrapGrpcCodec{v}
|
||||
rc.opts.Codecs[k] = &wrapGrpcCodec{v}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -819,8 +860,5 @@ func NewClient(opts ...client.Option) client.Client {
|
||||
encoding.RegisterCodec(&wrapMicroCodec{k})
|
||||
}
|
||||
|
||||
c.funcCall = c.fnCall
|
||||
c.funcStream = c.fnStream
|
||||
|
||||
return c
|
||||
}
|
||||
|
@@ -130,7 +130,7 @@ func (p *ConnPool) Get(ctx context.Context, addr string, opts ...grpc.DialOption
|
||||
}
|
||||
p.Unlock()
|
||||
|
||||
// nolint (TODO need fix) create new conn)
|
||||
// create new conn)
|
||||
cc, err := grpc.DialContext(ctx, addr, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
44
message.go
Normal file
44
message.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"go.unistack.org/micro/v3/client"
|
||||
"go.unistack.org/micro/v3/metadata"
|
||||
)
|
||||
|
||||
type grpcEvent struct {
|
||||
payload interface{}
|
||||
topic string
|
||||
contentType string
|
||||
opts client.MessageOptions
|
||||
}
|
||||
|
||||
func newGRPCEvent(topic string, payload interface{}, contentType string, opts ...client.MessageOption) client.Message {
|
||||
options := client.NewMessageOptions(opts...)
|
||||
|
||||
if len(options.ContentType) > 0 {
|
||||
contentType = options.ContentType
|
||||
}
|
||||
|
||||
return &grpcEvent{
|
||||
payload: payload,
|
||||
topic: topic,
|
||||
contentType: contentType,
|
||||
opts: options,
|
||||
}
|
||||
}
|
||||
|
||||
func (g *grpcEvent) ContentType() string {
|
||||
return g.contentType
|
||||
}
|
||||
|
||||
func (g *grpcEvent) Topic() string {
|
||||
return g.topic
|
||||
}
|
||||
|
||||
func (g *grpcEvent) Payload() interface{} {
|
||||
return g.payload
|
||||
}
|
||||
|
||||
func (g *grpcEvent) Metadata() metadata.Metadata {
|
||||
return g.opts.Metadata
|
||||
}
|
@@ -4,7 +4,7 @@ package grpc
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.unistack.org/micro/v4/client"
|
||||
"go.unistack.org/micro/v3/client"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/encoding"
|
||||
)
|
||||
@@ -98,8 +98,8 @@ func MaxSendMsgSize(s int) client.Option {
|
||||
type grpcDialOptions struct{}
|
||||
|
||||
// DialOptions to be used to configure gRPC dial options
|
||||
func DialOptions(opts ...grpc.DialOption) client.Option {
|
||||
return func(o *client.Options) {
|
||||
func DialOptions(opts ...grpc.DialOption) client.CallOption {
|
||||
return func(o *client.CallOptions) {
|
||||
if o.Context == nil {
|
||||
o.Context = context.Background()
|
||||
}
|
||||
|
@@ -4,8 +4,8 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"go.unistack.org/micro/v4/client"
|
||||
"go.unistack.org/micro/v4/codec"
|
||||
"go.unistack.org/micro/v3/client"
|
||||
"go.unistack.org/micro/v3/codec"
|
||||
)
|
||||
|
||||
type grpcRequest struct {
|
||||
|
19
response.go
19
response.go
@@ -1,8 +1,10 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"go.unistack.org/micro/v4/codec"
|
||||
"go.unistack.org/micro/v4/metadata"
|
||||
"strings"
|
||||
|
||||
"go.unistack.org/micro/v3/codec"
|
||||
"go.unistack.org/micro/v3/metadata"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
@@ -23,19 +25,18 @@ func (r *response) Header() metadata.Metadata {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return metadata.Metadata(meta.Copy())
|
||||
md := metadata.New(len(meta))
|
||||
for k, v := range meta {
|
||||
md.Set(k, strings.Join(v, ","))
|
||||
}
|
||||
return md
|
||||
}
|
||||
|
||||
// Read the undecoded response
|
||||
func (r *response) Read() ([]byte, error) {
|
||||
f := &codec.Frame{}
|
||||
wrap := &wrapStream{r.stream}
|
||||
|
||||
_, err := wrap.Read(f.Data)
|
||||
if err != nil {
|
||||
if err := r.codec.ReadBody(&wrapStream{r.stream}, f); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return f.Data, nil
|
||||
}
|
||||
|
@@ -5,8 +5,7 @@ import (
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"go.unistack.org/micro/v4/client"
|
||||
"go.unistack.org/micro/v4/tracer"
|
||||
"go.unistack.org/micro/v3/client"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
@@ -112,12 +111,6 @@ func (g *grpcStream) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if sp, ok := tracer.SpanFromContext(g.context); ok && sp != nil {
|
||||
if g.err != nil {
|
||||
sp.SetStatus(tracer.SpanStatusError, g.err.Error())
|
||||
}
|
||||
sp.Finish()
|
||||
}
|
||||
// close the connection
|
||||
g.closed = true
|
||||
g.close(g.err)
|
||||
|
Reference in New Issue
Block a user