trpc.group/trpc-go/trpc-go@v1.0.3/client/options.go (about) 1 // 2 // 3 // Tencent is pleased to support the open source community by making tRPC available. 4 // 5 // Copyright (C) 2023 THL A29 Limited, a Tencent company. 6 // All rights reserved. 7 // 8 // If you have downloaded a copy of the tRPC source code from Tencent, 9 // please note that tRPC source code is licensed under the Apache 2.0 License, 10 // A copy of the Apache 2.0 License is included in this file. 11 // 12 // 13 14 package client 15 16 import ( 17 "context" 18 "fmt" 19 "strings" 20 "sync" 21 "time" 22 23 "trpc.group/trpc-go/trpc-go/codec" 24 "trpc.group/trpc-go/trpc-go/filter" 25 "trpc.group/trpc-go/trpc-go/internal/attachment" 26 "trpc.group/trpc-go/trpc-go/naming/circuitbreaker" 27 "trpc.group/trpc-go/trpc-go/naming/discovery" 28 "trpc.group/trpc-go/trpc-go/naming/loadbalance" 29 "trpc.group/trpc-go/trpc-go/naming/registry" 30 "trpc.group/trpc-go/trpc-go/naming/selector" 31 "trpc.group/trpc-go/trpc-go/naming/servicerouter" 32 "trpc.group/trpc-go/trpc-go/pool/connpool" 33 "trpc.group/trpc-go/trpc-go/pool/multiplexed" 34 "trpc.group/trpc-go/trpc-go/transport" 35 ) 36 37 // Options are clientside options. 38 type Options struct { 39 ServiceName string // Backend service name. 40 CallerServiceName string // Service name of caller itself. 41 CalleeMethod string // Callee method name, usually used for metrics. 42 Timeout time.Duration // Timeout. 43 44 // Target is address of backend service: name://endpoint, 45 // also compatible with old addressing like ip://ip:port 46 Target string 47 endpoint string // The same as service name if target is not set. 48 49 Network string 50 Protocol string 51 CallType codec.RequestType // Type of request, referring to transport.RequestType. 52 CallOptions []transport.RoundTripOption // Options for client transport to call server. 53 Transport transport.ClientTransport 54 EnableMultiplexed bool 55 StreamTransport transport.ClientStreamTransport 56 57 SelectOptions []selector.Option 58 Selector selector.Selector 59 DisableServiceRouter bool 60 shouldErrReportToSelector func(error) bool 61 62 CurrentSerializationType int 63 CurrentCompressType int 64 SerializationType int 65 CompressType int 66 67 Codec codec.Codec 68 MetaData codec.MetaData 69 ClientStreamQueueSize int // Size of client stream's queue. 70 71 Filters filter.ClientChain // Filter chain. 72 FilterNames []string // The name of filters. 73 DisableFilter bool // Whether to disable filter. 74 selectorFilterPosFixed bool // Whether selector filter pos is fixed,if not, put it to the end. 75 76 ReqHead interface{} // Allow custom req head. 77 RspHead interface{} // Allow custom rsp head. 78 Node *onceNode // For getting node info. 79 80 MaxWindowSize uint32 // Max size of stream receiver's window. 81 SControl SendControl // Sender's flow control. 82 RControl RecvControl // Receiver's flow control. 83 StreamFilters StreamFilterChain // Stream filter chain. 84 85 fixTimeout func(error) error 86 87 attachment *attachment.Attachment 88 } 89 90 type onceNode struct { 91 *registry.Node 92 once sync.Once 93 } 94 95 func (n *onceNode) set(node *registry.Node, address string, cost time.Duration) { 96 if n == nil { 97 return 98 } 99 n.once.Do(func() { 100 *n.Node = *node 101 n.Node.Address = address 102 n.Node.CostTime = cost 103 }) 104 } 105 106 // Option sets client options. 107 type Option func(*Options) 108 109 // WithNamespace returns an Option that sets namespace of backend service: Production/Development. 110 func WithNamespace(namespace string) Option { 111 return func(o *Options) { 112 o.SelectOptions = append(o.SelectOptions, selector.WithNamespace(namespace)) 113 } 114 } 115 116 // WithClientStreamQueueSize returns an Option that sets the size of client stream's buffer queue, 117 // that is, max number of received messages to put into the channel. 118 func WithClientStreamQueueSize(size int) Option { 119 return func(o *Options) { 120 o.ClientStreamQueueSize = size 121 } 122 } 123 124 // WithServiceName returns an Option that sets service name of backend service. 125 func WithServiceName(s string) Option { 126 return func(o *Options) { 127 o.ServiceName = s 128 o.endpoint = s 129 } 130 } 131 132 // WithCallerServiceName returns an Option that sets service name of the caller service itself. 133 func WithCallerServiceName(s string) Option { 134 return func(o *Options) { 135 o.CallerServiceName = s 136 o.SelectOptions = append(o.SelectOptions, selector.WithSourceServiceName(s)) 137 } 138 } 139 140 // WithCallerNamespace returns an Option that sets namespace of the caller service itself. 141 func WithCallerNamespace(s string) Option { 142 return func(o *Options) { 143 o.SelectOptions = append(o.SelectOptions, selector.WithSourceNamespace(s)) 144 } 145 } 146 147 // WithDisableFilter returns an Option that sets whether to disable filter. 148 // It's used when a plugin setup and need a client to send request 149 // but filters' initialization has not been done. 150 func WithDisableFilter() Option { 151 return func(o *Options) { 152 o.DisableFilter = true 153 } 154 } 155 156 // WithDisableServiceRouter returns an Option that disables service router. 157 func WithDisableServiceRouter() Option { 158 return func(o *Options) { 159 o.DisableServiceRouter = true 160 o.SelectOptions = append(o.SelectOptions, selector.WithDisableServiceRouter()) 161 } 162 } 163 164 // WithEnvKey returns an Option that sets env key. 165 func WithEnvKey(key string) Option { 166 return func(o *Options) { 167 o.SelectOptions = append(o.SelectOptions, selector.WithEnvKey(key)) 168 } 169 } 170 171 // WithCallerEnvName returns an Option that sets env name of the caller service itself. 172 func WithCallerEnvName(envName string) Option { 173 return func(o *Options) { 174 o.SelectOptions = append(o.SelectOptions, selector.WithSourceEnvName(envName)) 175 } 176 } 177 178 // WithCallerSetName returns an Option that sets "Set" of the caller service itself. 179 func WithCallerSetName(setName string) Option { 180 return func(o *Options) { 181 o.SelectOptions = append(o.SelectOptions, selector.WithSourceSetName(setName)) 182 } 183 } 184 185 // WithCalleeSetName returns an Option that sets "Set" of the callee service. 186 func WithCalleeSetName(setName string) Option { 187 return func(o *Options) { 188 o.SelectOptions = append(o.SelectOptions, selector.WithDestinationSetName(setName)) 189 } 190 } 191 192 // WithCalleeEnvName returns an Option that sets env name of the callee service. 193 func WithCalleeEnvName(envName string) Option { 194 return func(o *Options) { 195 o.SelectOptions = append(o.SelectOptions, selector.WithDestinationEnvName(envName)) 196 } 197 } 198 199 // WithCalleeMethod returns an Option that sets callee method name. 200 func WithCalleeMethod(method string) Option { 201 return func(o *Options) { 202 o.CalleeMethod = method 203 } 204 } 205 206 // WithCallerMetadata returns an Option that sets metadata of caller. 207 // It should not be used for env/set as specific methods are provided for env/set. 208 func WithCallerMetadata(key string, val string) Option { 209 return func(o *Options) { 210 o.SelectOptions = append(o.SelectOptions, selector.WithSourceMetadata(key, val)) 211 } 212 } 213 214 // WithCalleeMetadata returns an Option that sets metadata of callee. 215 // It should not be used for env/set as specific methods are provided for env/set. 216 func WithCalleeMetadata(key string, val string) Option { 217 return func(o *Options) { 218 o.SelectOptions = append(o.SelectOptions, selector.WithDestinationMetadata(key, val)) 219 } 220 } 221 222 // WithBalancerName returns an Option that sets load balancer by name. 223 func WithBalancerName(balancerName string) Option { 224 balancer := loadbalance.Get(balancerName) 225 return func(o *Options) { 226 o.SelectOptions = append(o.SelectOptions, 227 selector.WithLoadBalancer(balancer), 228 selector.WithLoadBalanceType(balancerName), 229 ) 230 } 231 } 232 233 // WithDiscoveryName returns an Option that sets service discovery by name. 234 func WithDiscoveryName(name string) Option { 235 d := discovery.Get(name) 236 return func(o *Options) { 237 o.SelectOptions = append(o.SelectOptions, selector.WithDiscovery(d)) 238 } 239 } 240 241 // WithServiceRouterName returns an Option that sets service router by name. 242 func WithServiceRouterName(name string) Option { 243 r := servicerouter.Get(name) 244 return func(o *Options) { 245 o.SelectOptions = append(o.SelectOptions, selector.WithServiceRouter(r)) 246 } 247 } 248 249 // WithCircuitBreakerName returns an Option that sets circuit breaker by name. 250 func WithCircuitBreakerName(name string) Option { 251 cb := circuitbreaker.Get(name) 252 return func(o *Options) { 253 o.SelectOptions = append(o.SelectOptions, selector.WithCircuitBreaker(cb)) 254 } 255 } 256 257 // WithKey returns an Option that sets the hash key of stateful routing. 258 func WithKey(key string) Option { 259 return func(o *Options) { 260 o.SelectOptions = append(o.SelectOptions, selector.WithKey(key)) 261 } 262 } 263 264 // WithReplicas returns an Option that sets node replicas of stateful routing. 265 func WithReplicas(r int) Option { 266 return func(o *Options) { 267 o.SelectOptions = append(o.SelectOptions, selector.WithReplicas(r)) 268 } 269 } 270 271 // WithTarget returns an Option that sets target address using URI scheme://endpoint. 272 // e.g. ip://ip_addr:port 273 func WithTarget(t string) Option { 274 return func(o *Options) { 275 o.Target = t 276 o.endpoint = "" // should parse endpoint again after calling WithTarget 277 } 278 } 279 280 // WithNetwork returns an Option that sets dial network: tcp/udp, tcp by default. 281 func WithNetwork(s string) Option { 282 return func(o *Options) { 283 if s == "" { 284 return 285 } 286 o.Network = s 287 o.CallOptions = append(o.CallOptions, transport.WithDialNetwork(s)) 288 } 289 } 290 291 // WithPassword returns an Option that sets dial password. 292 func WithPassword(s string) Option { 293 return func(o *Options) { 294 if s == "" { 295 return 296 } 297 o.CallOptions = append(o.CallOptions, transport.WithDialPassword(s)) 298 } 299 } 300 301 // WithPool returns an Option that sets dial pool. 302 func WithPool(pool connpool.Pool) Option { 303 return func(o *Options) { 304 o.CallOptions = append(o.CallOptions, transport.WithDialPool(pool)) 305 } 306 } 307 308 // WithMultiplexedPool returns an Option that sets multiplexed pool. 309 // Calling this method enables multiplexing. 310 func WithMultiplexedPool(p multiplexed.Pool) Option { 311 return func(o *Options) { 312 o.EnableMultiplexed = true 313 o.CallOptions = append(o.CallOptions, transport.WithMultiplexedPool(p)) 314 } 315 } 316 317 // WithTimeout returns an Option that sets timeout. 318 func WithTimeout(t time.Duration) Option { 319 return func(o *Options) { 320 o.Timeout = t 321 } 322 } 323 324 // WithCurrentSerializationType returns an Option that sets serialization type of caller itself. 325 // WithSerializationType should be used to set serialization type of backend service. 326 func WithCurrentSerializationType(t int) Option { 327 return func(o *Options) { 328 o.CurrentSerializationType = t 329 } 330 } 331 332 // WithSerializationType returns an Option that sets serialization type of backend service. 333 // Generally, only WithSerializationType will be used as WithCurrentSerializationType is used 334 // for reverse proxy. 335 func WithSerializationType(t int) Option { 336 return func(o *Options) { 337 o.SerializationType = t 338 } 339 } 340 341 // WithCurrentCompressType returns an Option that sets compression type of caller itself. 342 // WithCompressType should be used to set compression type of backend service. 343 func WithCurrentCompressType(t int) Option { 344 return func(o *Options) { 345 o.CurrentCompressType = t 346 } 347 } 348 349 // WithCompressType returns an Option that sets compression type of backend service. 350 // Generally, only WithCompressType will be used as WithCurrentCompressType is used 351 // for reverse proxy. 352 func WithCompressType(t int) Option { 353 return func(o *Options) { 354 o.CompressType = t 355 } 356 } 357 358 // WithTransport returns an Option that sets client transport plugin. 359 func WithTransport(t transport.ClientTransport) Option { 360 return func(o *Options) { 361 if t != nil { 362 o.Transport = t 363 } 364 } 365 } 366 367 // WithProtocol returns an Option that sets protocol of backend service like trpc. 368 func WithProtocol(s string) Option { 369 return func(o *Options) { 370 if s == "" { 371 return 372 } 373 o.Protocol = s 374 o.Codec = codec.GetClient(s) 375 if b := transport.GetFramerBuilder(s); b != nil { 376 o.CallOptions = append(o.CallOptions, 377 transport.WithClientFramerBuilder(b), 378 transport.WithProtocol(s), 379 ) 380 } 381 if t := transport.GetClientTransport(s); t != nil { 382 o.Transport = t 383 } 384 } 385 } 386 387 // WithConnectionMode returns an Option that sets whether connection mode is connected. 388 // If connection mode is connected, udp will isolate packets from non-same path. 389 func WithConnectionMode(connMode transport.ConnectionMode) Option { 390 return func(o *Options) { 391 o.CallOptions = append(o.CallOptions, transport.WithConnectionMode(connMode)) 392 } 393 } 394 395 // WithSendOnly returns an Option that sets CallType SendOnly. 396 // Generally it's used for udp async sending. 397 func WithSendOnly() Option { 398 return func(o *Options) { 399 o.CallType = codec.SendOnly 400 o.CallOptions = append(o.CallOptions, transport.WithReqType(codec.SendOnly)) 401 } 402 } 403 404 // WithFilter returns an Option that appends client filter to client filter chain. 405 // ClientFilter processing could be before encoding or after decoding. 406 // Selector filter is built-in filter and is at the end of the client filter chain by default. 407 // It is also supported to set pos of selector filter through the yaml config file. 408 func WithFilter(f filter.ClientFilter) Option { 409 return func(o *Options) { 410 o.Filters = append(o.Filters, f) 411 o.FilterNames = append(o.FilterNames, "client.WithFilter") 412 } 413 } 414 415 // WithNamedFilter returns an Option that adds named filter 416 func WithNamedFilter(name string, f filter.ClientFilter) Option { 417 return func(o *Options) { 418 o.FilterNames = append(o.FilterNames, name) 419 o.Filters = append(o.Filters, f) 420 } 421 } 422 423 // WithFilters returns an Option that appends multiple client filters to the client filter chain. 424 func WithFilters(fs []filter.ClientFilter) Option { 425 return func(o *Options) { 426 for _, f := range fs { 427 WithFilter(f)(o) 428 } 429 } 430 } 431 432 // WithStreamFilters returns an Option that appends multiple client stream filters to 433 // the client stream filter chain. 434 // StreamFilter processing could be before or after stream's establishing, before or after sending data, 435 // before or after receiving data. 436 func WithStreamFilters(sfs ...StreamFilter) Option { 437 return func(o *Options) { 438 o.StreamFilters = append(o.StreamFilters, sfs...) 439 } 440 } 441 442 // WithStreamFilter returns an Option that appends a client stream filter to 443 // the client stream filter chain. 444 func WithStreamFilter(sf StreamFilter) Option { 445 return func(o *Options) { 446 o.StreamFilters = append(o.StreamFilters, sf) 447 } 448 } 449 450 // WithReqHead returns an Option that sets req head. 451 // It's default to clone server req head from source request. 452 func WithReqHead(h interface{}) Option { 453 return func(o *Options) { 454 o.ReqHead = h 455 } 456 } 457 458 // WithRspHead returns an Option that sets rsp head. 459 // Usually used for gateway service. 460 func WithRspHead(h interface{}) Option { 461 return func(o *Options) { 462 o.RspHead = h 463 } 464 } 465 466 // WithAttachment returns an Option that sets attachment. 467 func WithAttachment(attachment *Attachment) Option { 468 return func(o *Options) { 469 o.attachment = &attachment.attachment 470 } 471 } 472 473 // WithMetaData returns an Option that sets transparent transmitted metadata. 474 func WithMetaData(key string, val []byte) Option { 475 return func(o *Options) { 476 if o.MetaData == nil { 477 o.MetaData = codec.MetaData{} 478 } 479 o.MetaData[key] = val 480 } 481 } 482 483 // WithSelectorNode returns an Option that records the selected node. 484 // It's usually used for debugging. 485 func WithSelectorNode(n *registry.Node) Option { 486 return func(o *Options) { 487 o.Node = &onceNode{Node: n} 488 } 489 } 490 491 // WithTLS returns an Option that sets client tls files. 492 // If caFile="none", no server cert validation. 493 // If caFile="root", local ca cert will be used to validate server. 494 // certFile is only used for mTLS or should be empty. 495 // serverName is used to validate the name of server. hostname by default for https. 496 func WithTLS(certFile, keyFile, caFile, serverName string) Option { 497 return func(o *Options) { 498 if caFile == "" { 499 return 500 } 501 o.CallOptions = append(o.CallOptions, transport.WithDialTLS(certFile, keyFile, caFile, serverName)) 502 } 503 } 504 505 // WithDisableConnectionPool returns an Option that disables connection pool. 506 func WithDisableConnectionPool() Option { 507 return func(o *Options) { 508 o.CallOptions = append(o.CallOptions, transport.WithDisableConnectionPool()) 509 } 510 } 511 512 // WithMultiplexed returns an Option that enables multiplexed. 513 // WithMultiplexedPool should be used for custom Multiplexed. 514 func WithMultiplexed(enable bool) Option { 515 return func(o *Options) { 516 o.EnableMultiplexed = enable 517 } 518 } 519 520 // WithLocalAddr returns an Option that sets local addr. Randomly picking for multiple NICs. 521 // 522 // for non-persistent conn, ip & port can be specified: 523 // client.WithLocalAddr("127.0.0.1:8080") 524 // for conn pool or multiplexed, only ip can be specified: 525 // client.WithLocalAddr("127.0.0.1:") 526 func WithLocalAddr(addr string) Option { 527 return func(o *Options) { 528 o.CallOptions = append(o.CallOptions, transport.WithLocalAddr(addr)) 529 } 530 } 531 532 // WithDialTimeout returns an Option that sets timeout. 533 func WithDialTimeout(dur time.Duration) Option { 534 return func(o *Options) { 535 o.CallOptions = append(o.CallOptions, transport.WithDialTimeout(dur)) 536 } 537 } 538 539 // WithStreamTransport returns an Option that sets client stream transport. 540 func WithStreamTransport(st transport.ClientStreamTransport) Option { 541 return func(o *Options) { 542 o.StreamTransport = st 543 } 544 } 545 546 // WithMaxWindowSize returns an Option that sets max size of receive window. 547 // Client as the receiver will notify the sender of the window. 548 func WithMaxWindowSize(s uint32) Option { 549 return func(o *Options) { 550 o.MaxWindowSize = s 551 } 552 } 553 554 // WithSendControl returns an Option that sets send control. 555 func WithSendControl(sc SendControl) Option { 556 return func(o *Options) { 557 o.SControl = sc 558 } 559 } 560 561 // WithRecvControl returns an Option that sets recv control. 562 func WithRecvControl(rc RecvControl) Option { 563 return func(o *Options) { 564 o.RControl = rc 565 } 566 } 567 568 // WithShouldErrReportToSelector returns an Option that sets should err report to selector 569 func WithShouldErrReportToSelector(f func(error) bool) Option { 570 return func(o *Options) { 571 o.shouldErrReportToSelector = f 572 } 573 } 574 575 type optionsKey struct{} 576 577 func contextWithOptions(ctx context.Context, opts *Options) context.Context { 578 return context.WithValue(ctx, optionsKey{}, opts) 579 } 580 581 // OptionsFromContext returns options from context. 582 func OptionsFromContext(ctx context.Context) *Options { 583 opts, ok := ctx.Value(optionsKey{}).(*Options) 584 if ok { 585 return opts 586 } 587 return NewOptions() 588 } 589 590 type optionsImmutability struct{} 591 592 // WithOptionsImmutable marks options of outermost layer immutable. 593 // Cloning options is needed for modifying options in lower layers. 594 // 595 // It should only be used by filters that call the next filter concurrently. 596 func WithOptionsImmutable(ctx context.Context) context.Context { 597 return context.WithValue(ctx, optionsImmutability{}, optionsImmutability{}) 598 } 599 600 // IsOptionsImmutable checks the ctx if options are immutable. 601 func IsOptionsImmutable(ctx context.Context) bool { 602 _, ok := ctx.Value(optionsImmutability{}).(optionsImmutability) 603 return ok 604 } 605 606 // ---------------------------- Options util api ---------------------// 607 608 // NewOptions creates a new Options with fields set to default value. 609 func NewOptions() *Options { 610 const ( 611 invalidSerializationType = -1 612 invalidCompressType = -1 613 ) 614 return &Options{ 615 Transport: transport.DefaultClientTransport, 616 Selector: selector.DefaultSelector, 617 SerializationType: invalidSerializationType, // the initial value is -1 618 // CurrentSerializationType is the serialization type of caller itself. 619 // SerializationType is the serialization type of backend service. 620 // For proxy, CurrentSerializationType should be noop but SerializationType should not. 621 CurrentSerializationType: invalidSerializationType, 622 CurrentCompressType: invalidCompressType, 623 624 fixTimeout: func(err error) error { return err }, 625 shouldErrReportToSelector: func(err error) bool { return false }, 626 } 627 } 628 629 // clone clones new options to ensure thread safety. 630 // Note that this is a shallow copy. 631 func (opts *Options) clone() *Options { 632 if opts == nil { 633 return NewOptions() 634 } 635 o := *opts 636 return &o 637 } 638 639 // rebuildSliceCapacity rebuilds slice capacity. 640 // Since new options will be cloned for each RPC, 641 // to prevent that appending slice may affect the original data of the slice, 642 // cap of slice should be set to equal len of slice so that a new slice will be 643 // created for each slice appending. 644 func (opts *Options) rebuildSliceCapacity() { 645 if len(opts.CallOptions) != cap(opts.CallOptions) { 646 o := make([]transport.RoundTripOption, len(opts.CallOptions), len(opts.CallOptions)) 647 copy(o, opts.CallOptions) 648 opts.CallOptions = o 649 } 650 if len(opts.SelectOptions) != cap(opts.SelectOptions) { 651 o := make([]selector.Option, len(opts.SelectOptions), len(opts.SelectOptions)) 652 copy(o, opts.SelectOptions) 653 opts.SelectOptions = o 654 } 655 if len(opts.Filters) != cap(opts.Filters) { 656 o := make(filter.ClientChain, len(opts.Filters), len(opts.Filters)) 657 copy(o, opts.Filters) 658 opts.Filters = o 659 } 660 if len(opts.FilterNames) != cap(opts.FilterNames) { 661 o := make([]string, len(opts.FilterNames), len(opts.FilterNames)) 662 copy(o, opts.FilterNames) 663 opts.FilterNames = o 664 } 665 } 666 667 func (opts *Options) parseTarget() error { 668 if opts.Target == "" { 669 return nil 670 } 671 672 // Target should be like: selector://endpoint. 673 substr := "://" 674 index := strings.Index(opts.Target, substr) 675 if index == -1 { 676 return fmt.Errorf("client: target %s scheme invalid, format must be selector://endpoint", opts.Target) 677 } 678 opts.Selector = selector.Get(opts.Target[:index]) 679 if opts.Selector == nil { 680 return fmt.Errorf("client: selector %s not exist", opts.Target[:index]) 681 } 682 opts.endpoint = opts.Target[index+len(substr):] 683 if opts.endpoint == "" { 684 return fmt.Errorf("client: target %s endpoint empty, format must be selector://endpoint", opts.Target) 685 } 686 687 return nil 688 } 689 690 // LoadNodeConfig loads node config from config center. 691 func (opts *Options) LoadNodeConfig(node *registry.Node) { 692 opts.CallOptions = append(opts.CallOptions, transport.WithDialAddress(node.Address)) 693 // Naming service has higher priority. 694 // Use network from local config file only if it's not set by the naming service. 695 if node.Network != "" { 696 opts.Network = node.Network 697 opts.CallOptions = append(opts.CallOptions, transport.WithDialNetwork(node.Network)) 698 } 699 if node.Protocol != "" { 700 WithProtocol(node.Protocol)(opts) 701 } 702 }