本文次要钻研一下spring-cloud-kubernetes-client-discovery

DiscoveryClient

org/springframework/cloud/client/discovery/DiscoveryClient.java

public interface DiscoveryClient extends Ordered {    /**     * Default order of the discovery client.     */    int DEFAULT_ORDER = 0;    /**     * A human-readable description of the implementation, used in HealthIndicator.     * @return The description.     */    String description();    /**     * Gets all ServiceInstances associated with a particular serviceId.     * @param serviceId The serviceId to query.     * @return A List of ServiceInstance.     */    List<ServiceInstance> getInstances(String serviceId);    /**     * @return All known service IDs.     */    List<String> getServices();    /**     * Can be used to verify the client is valid and able to make calls.     * <p>     * A successful invocation with no exception thrown implies the client is able to make     * calls.     * <p>     * The default implementation simply calls {@link #getServices()} - client     * implementations can override with a lighter weight operation if they choose to.     */    default void probe() {        getServices();    }    /**     * Default implementation for getting order of discovery clients.     * @return order     */    @Override    default int getOrder() {        return DEFAULT_ORDER;    }}
spring-cloud-commons提供了DiscoveryClient接口,它定义了description、getInstances、getServices、probe、getOrder办法

KubernetesInformerDiscoveryClient

spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesInformerDiscoveryClient.java

public class KubernetesInformerDiscoveryClient implements DiscoveryClient {    private static final LogAccessor LOG = new LogAccessor(LogFactory.getLog(KubernetesInformerDiscoveryClient.class));    private final List<SharedInformerFactory> sharedInformerFactories;    private final List<Lister<V1Service>> serviceListers;    private final List<Lister<V1Endpoints>> endpointsListers;    private final Supplier<Boolean> informersReadyFunc;    private final KubernetesDiscoveryProperties properties;    private final Predicate<V1Service> filter;    private final ServicePortSecureResolver servicePortSecureResolver;    // visible only for testing and    // must be constructor injected in a future release    @Autowired    CoreV1Api coreV1Api;    @Override    public String description() {        return "Kubernetes Client Discovery";    }    @Override    public List<String> getServices() {        List<String> services = serviceListers.stream().flatMap(serviceLister -> serviceLister.list().stream())                .filter(service -> matchesServiceLabels(service, properties)).filter(filter)                .map(s -> s.getMetadata().getName()).distinct().toList();        LOG.debug(() -> "will return services : " + services);        return services;    }    @PostConstruct    public void afterPropertiesSet() {        postConstruct(sharedInformerFactories, properties, informersReadyFunc, serviceListers);    }    @Override    public int getOrder() {        return properties.order();    }    //......}    
spring-cloud-kubernetes-client-discovery的KubernetesInformerDiscoveryClient实现了DiscoveryClient接口;其description办法返回的是Kubernetes Client Discovery,其getServices办法应用的是serviceListers的list办法获取service,加上service.metadata的label与KubernetesDiscoveryProperties的serviceLabels匹配过滤而来

getInstances

    public List<ServiceInstance> getInstances(String serviceId) {        Objects.requireNonNull(serviceId, "serviceId must be provided");        List<V1Service> allServices = serviceListers.stream().flatMap(x -> x.list().stream())                .filter(scv -> scv.getMetadata() != null).filter(svc -> serviceId.equals(svc.getMetadata().getName()))                .filter(scv -> matchesServiceLabels(scv, properties)).toList();        List<ServiceInstance> serviceInstances = allServices.stream().filter(filter)                .flatMap(service -> serviceInstances(service, serviceId).stream())                .collect(Collectors.toCollection(ArrayList::new));        if (properties.includeExternalNameServices()) {            LOG.debug(() -> "Searching for 'ExternalName' type of services with serviceId : " + serviceId);            List<V1Service> externalNameServices = allServices.stream().filter(s -> s.getSpec() != null)                    .filter(s -> EXTERNAL_NAME.equals(s.getSpec().getType())).toList();            for (V1Service service : externalNameServices) {                ServiceMetadata serviceMetadata = serviceMetadata(service);                Map<String, String> serviceInstanceMetadata = serviceInstanceMetadata(Map.of(), serviceMetadata,                        properties);                K8sInstanceIdHostPodNameSupplier supplierOne = externalName(service);                K8sPodLabelsAndAnnotationsSupplier supplierTwo = externalName();                ServiceInstance externalNameServiceInstance = serviceInstance(null, serviceMetadata, supplierOne,                        supplierTwo, new ServicePortNameAndNumber(-1, null), serviceInstanceMetadata, properties);                serviceInstances.add(externalNameServiceInstance);            }        }        return serviceInstances;    }
getInstances办法用serviceListers来对service.metadata.name与serviceId进行匹配获取到service,之后通过serviceInstances办法失去serviceInstances,最初加上ExternalName类型的service

serviceInstances

    private List<ServiceInstance> serviceInstances(V1Service service, String serviceId) {        List<ServiceInstance> instances = new ArrayList<>();        List<V1Endpoints> allEndpoints = endpointsListers.stream()                .map(endpointsLister -> endpointsLister.namespace(service.getMetadata().getNamespace()).get(serviceId))                .filter(Objects::nonNull).toList();        for (V1Endpoints endpoints : allEndpoints) {            List<V1EndpointSubset> subsets = endpoints.getSubsets();            if (subsets == null || subsets.isEmpty()) {                LOG.debug(() -> "serviceId : " + serviceId + " does not have any subsets");            }            else {                ServiceMetadata serviceMetadata = serviceMetadata(service);                Map<String, Integer> portsData = endpointSubsetsPortData(subsets);                Map<String, String> serviceInstanceMetadata = serviceInstanceMetadata(portsData, serviceMetadata,                        properties);                for (V1EndpointSubset endpointSubset : subsets) {                    Map<String, Integer> endpointsPortData = endpointSubsetsPortData(List.of(endpointSubset));                    ServicePortNameAndNumber portData = endpointsPort(endpointsPortData, serviceMetadata, properties);                    List<V1EndpointAddress> addresses = addresses(endpointSubset, properties);                    for (V1EndpointAddress endpointAddress : addresses) {                        K8sInstanceIdHostPodNameSupplier supplierOne = nonExternalName(endpointAddress, service);                        K8sPodLabelsAndAnnotationsSupplier supplierTwo = nonExternalName(coreV1Api,                                service.getMetadata().getNamespace());                        ServiceInstance serviceInstance = serviceInstance(servicePortSecureResolver, serviceMetadata,                                supplierOne, supplierTwo, portData, serviceInstanceMetadata, properties);                        instances.add(serviceInstance);                    }                }            }        }        return instances;    }
serviceInstances办法通过endpointsListers依据namespace和serviceId来获取对应的Endpoints,依据其subsets,通过K8sInstanceIdHostPodNameSupplier、K8sPodLabelsAndAnnotationsSupplier来组装DefaultKubernetesServiceInstance,最初返回

小结

spring-cloud-commons提供了DiscoveryClient接口,它定义了description、getInstances、getServices、probe、getOrder办法;spring-cloud-kubernetes-client-discovery的KubernetesInformerDiscoveryClient实现了DiscoveryClient接口;其description办法返回的是Kubernetes Client Discovery,其getServices办法应用的是serviceListers的list办法获取service,加上service.metadata的label与KubernetesDiscoveryProperties的serviceLabels匹配过滤而来。