spring-cloud-kubernetes背后的三个关键知识点

首先为自己打个广告,我目前在某互联网公司做架构师,已经有5年经验,每天都会写架构师系列的文章,感兴趣的朋友可以关注我和我一起探讨,关注我,免费分享Java基础教程,以及进阶的高级Java架构师教程,全部免费送

概览

总结下来有三个关键知识点需要深入理解:

1、DiscoveryClient是个接口,对应的实现类是哪个?

2、
discoveryClient.getServices()方法取得了kubernetes的service信息,这背后的机制是什么?java应用是怎样取得所在kubernetes的服务信息的?

3、kubernetes的service信息存在哪里?如何将这些信息给出去?

接下来我们逐一分析每个知识点;

DiscoveryClient接口的实现类实例从何而来

先来回顾一下上一章的DiscoveryController.java的内容:

@RestController
public class DiscoveryController {
 @Autowired
 private DiscoveryClient discoveryClient;
 /**
 * 探针检查响应类
 * @return
 */
 @RequestMapping("/health")
 public String health() {
 return "health";
 }
 /**
 * 返回远程调用的结果
 * @return
 */
 @RequestMapping("/getservicedetail")
 public String getUri(
 @RequestParam(value = "servicename", defaultValue = "") String servicename) {
 return "Service [" + servicename + "]'s instance list : " + JSON.toJSONString(discoveryClient.getInstances(servicename));
 }
 /**
 * 返回发现的所有服务
 * @return
 */
 @RequestMapping("/services")
 public String services() {
 return this.discoveryClient.getServices().toString()
 + ", "
 + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
 }
}

上述代码中,我们并没有写创建DiscoveryClient实例的代码,discoveryClient从何而来?

这一切,要从DiscoveryController.java所在项目的pom.xml说起;

1、在pom.xml中,有对spring-cloud-kubernetes框架的依赖配置:


 org.springframework.cloud
 spring-cloud-kubernetes-discovery
 1.0.1.RELEASE
 

2、打开
spring-cloud-kubernetes-discovery的源码,地址是:https://github.com/spring-cloud/spring-cloud-kubernetes/tree/master/spring-cloud-kubernetes-discovery ,在这个工程中发现了文件spring.factories:spring-cloud-kubernetes背后的三个关键知识点_第1张图片

 

3、spring容器启动时,会寻找classpath下所有spring.factories文件(包括jar文件中的),spring.factories中配置的所有类都会实例化,我们在开发springboot时常用到的XXX-starter.jar就用到了这个技术,效果是一旦依赖了某个starter.jar很多功能就在spring初始化时候自动执行了(例如mysql的starter,启动时会连接数据库),关于此技术的详情,请参考以下三篇文章: 《自定义spring boot starter三部曲之一:准备工作》 《自定义spring boot starter三部曲之二:实战开发》 《自定义spring boot starter三部曲之三:源码分析spring.factories加载过程》

4、spring.factories文件中有两个类:
KubernetesDiscoveryClientAutoConfiguration和KubernetesDiscoveryClientConfigClientBootstrapConfiguration都会被实例化;

5、先看
KubernetesDiscoveryClientConfigClientBootstrapConfiguration,很简单的源码,KubernetesAutoConfiguration和KubernetesDiscoveryClientAutoConfiguration这两个类会被实例化:

/**
 * Bootstrap config for Kubernetes discovery config client.
 *
 * @author Zhanwei Wang
 */
@Configuration
@ConditionalOnProperty("spring.cloud.config.discovery.enabled")
@Import({ KubernetesAutoConfiguration.class,
 KubernetesDiscoveryClientAutoConfiguration.class })
public class KubernetesDiscoveryClientConfigClientBootstrapConfiguration {
} 

6、在
KubernetesAutoConfiguration的源码中,会实例化一个重要的类:DefaultKubernetesClient,如下:

