gitee.com/liuxuezhan/go-micro-v1.18.0@v1.0.0/client/grpc/grpc.go (about) 1 // Package grpc provides a gRPC client 2 package grpc 3 4 import ( 5 "context" 6 "crypto/tls" 7 "fmt" 8 "os" 9 "sync" 10 "time" 11 12 "gitee.com/liuxuezhan/go-micro-v1.18.0/broker" 13 "gitee.com/liuxuezhan/go-micro-v1.18.0/client" 14 "gitee.com/liuxuezhan/go-micro-v1.18.0/client/selector" 15 "gitee.com/liuxuezhan/go-micro-v1.18.0/codec" 16 raw "gitee.com/liuxuezhan/go-micro-v1.18.0/codec/bytes" 17 "gitee.com/liuxuezhan/go-micro-v1.18.0/errors" 18 "gitee.com/liuxuezhan/go-micro-v1.18.0/metadata" 19 "gitee.com/liuxuezhan/go-micro-v1.18.0/registry" 20 "gitee.com/liuxuezhan/go-micro-v1.18.0/transport" 21 22 "google.golang.org/grpc" 23 "google.golang.org/grpc/credentials" 24 "google.golang.org/grpc/encoding" 25 gmetadata "google.golang.org/grpc/metadata" 26 ) 27 28 type grpcClient struct { 29 once sync.Once 30 opts client.Options 31 pool *pool 32 } 33 34 func init() { 35 encoding.RegisterCodec(wrapCodec{jsonCodec{}}) 36 encoding.RegisterCodec(wrapCodec{protoCodec{}}) 37 encoding.RegisterCodec(wrapCodec{bytesCodec{}}) 38 } 39 40 // secure returns the dial option for whether its a secure or insecure connection 41 func (g *grpcClient) secure() grpc.DialOption { 42 if g.opts.Context != nil { 43 if v := g.opts.Context.Value(tlsAuth{}); v != nil { 44 tls := v.(*tls.Config) 45 creds := credentials.NewTLS(tls) 46 return grpc.WithTransportCredentials(creds) 47 } 48 } 49 return grpc.WithInsecure() 50 } 51 52 func (g *grpcClient) next(request client.Request, opts client.CallOptions) (selector.Next, error) { 53 service := request.Service() 54 55 // get proxy 56 if prx := os.Getenv("MICRO_PROXY"); len(prx) > 0 { 57 service = prx 58 } 59 60 // get proxy address 61 if prx := os.Getenv("MICRO_PROXY_ADDRESS"); len(prx) > 0 { 62 opts.Address = []string{prx} 63 } 64 65 // return remote address 66 if len(opts.Address) > 0 { 67 return func() (*registry.Node, error) { 68 return ®istry.Node{ 69 Address: opts.Address[0], 70 }, nil 71 }, nil 72 } 73 74 // get next nodes from the selector 75 next, err := g.opts.Selector.Select(service, opts.SelectOptions...) 76 if err != nil { 77 if err == selector.ErrNotFound { 78 return nil, errors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error()) 79 } 80 return nil, errors.InternalServerError("go.micro.client", "error selecting %s node: %s", service, err.Error()) 81 } 82 83 return next, nil 84 } 85 86 func (g *grpcClient) call(ctx context.Context, node *registry.Node, req client.Request, rsp interface{}, opts client.CallOptions) error { 87 address := node.Address 88 89 header := make(map[string]string) 90 if md, ok := metadata.FromContext(ctx); ok { 91 for k, v := range md { 92 header[k] = v 93 } 94 } 95 96 // set timeout in nanoseconds 97 header["timeout"] = fmt.Sprintf("%d", opts.RequestTimeout) 98 // set the content type for the request 99 header["x-content-type"] = req.ContentType() 100 101 md := gmetadata.New(header) 102 ctx = gmetadata.NewOutgoingContext(ctx, md) 103 104 cf, err := g.newGRPCCodec(req.ContentType()) 105 if err != nil { 106 return errors.InternalServerError("go.micro.client", err.Error()) 107 } 108 109 maxRecvMsgSize := g.maxRecvMsgSizeValue() 110 maxSendMsgSize := g.maxSendMsgSizeValue() 111 112 var grr error 113 114 grpcDialOptions := []grpc.DialOption{ 115 grpc.WithDefaultCallOptions(grpc.ForceCodec(cf)), 116 grpc.WithTimeout(opts.DialTimeout), 117 g.secure(), 118 grpc.WithDefaultCallOptions( 119 grpc.MaxCallRecvMsgSize(maxRecvMsgSize), 120 grpc.MaxCallSendMsgSize(maxSendMsgSize), 121 ), 122 } 123 124 if opts := g.getGrpcDialOptions(); opts != nil { 125 grpcDialOptions = append(grpcDialOptions, opts...) 126 } 127 128 cc, err := g.pool.getConn(address, grpcDialOptions...) 129 if err != nil { 130 return errors.InternalServerError("go.micro.client", fmt.Sprintf("Error sending request: %v", err)) 131 } 132 defer func() { 133 // defer execution of release 134 g.pool.release(address, cc, grr) 135 }() 136 137 ch := make(chan error, 1) 138 139 go func() { 140 grpcCallOptions := []grpc.CallOption{grpc.CallContentSubtype(cf.Name())} 141 if opts := g.getGrpcCallOptions(); opts != nil { 142 grpcCallOptions = append(grpcCallOptions, opts...) 143 } 144 err := cc.Invoke(ctx, methodToGRPC(req.Service(), req.Endpoint()), req.Body(), rsp, grpcCallOptions...) 145 ch <- microError(err) 146 }() 147 148 select { 149 case err := <-ch: 150 grr = err 151 case <-ctx.Done(): 152 grr = ctx.Err() 153 } 154 155 return grr 156 } 157 158 func (g *grpcClient) stream(ctx context.Context, node *registry.Node, req client.Request, opts client.CallOptions) (client.Stream, error) { 159 address := node.Address 160 161 header := make(map[string]string) 162 if md, ok := metadata.FromContext(ctx); ok { 163 for k, v := range md { 164 header[k] = v 165 } 166 } 167 168 // set timeout in nanoseconds 169 header["timeout"] = fmt.Sprintf("%d", opts.RequestTimeout) 170 // set the content type for the request 171 header["x-content-type"] = req.ContentType() 172 173 md := gmetadata.New(header) 174 ctx = gmetadata.NewOutgoingContext(ctx, md) 175 176 cf, err := g.newGRPCCodec(req.ContentType()) 177 if err != nil { 178 return nil, errors.InternalServerError("go.micro.client", err.Error()) 179 } 180 181 var dialCtx context.Context 182 var cancel context.CancelFunc 183 if opts.DialTimeout >= 0 { 184 dialCtx, cancel = context.WithTimeout(ctx, opts.DialTimeout) 185 } else { 186 dialCtx, cancel = context.WithCancel(ctx) 187 } 188 defer cancel() 189 190 wc := wrapCodec{cf} 191 192 grpcDialOptions := []grpc.DialOption{ 193 grpc.WithDefaultCallOptions(grpc.ForceCodec(wc)), 194 g.secure(), 195 } 196 197 if opts := g.getGrpcDialOptions(); opts != nil { 198 grpcDialOptions = append(grpcDialOptions, opts...) 199 } 200 201 cc, err := grpc.DialContext(dialCtx, address, grpcDialOptions...) 202 if err != nil { 203 return nil, errors.InternalServerError("go.micro.client", fmt.Sprintf("Error sending request: %v", err)) 204 } 205 206 desc := &grpc.StreamDesc{ 207 StreamName: req.Service() + req.Endpoint(), 208 ClientStreams: true, 209 ServerStreams: true, 210 } 211 212 grpcCallOptions := []grpc.CallOption{} 213 if opts := g.getGrpcCallOptions(); opts != nil { 214 grpcCallOptions = append(grpcCallOptions, opts...) 215 } 216 st, err := cc.NewStream(ctx, desc, methodToGRPC(req.Service(), req.Endpoint()), grpcCallOptions...) 217 if err != nil { 218 return nil, errors.InternalServerError("go.micro.client", fmt.Sprintf("Error creating stream: %v", err)) 219 } 220 221 codec := &grpcCodec{ 222 s: st, 223 c: wc, 224 } 225 226 // set request codec 227 if r, ok := req.(*grpcRequest); ok { 228 r.codec = codec 229 } 230 231 rsp := &response{ 232 conn: cc, 233 stream: st, 234 codec: cf, 235 gcodec: codec, 236 } 237 238 return &grpcStream{ 239 context: ctx, 240 request: req, 241 response: rsp, 242 stream: st, 243 conn: cc, 244 }, nil 245 } 246 247 func (g *grpcClient) maxRecvMsgSizeValue() int { 248 if g.opts.Context == nil { 249 return DefaultMaxRecvMsgSize 250 } 251 v := g.opts.Context.Value(maxRecvMsgSizeKey{}) 252 if v == nil { 253 return DefaultMaxRecvMsgSize 254 } 255 return v.(int) 256 } 257 258 func (g *grpcClient) maxSendMsgSizeValue() int { 259 if g.opts.Context == nil { 260 return DefaultMaxSendMsgSize 261 } 262 v := g.opts.Context.Value(maxSendMsgSizeKey{}) 263 if v == nil { 264 return DefaultMaxSendMsgSize 265 } 266 return v.(int) 267 } 268 269 func (g *grpcClient) newGRPCCodec(contentType string) (encoding.Codec, error) { 270 codecs := make(map[string]encoding.Codec) 271 if g.opts.Context != nil { 272 if v := g.opts.Context.Value(codecsKey{}); v != nil { 273 codecs = v.(map[string]encoding.Codec) 274 } 275 } 276 if c, ok := codecs[contentType]; ok { 277 return wrapCodec{c}, nil 278 } 279 if c, ok := defaultGRPCCodecs[contentType]; ok { 280 return wrapCodec{c}, nil 281 } 282 return nil, fmt.Errorf("Unsupported Content-Type: %s", contentType) 283 } 284 285 func (g *grpcClient) Init(opts ...client.Option) error { 286 size := g.opts.PoolSize 287 ttl := g.opts.PoolTTL 288 289 for _, o := range opts { 290 o(&g.opts) 291 } 292 293 // update pool configuration if the options changed 294 if size != g.opts.PoolSize || ttl != g.opts.PoolTTL { 295 g.pool.Lock() 296 g.pool.size = g.opts.PoolSize 297 g.pool.ttl = int64(g.opts.PoolTTL.Seconds()) 298 g.pool.Unlock() 299 } 300 301 return nil 302 } 303 304 func (g *grpcClient) Options() client.Options { 305 return g.opts 306 } 307 308 func (g *grpcClient) NewMessage(topic string, msg interface{}, opts ...client.MessageOption) client.Message { 309 return newGRPCEvent(topic, msg, g.opts.ContentType, opts...) 310 } 311 312 func (g *grpcClient) NewRequest(service, method string, req interface{}, reqOpts ...client.RequestOption) client.Request { 313 return newGRPCRequest(service, method, req, g.opts.ContentType, reqOpts...) 314 } 315 316 func (g *grpcClient) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error { 317 // make a copy of call opts 318 callOpts := g.opts.CallOptions 319 for _, opt := range opts { 320 opt(&callOpts) 321 } 322 323 next, err := g.next(req, callOpts) 324 if err != nil { 325 return err 326 } 327 328 // check if we already have a deadline 329 d, ok := ctx.Deadline() 330 if !ok { 331 // no deadline so we create a new one 332 var cancel context.CancelFunc 333 ctx, cancel = context.WithTimeout(ctx, callOpts.RequestTimeout) 334 defer cancel() 335 } else { 336 // got a deadline so no need to setup context 337 // but we need to set the timeout we pass along 338 opt := client.WithRequestTimeout(time.Until(d)) 339 opt(&callOpts) 340 } 341 342 // should we noop right here? 343 select { 344 case <-ctx.Done(): 345 return errors.New("go.micro.client", fmt.Sprintf("%v", ctx.Err()), 408) 346 default: 347 } 348 349 // make copy of call method 350 gcall := g.call 351 352 // wrap the call in reverse 353 for i := len(callOpts.CallWrappers); i > 0; i-- { 354 gcall = callOpts.CallWrappers[i-1](gcall) 355 } 356 357 // return errors.New("go.micro.client", "request timeout", 408) 358 call := func(i int) error { 359 // call backoff first. Someone may want an initial start delay 360 t, err := callOpts.Backoff(ctx, req, i) 361 if err != nil { 362 return errors.InternalServerError("go.micro.client", err.Error()) 363 } 364 365 // only sleep if greater than 0 366 if t.Seconds() > 0 { 367 time.Sleep(t) 368 } 369 370 // select next node 371 node, err := next() 372 service := req.Service() 373 if err != nil { 374 if err == selector.ErrNotFound { 375 return errors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error()) 376 } 377 return errors.InternalServerError("go.micro.client", "error selecting %s node: %s", service, err.Error()) 378 } 379 380 // make the call 381 err = gcall(ctx, node, req, rsp, callOpts) 382 g.opts.Selector.Mark(service, node, err) 383 return err 384 } 385 386 ch := make(chan error, callOpts.Retries+1) 387 var gerr error 388 389 for i := 0; i <= callOpts.Retries; i++ { 390 go func(i int) { 391 ch <- call(i) 392 }(i) 393 394 select { 395 case <-ctx.Done(): 396 return errors.New("go.micro.client", fmt.Sprintf("%v", ctx.Err()), 408) 397 case err := <-ch: 398 // if the call succeeded lets bail early 399 if err == nil { 400 return nil 401 } 402 403 retry, rerr := callOpts.Retry(ctx, req, i, err) 404 if rerr != nil { 405 return rerr 406 } 407 408 if !retry { 409 return err 410 } 411 412 gerr = err 413 } 414 } 415 416 return gerr 417 } 418 419 func (g *grpcClient) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Stream, error) { 420 // make a copy of call opts 421 callOpts := g.opts.CallOptions 422 for _, opt := range opts { 423 opt(&callOpts) 424 } 425 426 next, err := g.next(req, callOpts) 427 if err != nil { 428 return nil, err 429 } 430 431 // #200 - streams shouldn't have a request timeout set on the context 432 433 // should we noop right here? 434 select { 435 case <-ctx.Done(): 436 return nil, errors.New("go.micro.client", fmt.Sprintf("%v", ctx.Err()), 408) 437 default: 438 } 439 440 call := func(i int) (client.Stream, error) { 441 // call backoff first. Someone may want an initial start delay 442 t, err := callOpts.Backoff(ctx, req, i) 443 if err != nil { 444 return nil, errors.InternalServerError("go.micro.client", err.Error()) 445 } 446 447 // only sleep if greater than 0 448 if t.Seconds() > 0 { 449 time.Sleep(t) 450 } 451 452 node, err := next() 453 service := req.Service() 454 if err != nil { 455 if err == selector.ErrNotFound { 456 return nil, errors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error()) 457 } 458 return nil, errors.InternalServerError("go.micro.client", "error selecting %s node: %s", service, err.Error()) 459 } 460 461 stream, err := g.stream(ctx, node, req, callOpts) 462 g.opts.Selector.Mark(service, node, err) 463 return stream, err 464 } 465 466 type response struct { 467 stream client.Stream 468 err error 469 } 470 471 ch := make(chan response, callOpts.Retries+1) 472 var grr error 473 474 for i := 0; i <= callOpts.Retries; i++ { 475 go func(i int) { 476 s, err := call(i) 477 ch <- response{s, err} 478 }(i) 479 480 select { 481 case <-ctx.Done(): 482 return nil, errors.New("go.micro.client", fmt.Sprintf("%v", ctx.Err()), 408) 483 case rsp := <-ch: 484 // if the call succeeded lets bail early 485 if rsp.err == nil { 486 return rsp.stream, nil 487 } 488 489 retry, rerr := callOpts.Retry(ctx, req, i, err) 490 if rerr != nil { 491 return nil, rerr 492 } 493 494 if !retry { 495 return nil, rsp.err 496 } 497 498 grr = rsp.err 499 } 500 } 501 502 return nil, grr 503 } 504 505 func (g *grpcClient) Publish(ctx context.Context, p client.Message, opts ...client.PublishOption) error { 506 var options client.PublishOptions 507 for _, o := range opts { 508 o(&options) 509 } 510 511 md, ok := metadata.FromContext(ctx) 512 if !ok { 513 md = make(map[string]string) 514 } 515 md["Content-Type"] = p.ContentType() 516 md["Micro-Topic"] = p.Topic() 517 518 cf, err := g.newGRPCCodec(p.ContentType()) 519 if err != nil { 520 return errors.InternalServerError("go.micro.client", err.Error()) 521 } 522 523 var body []byte 524 525 // passed in raw data 526 if d, ok := p.Payload().(*raw.Frame); ok { 527 body = d.Data 528 } else { 529 // set the body 530 b, err := cf.Marshal(p.Payload()) 531 if err != nil { 532 return errors.InternalServerError("go.micro.client", err.Error()) 533 } 534 body = b 535 } 536 537 g.once.Do(func() { 538 g.opts.Broker.Connect() 539 }) 540 541 topic := p.Topic() 542 543 // get proxy topic 544 if prx := os.Getenv("MICRO_PROXY"); len(prx) > 0 { 545 options.Exchange = prx 546 } 547 548 // get the exchange 549 if len(options.Exchange) > 0 { 550 topic = options.Exchange 551 } 552 553 return g.opts.Broker.Publish(topic, &broker.Message{ 554 Header: md, 555 Body: body, 556 }) 557 } 558 559 func (g *grpcClient) String() string { 560 return "grpc" 561 } 562 563 func (g *grpcClient) getGrpcDialOptions() []grpc.DialOption { 564 if g.opts.CallOptions.Context == nil { 565 return nil 566 } 567 568 v := g.opts.CallOptions.Context.Value(grpcDialOptions{}) 569 570 if v == nil { 571 return nil 572 } 573 574 opts, ok := v.([]grpc.DialOption) 575 576 if !ok { 577 return nil 578 } 579 580 return opts 581 } 582 583 func (g *grpcClient) getGrpcCallOptions() []grpc.CallOption { 584 if g.opts.CallOptions.Context == nil { 585 return nil 586 } 587 588 v := g.opts.CallOptions.Context.Value(grpcCallOptions{}) 589 590 if v == nil { 591 return nil 592 } 593 594 opts, ok := v.([]grpc.CallOption) 595 596 if !ok { 597 return nil 598 } 599 600 return opts 601 } 602 603 func newClient(opts ...client.Option) client.Client { 604 options := client.Options{ 605 Codecs: make(map[string]codec.NewCodec), 606 CallOptions: client.CallOptions{ 607 Backoff: client.DefaultBackoff, 608 Retry: client.DefaultRetry, 609 Retries: client.DefaultRetries, 610 RequestTimeout: client.DefaultRequestTimeout, 611 DialTimeout: transport.DefaultDialTimeout, 612 }, 613 PoolSize: client.DefaultPoolSize, 614 PoolTTL: client.DefaultPoolTTL, 615 } 616 617 for _, o := range opts { 618 o(&options) 619 } 620 621 if len(options.ContentType) == 0 { 622 options.ContentType = "application/grpc+proto" 623 } 624 625 if options.Broker == nil { 626 options.Broker = broker.DefaultBroker 627 } 628 629 if options.Registry == nil { 630 options.Registry = registry.DefaultRegistry 631 } 632 633 if options.Selector == nil { 634 options.Selector = selector.NewSelector( 635 selector.Registry(options.Registry), 636 ) 637 } 638 639 rc := &grpcClient{ 640 once: sync.Once{}, 641 opts: options, 642 pool: newPool(options.PoolSize, options.PoolTTL), 643 } 644 645 c := client.Client(rc) 646 647 // wrap in reverse 648 for i := len(options.Wrappers); i > 0; i-- { 649 c = options.Wrappers[i-1](c) 650 } 651 652 return c 653 } 654 655 func NewClient(opts ...client.Option) client.Client { 656 return newClient(opts...) 657 }