Skip to content

Commit 7c37770

Browse files
authored
grpc: clean up doc strings and some code around Dial vs NewClient (#7029)
1 parent c808322 commit 7c37770

File tree

3 files changed

+130
-77
lines changed

3 files changed

+130
-77
lines changed

clientconn.go

+43-46
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,6 @@ const (
101101
defaultReadBufSize = 32 * 1024
102102
)
103103

104-
// Dial creates a client connection to the given target.
105-
func Dial(target string, opts ...DialOption) (*ClientConn, error) {
106-
return DialContext(context.Background(), target, opts...)
107-
}
108-
109104
type defaultConfigSelector struct {
110105
sc *ServiceConfig
111106
}
@@ -117,11 +112,22 @@ func (dcs *defaultConfigSelector) SelectConfig(rpcInfo iresolver.RPCInfo) (*ires
117112
}, nil
118113
}
119114

120-
func newClient(target, defaultScheme string, opts ...DialOption) (conn *ClientConn, err error) {
115+
// NewClient creates a new gRPC "channel" for the target URI provided. No I/O
116+
// is performed. Use of the ClientConn for RPCs will automatically cause it to
117+
// connect. Connect may be used to manually create a connection, but for most
118+
// users this is unnecessary.
119+
//
120+
// The target name syntax is defined in
121+
// https://github.com/grpc/grpc/blob/master/doc/naming.md. e.g. to use dns
122+
// resolver, a "dns:///" prefix should be applied to the target.
123+
//
124+
// The DialOptions returned by WithBlock, WithTimeout, and
125+
// WithReturnConnectionError are ignored by this function.
126+
func NewClient(target string, opts ...DialOption) (conn *ClientConn, err error) {
121127
cc := &ClientConn{
122128
target: target,
123129
conns: make(map[*addrConn]struct{}),
124-
dopts: defaultDialOptions(defaultScheme),
130+
dopts: defaultDialOptions(),
125131
czData: new(channelzData),
126132
}
127133

@@ -190,45 +196,36 @@ func newClient(target, defaultScheme string, opts ...DialOption) (conn *ClientCo
190196
return cc, nil
191197
}
192198

193-
// NewClient returns a new client in idle mode.
194-
func NewClient(target string, opts ...DialOption) (conn *ClientConn, err error) {
195-
return newClient(target, "dns", opts...)
199+
// Dial calls DialContext(context.Background(), target, opts...).
200+
//
201+
// Deprecated: use NewClient instead. Will be supported throughout 1.x.
202+
func Dial(target string, opts ...DialOption) (*ClientConn, error) {
203+
return DialContext(context.Background(), target, opts...)
196204
}
197205

198-
// DialContext creates a client connection to the given target. By default, it's
199-
// a non-blocking dial (the function won't wait for connections to be
200-
// established, and connecting happens in the background). To make it a blocking
201-
// dial, use WithBlock() dial option.
206+
// DialContext calls NewClient and then exits idle mode. If WithBlock(true) is
207+
// used, it calls Connect and WaitForStateChange until either the context
208+
// expires or the state of the ClientConn is Ready.
202209
//
203-
// In the non-blocking case, the ctx does not act against the connection. It
204-
// only controls the setup steps.
210+
// One subtle difference between NewClient and Dial and DialContext is that the
211+
// former uses "dns" as the default name resolver, while the latter use
212+
// "passthrough" for backward compatibility. This distinction should not matter
213+
// to most users, but could matter to legacy users that specify a custom dialer
214+
// and expect it to receive the target string directly.
205215
//
206-
// In the blocking case, ctx can be used to cancel or expire the pending
207-
// connection. Once this function returns, the cancellation and expiration of
208-
// ctx will be noop. Users should call ClientConn.Close to terminate all the
209-
// pending operations after this function returns.
210-
//
211-
// The target name syntax is defined in
212-
// https://github.com/grpc/grpc/blob/master/doc/naming.md.
213-
// e.g. to use dns resolver, a "dns:///" prefix should be applied to the target.
216+
// Deprecated: use NewClient instead. Will be supported throughout 1.x.
214217
func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *ClientConn, err error) {
215-
// At the end of this method, we kick the channel out of idle, rather than waiting for the first rpc.
216-
cc, err := newClient(target, "passthrough", opts...)
218+
// At the end of this method, we kick the channel out of idle, rather than
219+
// waiting for the first rpc.
220+
opts = append([]DialOption{withDefaultScheme("passthrough")}, opts...)
221+
cc, err := NewClient(target, opts...)
217222
if err != nil {
218223
return nil, err
219224
}
220225

221226
// We start the channel off in idle mode, but kick it out of idle now,
222-
// instead of waiting for the first RPC. Other gRPC implementations do wait
223-
// for the first RPC to kick the channel out of idle. But doing so would be
224-
// a major behavior change for our users who are used to seeing the channel
225-
// active after Dial.
226-
//
227-
// Taking this approach of kicking it out of idle at the end of this method
228-
// allows us to share the code between channel creation and exiting idle
229-
// mode. This will also make it easy for us to switch to starting the
230-
// channel off in idle, i.e. by making newClient exported.
231-
227+
// instead of waiting for the first RPC. This is the legacy behavior of
228+
// Dial.
232229
defer func() {
233230
if err != nil {
234231
cc.Close()
@@ -712,15 +709,15 @@ func init() {
712709
}
713710
}
714711

715-
func (cc *ClientConn) maybeApplyDefaultServiceConfig(addrs []resolver.Address) {
712+
func (cc *ClientConn) maybeApplyDefaultServiceConfig() {
716713
if cc.sc != nil {
717-
cc.applyServiceConfigAndBalancer(cc.sc, nil, addrs)
714+
cc.applyServiceConfigAndBalancer(cc.sc, nil)
718715
return
719716
}
720717
if cc.dopts.defaultServiceConfig != nil {
721-
cc.applyServiceConfigAndBalancer(cc.dopts.defaultServiceConfig, &defaultConfigSelector{cc.dopts.defaultServiceConfig}, addrs)
718+
cc.applyServiceConfigAndBalancer(cc.dopts.defaultServiceConfig, &defaultConfigSelector{cc.dopts.defaultServiceConfig})
722719
} else {
723-
cc.applyServiceConfigAndBalancer(emptyServiceConfig, &defaultConfigSelector{emptyServiceConfig}, addrs)
720+
cc.applyServiceConfigAndBalancer(emptyServiceConfig, &defaultConfigSelector{emptyServiceConfig})
724721
}
725722
}
726723

@@ -738,7 +735,7 @@ func (cc *ClientConn) updateResolverStateAndUnlock(s resolver.State, err error)
738735
// May need to apply the initial service config in case the resolver
739736
// doesn't support service configs, or doesn't provide a service config
740737
// with the new addresses.
741-
cc.maybeApplyDefaultServiceConfig(nil)
738+
cc.maybeApplyDefaultServiceConfig()
742739

743740
cc.balancerWrapper.resolverError(err)
744741

@@ -750,9 +747,9 @@ func (cc *ClientConn) updateResolverStateAndUnlock(s resolver.State, err error)
750747
var ret error
751748
if cc.dopts.disableServiceConfig {
752749
channelz.Infof(logger, cc.channelzID, "ignoring service config from resolver (%v) and applying the default because service config is disabled", s.ServiceConfig)
753-
cc.maybeApplyDefaultServiceConfig(s.Addresses)
750+
cc.maybeApplyDefaultServiceConfig()
754751
} else if s.ServiceConfig == nil {
755-
cc.maybeApplyDefaultServiceConfig(s.Addresses)
752+
cc.maybeApplyDefaultServiceConfig()
756753
// TODO: do we need to apply a failing LB policy if there is no
757754
// default, per the error handling design?
758755
} else {
@@ -765,7 +762,7 @@ func (cc *ClientConn) updateResolverStateAndUnlock(s resolver.State, err error)
765762
} else {
766763
configSelector = &defaultConfigSelector{sc}
767764
}
768-
cc.applyServiceConfigAndBalancer(sc, configSelector, s.Addresses)
765+
cc.applyServiceConfigAndBalancer(sc, configSelector)
769766
} else {
770767
ret = balancer.ErrBadResolverState
771768
if cc.sc == nil {
@@ -1072,7 +1069,7 @@ func (cc *ClientConn) getTransport(ctx context.Context, failfast bool, method st
10721069
})
10731070
}
10741071

1075-
func (cc *ClientConn) applyServiceConfigAndBalancer(sc *ServiceConfig, configSelector iresolver.ConfigSelector, addrs []resolver.Address) {
1072+
func (cc *ClientConn) applyServiceConfigAndBalancer(sc *ServiceConfig, configSelector iresolver.ConfigSelector) {
10761073
if sc == nil {
10771074
// should never reach here.
10781075
return
@@ -1747,7 +1744,7 @@ func (cc *ClientConn) parseTargetAndFindResolver() error {
17471744
// scheme, except when a custom dialer is specified in which case, we should
17481745
// always use passthrough scheme. For either case, we need to respect any overridden
17491746
// global defaults set by the user.
1750-
defScheme := cc.dopts.defScheme
1747+
defScheme := cc.dopts.defaultScheme
17511748
if internal.UserSetDefaultScheme {
17521749
defScheme = resolver.GetDefaultScheme()
17531750
}

clientconn_parsed_target_test.go

+76-28
Original file line numberDiff line numberDiff line change
@@ -33,91 +33,116 @@ import (
3333
"google.golang.org/grpc/resolver"
3434
)
3535

36-
func generateTarget(scheme string, target string) resolver.Target {
37-
return resolver.Target{URL: *testutils.MustParseURL(fmt.Sprintf("%s:///%s", scheme, target))}
36+
func generateTarget(target string) resolver.Target {
37+
return resolver.Target{URL: *testutils.MustParseURL(target)}
3838
}
3939

40-
// This is here just in case another test calls the SetDefaultScheme method.
40+
// Resets the default scheme as though it was never set by the user.
4141
func resetInitialResolverState() {
4242
resolver.SetDefaultScheme("passthrough")
4343
internal.UserSetDefaultScheme = false
4444
}
4545

46+
type testResolverForParser struct {
47+
resolver.Resolver
48+
}
49+
50+
func (testResolverForParser) Build(resolver.Target, resolver.ClientConn, resolver.BuildOptions) (resolver.Resolver, error) {
51+
return testResolverForParser{}, nil
52+
}
53+
54+
func (testResolverForParser) Close() {}
55+
56+
func (testResolverForParser) Scheme() string {
57+
return "testresolverforparser"
58+
}
59+
60+
func init() { resolver.Register(testResolverForParser{}) }
61+
4662
func (s) TestParsedTarget_Success_WithoutCustomDialer(t *testing.T) {
47-
resetInitialResolverState()
48-
dialScheme := resolver.GetDefaultScheme()
49-
newClientScheme := "dns"
5063
tests := []struct {
5164
target string
5265
wantDialParse resolver.Target
5366
wantNewClientParse resolver.Target
67+
wantCustomParse resolver.Target
5468
}{
5569
// No scheme is specified.
5670
{
5771
target: "://a/b",
58-
wantDialParse: generateTarget(dialScheme, "://a/b"),
59-
wantNewClientParse: generateTarget(newClientScheme, "://a/b"),
72+
wantDialParse: generateTarget("passthrough:///://a/b"),
73+
wantNewClientParse: generateTarget("dns:///://a/b"),
74+
wantCustomParse: generateTarget("testresolverforparser:///://a/b"),
6075
},
6176
{
6277
target: "a//b",
63-
wantDialParse: generateTarget(dialScheme, "a//b"),
64-
wantNewClientParse: generateTarget(newClientScheme, "a//b"),
78+
wantDialParse: generateTarget("passthrough:///a//b"),
79+
wantNewClientParse: generateTarget("dns:///a//b"),
80+
wantCustomParse: generateTarget("testresolverforparser:///a//b"),
6581
},
6682

6783
// An unregistered scheme is specified.
6884
{
6985
target: "a:///",
70-
wantDialParse: generateTarget(dialScheme, "a:///"),
71-
wantNewClientParse: generateTarget(newClientScheme, "a:///"),
86+
wantDialParse: generateTarget("passthrough:///a:///"),
87+
wantNewClientParse: generateTarget("dns:///a:///"),
88+
wantCustomParse: generateTarget("testresolverforparser:///a:///"),
7289
},
7390
{
7491
target: "a:b",
75-
wantDialParse: generateTarget(dialScheme, "a:b"),
76-
wantNewClientParse: generateTarget(newClientScheme, "a:b"),
92+
wantDialParse: generateTarget("passthrough:///a:b"),
93+
wantNewClientParse: generateTarget("dns:///a:b"),
94+
wantCustomParse: generateTarget("testresolverforparser:///a:b"),
7795
},
7896

7997
// A registered scheme is specified.
8098
{
8199
target: "dns://a.server.com/google.com",
82-
wantDialParse: resolver.Target{URL: *testutils.MustParseURL("dns://a.server.com/google.com")},
83-
wantNewClientParse: resolver.Target{URL: *testutils.MustParseURL("dns://a.server.com/google.com")},
100+
wantDialParse: generateTarget("dns://a.server.com/google.com"),
101+
wantNewClientParse: generateTarget("dns://a.server.com/google.com"),
102+
wantCustomParse: generateTarget("dns://a.server.com/google.com"),
84103
},
85104
{
86105
target: "unix-abstract:/ a///://::!@#$%25^&*()b",
87-
wantDialParse: resolver.Target{URL: *testutils.MustParseURL("unix-abstract:/ a///://::!@#$%25^&*()b")},
88-
wantNewClientParse: resolver.Target{URL: *testutils.MustParseURL("unix-abstract:/ a///://::!@#$%25^&*()b")},
106+
wantDialParse: generateTarget("unix-abstract:/ a///://::!@#$%25^&*()b"),
107+
wantNewClientParse: generateTarget("unix-abstract:/ a///://::!@#$%25^&*()b"),
108+
wantCustomParse: generateTarget("unix-abstract:/ a///://::!@#$%25^&*()b"),
89109
},
90110
{
91111
target: "unix-abstract:passthrough:abc",
92-
wantDialParse: resolver.Target{URL: *testutils.MustParseURL("unix-abstract:passthrough:abc")},
93-
wantNewClientParse: resolver.Target{URL: *testutils.MustParseURL("unix-abstract:passthrough:abc")},
112+
wantDialParse: generateTarget("unix-abstract:passthrough:abc"),
113+
wantNewClientParse: generateTarget("unix-abstract:passthrough:abc"),
114+
wantCustomParse: generateTarget("unix-abstract:passthrough:abc"),
94115
},
95116
{
96117
target: "passthrough:///unix:///a/b/c",
97-
wantDialParse: resolver.Target{URL: *testutils.MustParseURL("passthrough:///unix:///a/b/c")},
98-
wantNewClientParse: resolver.Target{URL: *testutils.MustParseURL("passthrough:///unix:///a/b/c")},
118+
wantDialParse: generateTarget("passthrough:///unix:///a/b/c"),
119+
wantNewClientParse: generateTarget("passthrough:///unix:///a/b/c"),
120+
wantCustomParse: generateTarget("passthrough:///unix:///a/b/c"),
99121
},
100122

101123
// Cases for `scheme:absolute-path`.
102124
{
103125
target: "dns:/a/b/c",
104-
wantDialParse: resolver.Target{URL: *testutils.MustParseURL("dns:/a/b/c")},
105-
wantNewClientParse: resolver.Target{URL: *testutils.MustParseURL("dns:/a/b/c")},
126+
wantDialParse: generateTarget("dns:/a/b/c"),
127+
wantNewClientParse: generateTarget("dns:/a/b/c"),
128+
wantCustomParse: generateTarget("dns:/a/b/c"),
106129
},
107130
{
108131
target: "unregistered:/a/b/c",
109-
wantDialParse: generateTarget(dialScheme, "unregistered:/a/b/c"),
110-
wantNewClientParse: generateTarget(newClientScheme, "unregistered:/a/b/c"),
132+
wantDialParse: generateTarget("passthrough:///unregistered:/a/b/c"),
133+
wantNewClientParse: generateTarget("dns:///unregistered:/a/b/c"),
134+
wantCustomParse: generateTarget("testresolverforparser:///unregistered:/a/b/c"),
111135
},
112136
}
113137

114138
for _, test := range tests {
115139
t.Run(test.target, func(t *testing.T) {
140+
resetInitialResolverState()
116141
cc, err := Dial(test.target, WithTransportCredentials(insecure.NewCredentials()))
117142
if err != nil {
118143
t.Fatalf("Dial(%q) failed: %v", test.target, err)
119144
}
120-
defer cc.Close()
145+
cc.Close()
121146

122147
if !cmp.Equal(cc.parsedTarget, test.wantDialParse) {
123148
t.Errorf("cc.parsedTarget for dial target %q = %+v, want %+v", test.target, cc.parsedTarget, test.wantDialParse)
@@ -127,13 +152,36 @@ func (s) TestParsedTarget_Success_WithoutCustomDialer(t *testing.T) {
127152
if err != nil {
128153
t.Fatalf("NewClient(%q) failed: %v", test.target, err)
129154
}
130-
defer cc.Close()
155+
cc.Close()
131156

132157
if !cmp.Equal(cc.parsedTarget, test.wantNewClientParse) {
133158
t.Errorf("cc.parsedTarget for newClient target %q = %+v, want %+v", test.target, cc.parsedTarget, test.wantNewClientParse)
134159
}
160+
161+
resolver.SetDefaultScheme("testresolverforparser")
162+
cc, err = Dial(test.target, WithTransportCredentials(insecure.NewCredentials()))
163+
if err != nil {
164+
t.Fatalf("Dial(%q) failed: %v", test.target, err)
165+
}
166+
cc.Close()
167+
168+
if !cmp.Equal(cc.parsedTarget, test.wantCustomParse) {
169+
t.Errorf("cc.parsedTarget for dial target %q = %+v, want %+v", test.target, cc.parsedTarget, test.wantDialParse)
170+
}
171+
172+
cc, err = NewClient(test.target, WithTransportCredentials(insecure.NewCredentials()))
173+
if err != nil {
174+
t.Fatalf("NewClient(%q) failed: %v", test.target, err)
175+
}
176+
cc.Close()
177+
178+
if !cmp.Equal(cc.parsedTarget, test.wantCustomParse) {
179+
t.Errorf("cc.parsedTarget for newClient target %q = %+v, want %+v", test.target, cc.parsedTarget, test.wantNewClientParse)
180+
}
181+
135182
})
136183
}
184+
resetInitialResolverState()
137185
}
138186

139187
func (s) TestParsedTarget_Failure_WithoutCustomDialer(t *testing.T) {

dialoptions.go

+11-3
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ type dialOptions struct {
7979
resolvers []resolver.Builder
8080
idleTimeout time.Duration
8181
recvBufferPool SharedBufferPool
82-
defScheme string
82+
defaultScheme string
8383
}
8484

8585
// DialOption configures how we set up the connection.
@@ -632,7 +632,7 @@ func withHealthCheckFunc(f internal.HealthChecker) DialOption {
632632
})
633633
}
634634

635-
func defaultDialOptions(defScheme string) dialOptions {
635+
func defaultDialOptions() dialOptions {
636636
return dialOptions{
637637
copts: transport.ConnectOptions{
638638
ReadBufferSize: defaultReadBufSize,
@@ -644,7 +644,7 @@ func defaultDialOptions(defScheme string) dialOptions {
644644
healthCheckFunc: internal.HealthCheckFunc,
645645
idleTimeout: 30 * time.Minute,
646646
recvBufferPool: nopBufferPool{},
647-
defScheme: defScheme,
647+
defaultScheme: "dns",
648648
}
649649
}
650650

@@ -659,6 +659,14 @@ func withMinConnectDeadline(f func() time.Duration) DialOption {
659659
})
660660
}
661661

662+
// withDefaultScheme is used to allow Dial to use "passthrough" as the default
663+
// name resolver, while NewClient uses "dns" otherwise.
664+
func withDefaultScheme(s string) DialOption {
665+
return newFuncDialOption(func(o *dialOptions) {
666+
o.defaultScheme = s
667+
})
668+
}
669+
662670
// WithResolvers allows a list of resolver implementations to be registered
663671
// locally with the ClientConn without needing to be globally registered via
664672
// resolver.Register. They will be matched against the scheme used for the

0 commit comments

Comments
 (0)