@Bean
@ConditionalOnMissingBean
public KubernetesClient kubernetesClient(Config config) {
 return new DefaultKubernetesClient(config);
}

7、再看
KubernetesDiscoveryClientAutoConfiguration源码,注意kubernetesDiscoveryClient方法,这里面实例化了DiscoveryController所需的DiscoveryClient接口实现,还要重点关注的地方是KubernetesClient参数的值,是上面提到的DefaultKubernetesClient对象:

@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "spring.cloud.kubernetes.discovery.enabled", matchIfMissing = true)
public KubernetesDiscoveryClient kubernetesDiscoveryClient(KubernetesClient client,
 KubernetesDiscoveryProperties properties,
 KubernetesClientServicesFunction kubernetesClientServicesFunction,
 DefaultIsServicePortSecureResolver isServicePortSecureResolver) {
 return new KubernetesDiscoveryClient(client, properties,
 kubernetesClientServicesFunction, isServicePortSecureResolver);
} 

8、至此,第一个问题算是弄清楚了:我们编写的DiscoveryController类所需的DiscoveryClient接口实现类是KubernetesDiscoveryClient,用到的是spring规范中的spring.factories

9、另外有一点很重要,下面要用到的:KubernetesDiscoveryClient有个成员变量是KubernetesClient,该变量的值是DefaultKubernetesClient实例;

接下来看第二个问题;

java应用怎么能取得所在kubernetes的服务信息

1、看看DiscoveryController是如何获取所在kubernetes的服务信息的:

@RequestMapping("/services")
 public String services() {
 return this.discoveryClient.getServices().toString()
 + ", "
 + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
 }

如上所示,
discoveryClient.getServices()方法返回了所有kubernetes的服务信息;

2、discoveryClient对应的类是spring-cloud-kubernetes项目的
KubernetesDiscoveryClient.java,看方法:

public List getServices(Predicate filter) {
 return this.kubernetesClientServicesFunction.apply(this.client).list().getItems()
 .stream().filter(filter).map(s -> s.getMetadata().getName())
 .collect(Collectors.toList());
 }

这代码的关键在于
this.kubernetesClientServicesFunction.apply(this.client).list(),先看KubernetesClientServicesFunction实例的初始化过程,在KubernetesDiscoveryClientAutoConfiguration类中:

@Bean
public KubernetesClientServicesFunction servicesFunction(
 KubernetesDiscoveryProperties properties) {
 if (properties.getServiceLabels().isEmpty()) {
 return KubernetesClient::services;
 }
 return (client) -> client.services().withLabels(properties.getServiceLabels());
}

KubernetesClientServicesFunction是个lambda表达式,用于KubernetesClient的时候,返回KubernetesClient.services()的结果,如果指定了标签过滤,就用指定的标签来做过滤(也就是kubernetes中的标签选择器的效果)

因此,数据来源其实就是上面的this.client,调用其services方法的返回结果;

3、
KubernetesDiscoveryClient.getServices方法中的this.client是什么呢?分析前面的问题时已经提到过了,就是DefaultKubernetesClient类的实例,所以,此时要去看DefaultKubernetesClient.services方法,发现client是ServiceOperationsImpl实例:

@Override
 public MixedOperation> services() {
 return new ServiceOperationsImpl(httpClient, getConfiguration(), getNamespace());
 } 

4、接着看
ServiceOperationsImpl.java,我们关心的是它的list方法,此方法在父类BaseOperation中找到:

public L list() throws KubernetesClientException {
 try {
 HttpUrl.Builder requestUrlBuilder = HttpUrl.get(getNamespacedUrl()).newBuilder();
 String labelQueryParam = getLabelQueryParam();
 if (Utils.isNotNullOrEmpty(labelQueryParam)) {
 requestUrlBuilder.addQueryParameter("labelSelector", labelQueryParam);
 }
 String fieldQueryString = getFieldQueryParam();
 if (Utils.isNotNullOrEmpty(fieldQueryString)) {
 requestUrlBuilder.addQueryParameter("fieldSelector", fieldQueryString);
 }
 Request.Builder requestBuilder = new Request.Builder().get().url(requestUrlBuilder.build());
 L answer = handleResponse(requestBuilder, listType);
 updateApiVersion(answer);
 return answer;
 } catch (InterruptedException | ExecutionException | IOException e) {
 throw KubernetesClientException.launderThrowable(forOperationType("list"), e);
 }
 }

