序
本文主要研究一下spring-cloud-kubernetes-client-discovery
DiscoveryClient
org/springframework/cloud/client/discovery/DiscoveryClient.java
代码语言:javascript复制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
代码语言:javascript复制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
代码语言:javascript复制 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
代码语言:javascript复制 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匹配过滤而来。