-
Notifications
You must be signed in to change notification settings - Fork 3.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remove NameResolver.Factory #7133
Comments
Removing |
I'm trying to find how to replace the use of Is there any migration documentation/example/hint about how to replace the deprecate method with a proper solution ? |
There's no migration guide, since the process is just "make a normal name resolver." That would be one that uses a target string to look up addresses. You'd start with using the ServiceLoader mechanism described in the It looks like you are passing in per-channel information via the factory constructor. That should use the target uri. People abusing I don't see much point to passing the etcd hosts as URIs. Yes, that was necessary for etcd itself for compatibility, but it is strange for a gRPC name resolver. I'd probably just pass the host:post for each server, say, separated by commas. But it's not clear why there are pluggable URIResolverLoaders; is that for using things other than DNS to resolve names? I'd maybe use a target string akin to: If you really love your URIs, then you could do something more like |
That implementation is quite old so I don't remember all the reason why it is what it is :) I guess some of the original design goal were to hide internals as much as possible so that we have the |
@ejona86 I got something working but it is quite ugly as etcd itself as well as any example or other client including the go one use a list of URIs. Wonder having grpc-java supporting something like |
@lburgazzoli, the URL list is probably not a good enough reason, since we know everything except the hostname will be thrown away. In my mind you are free to convert the URLs to a target string with a utility and "clean them up" in the process by stripping the other stuff. |
I'm certainly not advocating to support URLs by default but in mind mind something like: forTargets("http://host:port", "dns+srv:///_etcd._tcp.acme.com", "dns:///etcd.acme.com:8080"); Then under the hoods, gtpc-java could use the same mechanics as today but applied to multiple target ad finally aggregate the result (this is in fact what my custom name resolver was doing). In my case then I'd only have to provide a However I've implemented what you suggested by having some logic that deals with encoding/deconding from a a list of URLs to a single target entry so what I'm suggesting is not required by since there's a concept of load balancing in grpc-java, I think having an easy way to define multiple targets would be nice. |
As a general purpose mechanism it initially seems generic and useful. But it ends up hard-coding a critical part of service discovery as part of the client, which we would consider a Bad Idea. Basically, primary/secondary nodes are hard-coded. For communicating with etcd itself, that's not as strange because you have to bootstrap yourself somehow. But for communicating with an actual backend that would be very weak. I'm noticing now that For communicating with a low-level service like etcd, I'd normally expect either the (not-supported-in-grpc-java) Now, I completely understand you're doing things "the etcd way" and that made sense to etcd given how it evolved. It's just that the difficulties experienced don't seem inherent in any way, so I'd be prone to let this etcd-resolver-that-is-rarely-seen be a bit uglier. I'll also note that it would be possible to gather the etcd configuration for a set of endpoints and give it a name and use that name for your |
@ejona86 thx for your advice |
We have a custom NameResolverProvider that can be configured with a custom domain. To give an example: doing Instantiate and register our Is there a way for us to not have to depend on the global registry? |
@tommyulfsparre, can you explain why you don't just use |
@ejona86 we actual support both but using the fqdn uses a different scheme. The reason (iirc) that we wanted to match our old pre-gRPC discovery names which didn't encode any locality. Our application framework will detect the region (or if you are running locally) its running in and configures the |
@tommyulfsparre, is the application or framework calling |
@ejona86 the framework is calling The framework injects a So a common application usage is: final ManagedChannel channel = GrpcChannelFactory.forTarget("my-resolver://foobar").build() |
Did this means in current version (1.37.0), different address with their different authority is not supported? @ejona86 |
A NameResolver is able to vary the authority per IP address group via |
As brought up in #8543, when we remove |
We are using Zookeeper as service registry for our services, and with authentication, and cluster configuration the Global registration of the NameResolver.Factory makes it very awkward. Is there a best practice way to do that without a local NameResolver factory ? |
Not sure I completely understand the concern - since you say "Global registration ... makes it very awkward" and also "Is there a best practice way to do that without a local NameResolver factory". So the use of global and local is confusing. What would be an ideal solution for you? |
"authentication" shouldn't be a problem. Worst-case use |
Well, the Zookeeper connection is very much a local resource for a subset of gRPC services I use, and the NameResolverRegistry by its current design a global resource. I was wondering if there was a solution where I would not have to publish it in a VM scoped Registry. To make things even more awkward I have two Zookeeper ensembles I need to connect to for different services. It felt unnecessarily convoluted as it would be pretty easy with the deprecated nameResolverFactory method. But if that is how the target architecture looks like, I will find a suitable workaround. Although I must admit I do not understand, nor agree with that design decision. |
This would normally be handled by using the authority section of a target (e.g.,
There's two separate things going on. We don't want "I'm using two different zookeeper servers" to be a reason to avoid using the registry. The registry should handle that case. We can maybe do something to make that easier, but it is something supported for similar situations (e.g., DNS, xDS). "a local resource for a subset of gRPC services" on the other hand is something that may not fit nicely into the registry model and I'd like to understand that better. Is this like "Zookeeper FOO is used for most services, but services from an acquisition/another company/etc use Zookeeper BAR"? Or is it more, "Service A has a special sharding/whatever and has its own Zookeeper to manage itself"?
Yes and no. It was very easy to workaround the name resolver system by not using the target string at all. It seemed there were many NRs that couldn't be used in the global registry. The override wasn't really meant for that purpose; it was more for the purpose, "let me change the default from DNS to my own," which it does a poor job at but was quick and easy and gave us time to work on more pressing matters while we also gain more experience with the system. There's multiple reasons for the changes. Much of it is to align the various languages. The entire purpose of using the target URI is we wanted something powerful enough you could easily pass a string on the command line to a program and it contact the appropriate service. If NR is hard-coded, that stops working and brings other challenges. But we also want to support inprocess, unix, and binder name resolvers. This would allow using other transport types with stable API. But that requires a negotiation and choosing a transport based on the target string and name resolver before constructing the builder (see #9076). We're working around things for now, but essentially there's more situations that the |
Looking through OSS references: https://sourcegraph.com/search?q=context:global+.nameResolverFactory++-file:io/grpc/&patternType=standard&case=yes&sm=0&groupBy=repo There's a reasonable number in repos with more than 1000 stars. A surprising number are setting DnsNameResolverProvider, which 1) is generally the default, and 2) is in the internal package, so apparently those users favor YOLO instead of filing an issue. I suspect that's a workaround for a ServiceLoader issue, or some serious cargo culting. |
The loss of ManagedChannelBuilder#nameResolverFactory would be problematic for PackageManager-based service discovery on Android. Such an NRP needs an instance of PackageManager to do its job, but this object isn't globally accessible -- it needs to be passed to a NRP as a constructor argument, which kind of precludes SPI. That leaves the NameResolverRegistry method, but the ManagedChannelBuilder#nameResolverRegistry method isn't public. The global default registry isn't great because our code is an SDK/library meant to be installed in 3p apps. We don't really want to change how names are resolved for other gRPC-using code in the hosting process. Imagine a pathological case where two libraries each have their own different idea of how to resolve target URIs that start with Can the ManagedChannelBuilder#nameResolverRegistry method be made public to replace nameResolverFactory()? |
SPI isn't a requirement.
That's the more important situation.
Well, sure. But if you are going to need some custom resolver that is just for your library, then use a more unique scheme:
We've taken care that this not the case. We don't use registration order at all. If two resolvers for the same scheme have the same priority, we order based on class name. |
Thanks for the answer! I'd actually like to standardize the URI scheme then write and publish an official NameResolver for it for many android apps and libraries to use. Within a single app/process, there could be multiple independent bodies of code (e.g. sdks) using it. Each body of code needs to ensure for itself that the NRP is installed on its channels, providing its instance of the Application context (a single android:process can host multiple android.app.Applications). At the root I guess my complaint is that non-public visibility of ManagedChannelBuilder#nameResolverRegistry forces me to use a global variable, and take on all the complications that follow from that. |
For libraries wanting to specify resolvers, #11055 (comment) would provide a path forward while removing the biggest issues with changing the name resolver in the middle of building. |
Thanks for addressing this! I'm curious though: what's wrong with specifying the registry with a ManagedChannelBuilder method? I'm surprised to hear that you consider this "changing the name resolver in the middle of building" because I always considered Builder methods as simple setters with no actual work happening until you call build(). Moving the registry to a positional param on a static factory method would give up the important advantages of the builder pattern: 1) Self-documenting call sites 2) Only N methods needed to handle N optional parameters, compared to the factory method's 2^N overloads. |
@jdcormie, observability is needing to configure the channel based on the target string. But we don't actually know the target string until we build the channel. Chicken and egg. This problem is limited to the value of the default scheme. Even before that we had the problem that the name resolver is used to figure out what transport to use. |
NameResolver.Factory
is old and was meant to be replaced byNameResolverProvider
. I've been slowly trying to remove it, especiallyManagedChannelBuilder.nameResolverFactory()
. I see evidence new users are using it, so this issue is to be a gathering point for why it is being removed and making sure there are alternatives to the current usages.Since NameResolverRegistry was added in v1.21, most users shouldn't need the factory. A small number of users may need a "default name resolver override". This would be similar to
defaultLoadBalancingPolicy()
which was added when we removedloadBalancerFactory()
. However, the NameResolver API needs tweaks to support that, where each NameResolver would be for a particular scheme and the NameResolverRegistry would select the appropriate NameResolver (instead of the current "call all the name resolvers in order until one understands the URI").NameResolver.Factory is used internally in grpc. I don't care much about that, since it does little harm, but I don't expect NameResolver.Factory to ever become stable and it should be removed. It just may go slowly due to priorities.
The text was updated successfully, but these errors were encountered: