Compare commits

..

23 Commits

Author SHA1 Message Date
75437a46c8 prepare v4 (#139)
All checks were successful
test / test (push) Successful in 1m55s
move to v4 micro

Co-authored-by: Василий Толстов <v.tolstov@unistack.org>
Co-authored-by: Александр Толстихин <tolstihin1996@mail.ru>
Reviewed-on: #139
Co-authored-by: Evstigneev Denis <danteevstigneev@yandex.ru>
Co-committed-by: Evstigneev Denis <danteevstigneev@yandex.ru>
2025-03-03 09:53:41 +03:00
cee07d11d4 Merge pull request 'move set content-type in client publish' (#138) from devstigneev/micro-client-grpc:v3 into v3
All checks were successful
test / test (push) Successful in 3m5s
Reviewed-on: #138
2025-01-18 15:37:52 +03:00
d5360e2804 move set content-type in client publish
All checks were successful
lint / lint (pull_request) Successful in 1m37s
test / test (pull_request) Successful in 3m5s
2025-01-17 17:52:22 +03:00
2d23f347eb Merge pull request 'update hooks calling and fix errors create' (#137) from devstigneev/micro-client-grpc:v3 into v3
All checks were successful
test / test (push) Successful in 3m23s
Reviewed-on: #137
2024-12-19 23:49:49 +03:00
78664a34ed add test
All checks were successful
lint / lint (pull_request) Successful in 54s
test / test (pull_request) Successful in 2m17s
2024-12-19 23:36:02 +03:00
bec0e310e9 update hooks calling and fix errors create
Some checks failed
lint / lint (pull_request) Failing after 1m15s
test / test (pull_request) Failing after 12m32s
2024-12-19 15:20:07 +03:00
92436e9016 Update workflows (#136)
All checks were successful
test / test (push) Successful in 2m48s
- Rename workflows
- pr -> job_lint
- build -> job_test
- update golangci

Co-authored-by: Aleksandr Tolstikhin <atolstikhin@mtsbank.ru>
Reviewed-on: #136
Co-authored-by: Александр Толстихин <tolstihin1996@mail.ru>
Co-committed-by: Александр Толстихин <tolstihin1996@mail.ru>
2024-12-11 00:47:18 +03:00
25618a3859 update deps
Some checks failed
build / lint (push) Failing after 8s
build / test (push) Failing after 8s
codeql / analyze (go) (push) Failing after 10s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-09-20 18:03:39 +03:00
b22f150601 update to latest micro
Some checks failed
build / test (push) Failing after 7s
build / lint (push) Failing after 8s
codeql / analyze (go) (push) Failing after 12s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-09-17 13:11:39 +03:00
71bcb63b60 fixup trace stream
Some checks failed
codeql / analyze (go) (push) Failing after 1m44s
build / test (push) Failing after 1m49s
build / lint (push) Successful in 9m17s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-04-23 00:24:46 +03:00
698bfbc6f1 meter and tracing
Some checks failed
build / test (push) Failing after 1m45s
codeql / analyze (go) (push) Failing after 1m44s
build / lint (push) Has been cancelled
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-04-23 00:20:29 +03:00
209042de3a Merge pull request 'issue_132' (#134) from devstigneev/micro-client-grpc:issue_132 into v3
Some checks failed
build / test (push) Failing after 1m40s
codeql / analyze (go) (push) Failing after 2m4s
build / lint (push) Successful in 9m23s
Reviewed-on: #134
2024-02-29 23:07:33 +03:00
990685628d Merge remote-tracking branch 'origin/v3' into issue_132
Some checks failed
automerge / automerge (pull_request) Has been skipped
dependabot-automerge / automerge (pull_request) Has been skipped
autoapprove / autoapprove (pull_request) Successful in 10s
codeql / analyze (go) (pull_request) Has been cancelled
prbuild / test (pull_request) Has been cancelled
prbuild / lint (pull_request) Has been cancelled
# Conflicts:
#	grpc.go
2024-02-29 17:29:50 +03:00
2fbcee7b59 export grpc pool conn
Some checks failed
build / test (push) Has been cancelled
build / lint (push) Has been cancelled
codeql / analyze (go) (push) Has been cancelled
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-02-14 19:23:01 +03:00
2adba9d0da export grpc conn pool
Some checks are pending
build / test (push) Waiting to run
build / lint (push) Waiting to run
codeql / analyze (go) (push) Waiting to run
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-02-14 18:09:07 +03:00
9d70c4dd34 Merge pull request 'fixup md' (#131) from pubmdfix into v3
Some checks failed
build / test (push) Failing after 1m28s
build / lint (push) Failing after 2m36s
codeql / analyze (go) (push) Failing after 2m50s
Reviewed-on: #131
2023-12-21 00:17:46 +03:00
b9704903f2 fixup md
Some checks failed
codeql / analyze (go) (pull_request) Failing after 2m39s
prbuild / test (pull_request) Failing after 1m28s
prbuild / lint (pull_request) Failing after 2m40s
autoapprove / autoapprove (pull_request) Failing after 1m25s
automerge / automerge (pull_request) Failing after 3s
dependabot-automerge / automerge (pull_request) Has been skipped
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-12-21 00:17:25 +03:00
4eda58e404 Merge pull request 'fix MessageMetadata option' (#130) from client-metadata into v3
Some checks failed
build / test (push) Failing after 1m0s
build / lint (push) Successful in 13s
codeql / analyze (go) (push) Failing after 1m1s
Reviewed-on: #130
2023-10-26 03:15:02 +03:00
0f8d0a1123 fix MessageMetadata option
Some checks failed
codeql / analyze (go) (pull_request) Failing after 1m18s
prbuild / test (pull_request) Failing after 1m4s
prbuild / lint (pull_request) Successful in 22s
autoapprove / autoapprove (pull_request) Failing after 8s
automerge / automerge (pull_request) Failing after 3s
dependabot-automerge / automerge (pull_request) Has been skipped
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-10-26 03:12:32 +03:00
603b855e2a Merge pull request 'dialopts fix' (#129) from dialoptsfix into v3
Some checks failed
build / test (push) Failing after 6s
build / lint (push) Failing after 5s
codeql / analyze (go) (push) Failing after 6s
Reviewed-on: #129
2023-06-23 12:24:12 +03:00
f2cfd562c3 dialopts fix
Some checks failed
autoapprove / autoapprove (pull_request) Failing after 6s
automerge / automerge (pull_request) Failing after 4s
dependabot-automerge / automerge (pull_request) Has been skipped
codeql / analyze (go) (pull_request) Failing after 6s
prbuild / test (pull_request) Failing after 6s
prbuild / lint (pull_request) Failing after 5s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-06-23 12:23:44 +03:00
781cf3d719 Merge pull request 'change DialOptions signature' (#128) from quicfix into v3
Reviewed-on: #128
2023-06-22 23:44:11 +03:00
1b65507fe5 change DialOptions signature
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-06-22 23:43:38 +03:00
12 changed files with 280 additions and 1306 deletions

View File

@@ -1,9 +1,7 @@
package grpc package grpc
import ( import (
"io" "go.unistack.org/micro/v4/codec"
"go.unistack.org/micro/v3/codec"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/encoding" "google.golang.org/grpc/encoding"
) )
@@ -65,63 +63,3 @@ func (w *wrapGrpcCodec) Unmarshal(d []byte, v interface{}, opts ...codec.Option)
} }
return w.Codec.Unmarshal(d, v) 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
}

67
codec_test.go Normal file
View File

@@ -0,0 +1,67 @@
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")
}
}

View File

@@ -1,7 +1,7 @@
package grpc package grpc
import ( import (
"go.unistack.org/micro/v3/errors" "go.unistack.org/micro/v4/errors"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
) )

24
go.mod
View File

@@ -1,8 +1,24 @@
module go.unistack.org/micro-client-grpc/v3 module go.unistack.org/micro-client-grpc/v4
go 1.16 go 1.23.0
toolchain go1.23.3
require ( require (
go.unistack.org/micro/v3 v3.10.22 go.unistack.org/micro/v4 v4.1.2
google.golang.org/grpc v1.52.3 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
) )

1081
go.sum

File diff suppressed because it is too large Load Diff

264
grpc.go
View File

@@ -1,28 +1,32 @@
// Package grpc provides a gRPC client // Package grpc provides a gRPC client for micro framework
package grpc // import "go.unistack.org/micro-client-grpc/v3" package grpc
import ( import (
"context" "context"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"net" "net"
"os"
"reflect" "reflect"
"strconv"
"strings" "strings"
"sync" "sync"
"time" "time"
"go.unistack.org/micro/v3/broker" "go.unistack.org/micro/v4/client"
"go.unistack.org/micro/v3/client" "go.unistack.org/micro/v4/codec"
"go.unistack.org/micro/v3/codec" "go.unistack.org/micro/v4/errors"
"go.unistack.org/micro/v3/errors" "go.unistack.org/micro/v4/metadata"
"go.unistack.org/micro/v3/metadata" "go.unistack.org/micro/v4/options"
"go.unistack.org/micro/v3/selector" "go.unistack.org/micro/v4/selector"
"go.unistack.org/micro/v4/semconv"
"go.unistack.org/micro/v4/tracer"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/encoding" "google.golang.org/grpc/encoding"
gmetadata "google.golang.org/grpc/metadata" gmetadata "google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
) )
const ( const (
@@ -30,8 +34,10 @@ const (
) )
type grpcClient struct { type grpcClient struct {
pool *ConnPool funcCall client.FuncCall
opts client.Options funcStream client.FuncStream
pool *ConnPool
opts client.Options
sync.RWMutex sync.RWMutex
init bool init bool
} }
@@ -68,15 +74,12 @@ 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 { 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 { if md, ok := metadata.FromOutgoingContext(ctx); ok {
header = make(map[string]string, len(md)) header = metadata.Copy(md)
for k, v := range md {
header[strings.ToLower(k)] = v
}
} else { } else {
header = make(map[string]string, 2) header = make(map[string][]string, 2)
} }
if opts.RequestMetadata != nil { if opts.RequestMetadata != nil {
for k, v := range opts.RequestMetadata { for k, v := range opts.RequestMetadata {
@@ -84,16 +87,15 @@ func (g *grpcClient) call(ctx context.Context, addr string, req client.Request,
} }
} }
// set timeout in nanoseconds // set timeout in nanoseconds
header["Grpc-Timeout"] = fmt.Sprintf("%dn", opts.RequestTimeout) header["grpc-timeout"] = append(header["grpc-timeout"], fmt.Sprintf("%dn", opts.RequestTimeout))
header["timeout"] = fmt.Sprintf("%dn", opts.RequestTimeout) header["timeout"] = append(header["timeout"], fmt.Sprintf("%dn", opts.RequestTimeout))
header["content-type"] = req.ContentType() header["content-type"] = append(header["content-type"], req.ContentType())
md := gmetadata.New(header) ctx = gmetadata.NewOutgoingContext(ctx, header)
ctx = gmetadata.NewOutgoingContext(ctx, md)
cf, err := g.newCodec(req.ContentType()) cf, err := g.newCodec(req.ContentType())
if err != nil { if err != nil {
return errors.InternalServerError("go.micro.client", err.Error()) return errors.InternalServerError("go.micro.client", "%+v", err)
} }
maxRecvMsgSize := g.maxRecvMsgSizeValue() maxRecvMsgSize := g.maxRecvMsgSizeValue()
@@ -120,6 +122,9 @@ func (g *grpcClient) call(ctx context.Context, addr string, req client.Request,
grpc.WithDefaultServiceConfig(cfgService), grpc.WithDefaultServiceConfig(cfgService),
} }
if opts := g.getGrpcDialOptions(g.opts.Context); opts != nil {
grpcDialOptions = append(grpcDialOptions, opts...)
}
if opts := g.getGrpcDialOptions(opts.Context); opts != nil { if opts := g.getGrpcDialOptions(opts.Context); opts != nil {
grpcDialOptions = append(grpcDialOptions, opts...) grpcDialOptions = append(grpcDialOptions, opts...)
} }
@@ -134,7 +139,7 @@ func (g *grpcClient) call(ctx context.Context, addr string, req client.Request,
cc, err := g.pool.Get(dialCtx, addr, grpcDialOptions...) cc, err := g.pool.Get(dialCtx, addr, grpcDialOptions...)
if err != nil { if err != nil {
return errors.InternalServerError("go.micro.client", fmt.Sprintf("Error sending request: %v", err)) return errors.InternalServerError("go.micro.client", "Error sending request: %v", err)
} }
defer func() { defer func() {
// defer execution of release // defer execution of release
@@ -172,7 +177,7 @@ func (g *grpcClient) call(ctx context.Context, addr string, req client.Request,
if opts.ResponseMetadata != nil { if opts.ResponseMetadata != nil {
*opts.ResponseMetadata = metadata.New(gmd.Len()) *opts.ResponseMetadata = metadata.New(gmd.Len())
for k, v := range gmd { for k, v := range gmd {
opts.ResponseMetadata.Set(k, strings.Join(v, ",")) opts.ResponseMetadata.Append(k, v...)
} }
} }
@@ -180,31 +185,27 @@ 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 { 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 { if md, ok := metadata.FromOutgoingContext(ctx); ok {
header = make(map[string]string, len(md)) header = metadata.Copy(md)
for k, v := range md {
header[k] = v
}
} else { } else {
header = make(map[string]string) header = make(map[string][]string)
} }
// set timeout in nanoseconds // set timeout in nanoseconds
if opts.StreamTimeout > time.Duration(0) { if opts.StreamTimeout > time.Duration(0) {
header["Grpc-Timeout"] = fmt.Sprintf("%dn", opts.StreamTimeout) header["grpc-timeout"] = append(header["grpc-timeout"], fmt.Sprintf("%dn", opts.RequestTimeout))
header["timeout"] = fmt.Sprintf("%dn", opts.StreamTimeout) header["timeout"] = append(header["timeout"], fmt.Sprintf("%dn", opts.RequestTimeout))
} }
// set the content type for the request // set the content type for the request
header["content-type"] = req.ContentType() header["content-type"] = append(header["content-type"], req.ContentType())
md := gmetadata.New(header) ctx = gmetadata.NewOutgoingContext(ctx, header)
ctx = gmetadata.NewOutgoingContext(ctx, md)
cf, err := g.newCodec(req.ContentType()) cf, err := g.newCodec(req.ContentType())
if err != nil { if err != nil {
return errors.InternalServerError("go.micro.client", err.Error()) return errors.InternalServerError("go.micro.client", "%+v", err)
} }
var dialCtx context.Context var dialCtx context.Context
@@ -245,7 +246,7 @@ func (g *grpcClient) stream(ctx context.Context, addr string, req client.Request
cc, err := g.pool.Get(dialCtx, addr, grpcDialOptions...) cc, err := g.pool.Get(dialCtx, addr, grpcDialOptions...)
if err != nil { if err != nil {
return errors.InternalServerError("go.micro.client", fmt.Sprintf("Error sending request: %v", err)) return errors.InternalServerError("go.micro.client", "Error sending request: %v", err)
} }
desc := &grpc.StreamDesc{ desc := &grpc.StreamDesc{
@@ -278,7 +279,7 @@ func (g *grpcClient) stream(ctx context.Context, addr string, req client.Request
// release the connection // release the connection
g.pool.Put(cc, err) g.pool.Put(cc, err)
// now return the error // now return the error
return errors.InternalServerError("go.micro.client", fmt.Sprintf("Error creating stream: %v", err)) return errors.InternalServerError("go.micro.client", "Error creating stream: %v", err)
} }
// set request codec // set request codec
@@ -403,25 +404,17 @@ func (g *grpcClient) Init(opts ...client.Option) error {
g.pool.Unlock() g.pool.Unlock()
} }
if err := g.opts.Broker.Init(); err != nil { g.funcCall = g.fnCall
return err g.funcStream = g.fnStream
}
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.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)
}
})
g.init = true g.init = true
return nil return nil
@@ -431,10 +424,6 @@ func (g *grpcClient) Options() client.Options {
return g.opts 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 { func (g *grpcClient) NewRequest(service, method string, req interface{}, reqOpts ...client.RequestOption) client.Request {
return newGRPCRequest(service, method, req, g.opts.ContentType, reqOpts...) return newGRPCRequest(service, method, req, g.opts.ContentType, reqOpts...)
} }
@@ -445,6 +434,32 @@ func (g *grpcClient) Call(ctx context.Context, req client.Request, rsp interface
} else if rsp == nil { } else if rsp == nil {
return errors.InternalServerError("go.micro.client", "rsp is 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 // make a copy of call opts
callOpts := g.opts.CallOptions callOpts := g.opts.CallOptions
@@ -476,11 +491,6 @@ func (g *grpcClient) Call(ctx context.Context, req client.Request, rsp interface
// make copy of call method // make copy of call method
gcall := g.call 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 // use the router passed as a call option, or fallback to the rpc clients router
if callOpts.Router == nil { if callOpts.Router == nil {
callOpts.Router = g.opts.Router callOpts.Router = g.opts.Router
@@ -502,7 +512,7 @@ func (g *grpcClient) Call(ctx context.Context, req client.Request, rsp interface
// call backoff first. Someone may want an initial start delay // call backoff first. Someone may want an initial start delay
t, err := callOpts.Backoff(ctx, req, i) t, err := callOpts.Backoff(ctx, req, i)
if err != nil { if err != nil {
return errors.InternalServerError("go.micro.client", err.Error()) return errors.InternalServerError("go.micro.client", "%+v", err)
} }
// only sleep if greater than 0 // only sleep if greater than 0
@@ -517,7 +527,7 @@ func (g *grpcClient) Call(ctx context.Context, req client.Request, rsp interface
// TODO apply any filtering here // TODO apply any filtering here
routes, err = g.opts.Lookup(ctx, req, callOpts) routes, err = g.opts.Lookup(ctx, req, callOpts)
if err != nil { if err != nil {
return errors.InternalServerError("go.micro.client", err.Error()) return errors.InternalServerError("go.micro.client", "%+v", err)
} }
// balance the list of nodes // balance the list of nodes
@@ -580,6 +590,31 @@ func (g *grpcClient) Call(ctx context.Context, req client.Request, rsp interface
} }
func (g *grpcClient) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Stream, error) { 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 // make a copy of call opts
callOpts := g.opts.CallOptions callOpts := g.opts.CallOptions
for _, opt := range opts { for _, opt := range opts {
@@ -598,11 +633,6 @@ func (g *grpcClient) Stream(ctx context.Context, req client.Request, opts ...cli
// make a copy of stream // make a copy of stream
gstream := g.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 // use the router passed as a call option, or fallback to the rpc clients router
if callOpts.Router == nil { if callOpts.Router == nil {
callOpts.Router = g.opts.Router callOpts.Router = g.opts.Router
@@ -624,7 +654,7 @@ func (g *grpcClient) Stream(ctx context.Context, req client.Request, opts ...cli
// call backoff first. Someone may want an initial start delay // call backoff first. Someone may want an initial start delay
t, err := callOpts.Backoff(ctx, req, i) t, err := callOpts.Backoff(ctx, req, i)
if err != nil { if err != nil {
return nil, errors.InternalServerError("go.micro.client", err.Error()) return nil, errors.InternalServerError("go.micro.client", "%+v", err)
} }
// only sleep if greater than 0 // only sleep if greater than 0
@@ -639,7 +669,7 @@ func (g *grpcClient) Stream(ctx context.Context, req client.Request, opts ...cli
// TODO apply any filtering here // TODO apply any filtering here
routes, err = g.opts.Lookup(ctx, req, callOpts) routes, err = g.opts.Lookup(ctx, req, callOpts)
if err != nil { if err != nil {
return nil, errors.InternalServerError("go.micro.client", err.Error()) return nil, errors.InternalServerError("go.micro.client", "%+v", err)
} }
// balance the list of nodes // balance the list of nodes
@@ -712,71 +742,6 @@ func (g *grpcClient) Stream(ctx context.Context, req client.Request, opts ...cli
return nil, grr 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 { func (g *grpcClient) String() string {
return "grpc" return "grpc"
} }
@@ -836,22 +801,16 @@ func NewClient(opts ...client.Option) client.Client {
options.ContentType = DefaultContentType options.ContentType = DefaultContentType
} }
rc := &grpcClient{ c := &grpcClient{
opts: options, opts: options,
} }
rc.pool = NewConnPool(options.PoolSize, options.PoolTTL, rc.poolMaxIdle(), rc.poolMaxStreams()) c.pool = NewConnPool(options.PoolSize, options.PoolTTL, c.poolMaxIdle(), c.poolMaxStreams())
c := client.Client(rc)
// wrap in reverse if c.opts.Context != nil {
for i := len(options.Wrappers); i > 0; i-- { if codecs, ok := c.opts.Context.Value(codecsKey{}).(map[string]encoding.Codec); ok && codecs != nil {
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 { for k, v := range codecs {
rc.opts.Codecs[k] = &wrapGrpcCodec{v} c.opts.Codecs[k] = &wrapGrpcCodec{v}
} }
} }
} }
@@ -860,5 +819,8 @@ func NewClient(opts ...client.Option) client.Client {
encoding.RegisterCodec(&wrapMicroCodec{k}) encoding.RegisterCodec(&wrapMicroCodec{k})
} }
c.funcCall = c.fnCall
c.funcStream = c.fnStream
return c return c
} }

View File

@@ -130,7 +130,7 @@ func (p *ConnPool) Get(ctx context.Context, addr string, opts ...grpc.DialOption
} }
p.Unlock() p.Unlock()
// create new conn) // nolint (TODO need fix) create new conn)
cc, err := grpc.DialContext(ctx, addr, opts...) cc, err := grpc.DialContext(ctx, addr, opts...)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -1,44 +0,0 @@
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
}

View File

@@ -4,7 +4,7 @@ package grpc
import ( import (
"context" "context"
"go.unistack.org/micro/v3/client" "go.unistack.org/micro/v4/client"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/encoding" "google.golang.org/grpc/encoding"
) )
@@ -98,8 +98,8 @@ func MaxSendMsgSize(s int) client.Option {
type grpcDialOptions struct{} type grpcDialOptions struct{}
// DialOptions to be used to configure gRPC dial options // DialOptions to be used to configure gRPC dial options
func DialOptions(opts ...grpc.DialOption) client.CallOption { func DialOptions(opts ...grpc.DialOption) client.Option {
return func(o *client.CallOptions) { return func(o *client.Options) {
if o.Context == nil { if o.Context == nil {
o.Context = context.Background() o.Context = context.Background()
} }

View File

@@ -4,8 +4,8 @@ import (
"fmt" "fmt"
"strings" "strings"
"go.unistack.org/micro/v3/client" "go.unistack.org/micro/v4/client"
"go.unistack.org/micro/v3/codec" "go.unistack.org/micro/v4/codec"
) )
type grpcRequest struct { type grpcRequest struct {

View File

@@ -1,10 +1,8 @@
package grpc package grpc
import ( import (
"strings" "go.unistack.org/micro/v4/codec"
"go.unistack.org/micro/v4/metadata"
"go.unistack.org/micro/v3/codec"
"go.unistack.org/micro/v3/metadata"
"google.golang.org/grpc" "google.golang.org/grpc"
) )
@@ -25,18 +23,19 @@ func (r *response) Header() metadata.Metadata {
if err != nil { if err != nil {
return nil return nil
} }
md := metadata.New(len(meta))
for k, v := range meta { return metadata.Metadata(meta.Copy())
md.Set(k, strings.Join(v, ","))
}
return md
} }
// Read the undecoded response // Read the undecoded response
func (r *response) Read() ([]byte, error) { func (r *response) Read() ([]byte, error) {
f := &codec.Frame{} f := &codec.Frame{}
if err := r.codec.ReadBody(&wrapStream{r.stream}, f); err != nil { wrap := &wrapStream{r.stream}
_, err := wrap.Read(f.Data)
if err != nil {
return nil, err return nil, err
} }
return f.Data, nil return f.Data, nil
} }

View File

@@ -5,7 +5,8 @@ import (
"io" "io"
"sync" "sync"
"go.unistack.org/micro/v3/client" "go.unistack.org/micro/v4/client"
"go.unistack.org/micro/v4/tracer"
"google.golang.org/grpc" "google.golang.org/grpc"
) )
@@ -111,6 +112,12 @@ func (g *grpcStream) Close() error {
return nil 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 // close the connection
g.closed = true g.closed = true
g.close(g.err) g.close(g.err)