序
本文次要钻研一下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匹配过滤而来。