展开上面代码的handleResponse方法,可见里面是一次http请求,至于请求的地址,可以展开getNamespacedUrl()方法,里面调用的getRootUrl方法如下:

public URL getRootUrl() {
 try {
 if (apiGroup != null) {
 return new URL(URLUtils.join(config.getMasterUrl().toString(), "apis", apiGroup, apiVersion));
 }
 return new URL(URLUtils.join(config.getMasterUrl().toString(), "api", apiVersion));
 } catch (MalformedURLException e) {
 throw KubernetesClientException.launderThrowable(e);
 }
 }

可见最终的地址应该是:xxxxxx/api/v1或者xxxxxx/apis/xx/v1这样的字符串。

这样的字符串意味着什么呢?这是访问kubernetes的API Server时用到的URL标准格式,有关API Server服务的详情请参考官方文档,地址是:
https://kubernetes.io/docs/reference/using-api/api-concepts/

如下图,用OperationSupport类的源码和官方文档的URL截图做个对比,大家就一目了然了:

spring-cloud-kubernetes背后的三个关键知识点_第2张图片

5、还剩个小问题,上图中,OperationSupport类的成员变量resourceT是什么值?官方文档示例中是"pods",在获取service的时候又该是多少呢?顺着源码一路找下去,找到了类的构造方法,如下所示,第五个参数就是resourceT,这里直接被写死为"services":

public ServiceOperationsImpl(OkHttpClient client, Config config, String apiVersion, String namespace, String name, Boolean cascading, Service item, String resourceVersion, Boolean reloadingFromServer, long gracePeriodSeconds, Map labels, Map labelsNot, Map labelsIn, Map labelsNotIn, Map fields) {
 super(client, config, null, apiVersion, "services", namespace, name, cascading, item, resourceVersion, reloadingFromServer, gracePeriodSeconds, labels, labelsNot, labelsIn, labelsNotIn, fields);
 }

至此,第二个问题“controller中用到的kubernetes服务数据从何而来"已经清楚了:最终是调用okhttp的newCall方法向kubernetes的API Server发起http请求,获取service资源的数据列表;

接下来,该最后一个问题了;

API Server收到请求后做了什么?

关于API Server如何响应各类http请求,本文只做一些简单的说明,详细信息还请参考官方文档,地址是:
https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/

如下图所示,在kubernetes环境中,pod、service这些资源的数据都存储在etcd,任何服务想要增删改查etcd的数据,都只能通过向API Server发起RestFul请求的方式来完成,咱们的DiscoveryController类获取所有service也是发请求到API Server,由API Server从etcd中取得service的数据返回给DiscoveryController:

spring-cloud-kubernetes背后的三个关键知识点_第3张图片

至此,spring-cloud-kubernetes背后的三个关键知识点都已经学习了,下图算是对这些问题的一个小结:

spring-cloud-kubernetes背后的三个关键知识点_第4张图片

希望以上的分析总结能对您有参考作用,由于对基本原理都已经了解,后面的spring-cloud-kubernetes实战可以更顺畅,也能从原理出发继续深入的分析和学习。

 

获取更多学习教材 添加微信 leftbaby999 然后私信扣【888】免费领取资料Java进阶全套视频以及源码学习资料

                                  (文档资料只供50份,先到先得)spring-cloud-kubernetes背后的三个关键知识点_第5张图片

 

你可能感兴趣的:(后端,java实战,构架,java,后端,面试,spring,数据库架构)