spring-cloud-kubernetes背后的三个关键知识点
在一文中,对spring-cloud-kubernetes这个springcloud官方kubernetes服务框架有了基本了解,今天来小结此框架涉及的关键技术,为后面的深入学习做准备;
系列文章列表
本文是《spring-cloud-kubernetes实战系列》的第三篇,全文链接如下:
概览
总结下来有三个关键知识点需要深入理解:
- discoveryclient是个接口,对应的实现类是哪个?
- discoveryclient.getservices()方法取得了kubernetes的service信息,这背后的机制是什么?java应用是怎样取得所在kubernetes的服务信息的?
- 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说起;
- 在pom.xml中,有对spring-cloud-kubernetes框架的依赖配置:
<dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-kubernetes-discovery</artifactid> <version>1.0.1.release</version> </dependency>
- 打开spring-cloud-kubernetes-discovery的源码,地址是:https://github.com/spring-cloud/spring-cloud-kubernetes/tree/master/spring-cloud-kubernetes-discovery ,在这个工程中发现了文件spring.factories:
- spring容器启动时,会寻找classpath下所有spring.factories文件(包括jar文件中的),spring.factories中配置的所有类都会实例化,我们在开发springboot时常用到的xxx-starter.jar就用到了这个技术,效果是一旦依赖了某个starter.jar很多功能就在spring初始化时候自动执行了(例如mysql的starter,启动时会连接数据库),关于此技术的详情,请参考以下三篇文章:
- spring.factories文件中有两个类:kubernetesdiscoveryclientautoconfiguration和kubernetesdiscoveryclientconfigclientbootstrapconfiguration都会被实例化;
- 先看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 { }
- 在kubernetesautoconfiguration的源码中,会实例化一个重要的类:defaultkubernetesclient,如下:
@bean @conditionalonmissingbean public kubernetesclient kubernetesclient(config config) { return new defaultkubernetesclient(config); }
- 再看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); }
- 至此,第一个问题算是弄清楚了:我们编写的discoverycontroller类所需的discoveryclient接口实现类是kubernetesdiscoveryclient,用到的是spring规范中的spring.factories
- 另外有一点很重要,下面要用到的:kubernetesdiscoveryclient有个成员变量是kubernetesclient,该变量的值是defaultkubernetesclient实例;
接下来看第二个问题;
java应用怎么能取得所在kubernetes的服务信息
- 看看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的服务信息;
- discoveryclient对应的类是spring-cloud-kubernetes项目的kubernetesdiscoveryclient.java,看方法:
public list<string> getservices(predicate<service> 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方法的返回结果;
- kubernetesdiscoveryclient.getservices方法中的this.client是什么呢?分析前面的问题时已经提到过了,就是defaultkubernetesclient类的实例,所以,此时要去看defaultkubernetesclient.services方法,发现client是serviceoperationsimpl实例:
@override public mixedoperation<service, servicelist, doneableservice, serviceresource<service, doneableservice>> services() { return new serviceoperationsimpl(httpclient, getconfiguration(), getnamespace()); }
- 接着看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截图做个对比,大家就一目了然了:
- 还剩个小问题,上图中,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<string, string> labels, map<string, string> labelsnot, map<string, string[]> labelsin, map<string, string[]> labelsnotin, map<string, string> 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:
如果您想弄清楚service数据在etcd中如何存储的,可以参考一文,亲自动手连接etcd查看里面的service内容;
至此,spring-cloud-kubernetes背后的三个关键知识点都已经学习了,下图算是对这些问题的一个小结:
希望以上的分析总结能对您有参考作用,由于对基本原理都已经了解,后面的spring-cloud-kubernetes实战可以更顺畅,也能从原理出发继续深入的分析和学习。
欢迎关注我的公众号:程序员欣宸
上一篇: 安卓平滑曲线的实现:三次贝塞尔曲线
下一篇: 调用arcpy包批量进行矢量掩膜提取
推荐阅读
-
小程序8000亿交易规模背后:流量和赚钱的关键10问来了
-
破坏亲密关系的三个关键词 夫妻相处避开这些雷
-
日均指数1W5的关键词“垃圾分类”排名百度首页,在三个月如何做到
-
吕惠卿本是王安石最亲密的战友,为何在最关键的时候背后捅刀子?
-
搜索功能(五)03-搜索关键字历史重复记录-数组去重操作——es6 set类数组-数据结构-数组去重 & 三个点(...)方法-对象的延展操作-拆解数组
-
Kimball多维体系结构中的三个关键性概念
-
C#中类成员的定义的修饰关键词知识点总结
-
荐 2020年Java面试上必问的26个高频关键知识点,刷三遍必进阿里腾讯大厂!就这么自信!
-
spring-cloud-kubernetes背后的三个关键知识点
-
推荐阅读网站被用户喜爱的秘密 :挖掘关键词背后的用户需求