写点什么

Kubernetes 官方 java 客户端之八:fluent style

作者:程序员欣宸
  • 2022 年 4 月 05 日
  • 本文字数:4885 字

    阅读完需:约 16 分钟

Kubernetes官方java客户端之八:fluent style

欢迎访问我的 GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos

概览

  1. 本文是《Kubernetes 官方 java 客户端》系列的第八篇,以下提到的 java 客户端都是指 client-jar.jar;

  2. 前文《Kubernetes官方java客户端之七:patch操作》涉及的知识点、代码、操作都太多了,对作者和读者都是莫大的折磨,到了本篇咱们轻松一下,写几段简单流畅的代码,了解 java 客户端对 fluent style 编程的支持,并且编码完成后的验证操作也很简单;

关于 fluent styel

  1. 也称为 fluid coding, fluent programming,是一种增强代码可读性的风格,使得阅读代码时更加自然流畅,特点是函数返回有关类型,使得多个函数的调用前后链接起来。

  2. 关于 fluent style 可以参考 Martin Flowler 于 2005 年发表的文章,地址是:https://martinfowler.com/bliki/FluentInterface.html ,使用 fluent style 前后的代码对比如下图所示:


源码下载

  1. 如果您不想编码,可以在 GitHub 下载所有源码,地址和链接信息如下表所示(https://github.com/zq2599/blog_demos):



  1. 这个 git 项目中有多个文件夹,本章的应用在 kubernetesclient 文件夹下,如下图红框所示:


实战步骤概述

  1. 在父工程 kubernetesclient 下面新建名为 fluent 的子工程;

  2. fluent 工程中只有一个类 FluentStyleApplication,启动的 main 方法以及 fluent style 的代码都在此类中;

  3. FluentStyleApplication.java 提供四个 web 接口,功能分别是:新建 namespace、新建 deployment、新建 service、删除前面三个接口新建的所有资源;

  4. fluent 工程编码完成后,不需要做成镜像部署在 kubernetes 环境内部,而是作为一个普通 SpringBoot 应用找个 java 环境启动即可,与《Kubernetes官方java客户端之三:外部应用 》一文的部署和启动一致;

  5. 依次调用每个接口,验证功能是否符合预期;

编码

  1. 在父工程 kubernetesclient 下面新建名为 fluent 的 maven 子工程,pom.xml 内容如下,需要注意的是排除掉 spring-boot-starter-json,原因请参考《Kubernetes官方java客户端之二:序列化和反序列化问题 》

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>
<parent> <groupId>com.bolingcavalry</groupId> <artifactId>kubernetesclient</artifactId> <version>1.0-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent>
<groupId>com.bolingcavalry</groupId> <artifactId>fluent</artifactId> <version>0.0.1-SNAPSHOT</version> <name>fluent</name> <description>Demo project for fluent style</description> <packaging>jar</packaging>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-json</artifactId> </exclusion> </exclusions> </dependency>
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency>
<dependency> <groupId>io.kubernetes</groupId> <artifactId>client-java</artifactId> </dependency>
</dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.3.0.RELEASE</version> </plugin> </plugins> </build>
</project>
复制代码
  1. 新建 FluentStyleApplication.java,首先,该类作为启动类,要有 main 方法:


public static void main(String[] args) {  SpringApplication.run(FluentStyleApplication.class, args);}
复制代码


  1. 定义常量 NAMESPACE 作为本次实战的 namespace:


private final static String NAMESPACE = "fluent";
复制代码


  1. 用 @PostConstruct 注解修饰 setDefaultApiClient 方法,令其在实例化时执行一次,里面做了一些全局性的初始化设置,注意 kubeConfigPath 变量对应的 config 文件路径要正确:


		/**     * 默认的全局设置     * @return     * @throws Exception     */    @PostConstruct    private void setDefaultApiClient() throws Exception {        // 存放K8S的config文件的全路径        String kubeConfigPath = "/Users/zhaoqin/temp/202007/05/config";        // 以config作为入参创建的client对象,可以访问到K8S的API Server        ApiClient client = ClientBuilder                .kubeconfig(KubeConfig.loadKubeConfig(new FileReader(kubeConfigPath)))                .build();
// 会打印和API Server之间请求响应的详细内容,生产环境慎用 client.setDebugging(true);
// 创建操作类 Configuration.setDefaultApiClient(client); }
复制代码


  1. 接下来是创建 namespace 的 web 服务,如下所示,由于 namespace 在 kubernetes 的 apiVersion 是 v1,因此创建的是 V1Namespace 实例:


 @RequestMapping(value = "/fluent/createnamespace")    public V1Namespace createnamespace() throws Exception {
V1Namespace v1Namespace = new V1NamespaceBuilder() .withNewMetadata() .withName(NAMESPACE) .addToLabels("label1", "aaa") .addToLabels("label2", "bbb") .endMetadata() .build();
return new CoreV1Api().createNamespace(v1Namespace, null, null, null); }
复制代码
  1. 为了更清晰的展现 fluent style 效果,将上述代码与创建 namespace 的 yaml 文件内容放在一起对比,如下图所示,可见对照着 yaml 文件就能将代码写出来:

  2. 接下来是创建 service 的代码,为了便于和 yaml 对应起来,代码中特意加了缩进:


 @RequestMapping(value = "/fluent/createservice")    public V1Service createservice() throws Exception {        V1Service v1Service = new V1ServiceBuilder()                // meta设置                .withNewMetadata()                    .withName("nginx")                .endMetadata()
// spec设置 .withNewSpec() .withType("NodePort") .addToPorts(new V1ServicePort().port(80).nodePort(30103)) .addToSelector("name", "nginx") .endSpec() .build();
return new CoreV1Api().createNamespacedService(NAMESPACE, v1Service, null, null, null); }
复制代码


  1. 创建 deployment 的代码如下,因为内容较多所以相对复杂一些,请注意,由于 deployment 在 kubernetes 的 apiVersion 是 extensions/v1beta1,因此创建的是 ExtensionsV1beta1Deployment 实例:


 @RequestMapping(value = "/fluent/createdeployment")    public ExtensionsV1beta1Deployment createdeployment() throws Exception {        ExtensionsV1beta1Deployment v1Deployment = new ExtensionsV1beta1DeploymentBuilder()                // meta设置                .withNewMetadata()                    .withName("nginx")                .endMetadata()
// spec设置 .withNewSpec() .withReplicas(1) // spec的templat .withNewTemplate() // template的meta .withNewMetadata() .addToLabels("name", "nginx") .endMetadata()
// template的spec .withNewSpec() .addNewContainer() .withName("nginx") .withImage("nginx:1.18.0") .addToPorts(new V1ContainerPort().containerPort(80)) .endContainer() .endSpec()
.endTemplate() .endSpec() .build();
return new ExtensionsV1beta1Api().createNamespacedDeployment(NAMESPACE, v1Deployment, null, null, null); }
复制代码


  1. 从上述代码可见,withXXX endXXX 是成对出现的,请在编程的时候注意不要遗漏了 endXXX,建议在写 withXXX 的同时就把 endXXX 写上;

  2. 最后一个方法是清理所有资源的,前面创建的 deployment、service、namespace 都在此一次性清理掉,实际操作中发现了一个尴尬的情况:删除 deployment 和 namespace 时,发送到 API Server 的删除请求都收到的操作成功的响应,但 kubernetes 客户端在反序列化响应内容时抛出异常(日志中显示了详细情况),鄙人能力有限暂未找到解决之道,因此只能用 try catch 来避免整个方法抛出异常,好在 kubernetes 实际上已经删除成功了,影响不大:


    @RequestMapping(value = "/fluent/clear")    public String clear() throws Exception {
// 删除deployment try { new ExtensionsV1beta1Api().deleteNamespacedDeployment("nginx", NAMESPACE, null, null, null, null, null, null); } catch (Exception e) { log.error("delete deployment error", e); }
CoreV1Api coreV1Api = new CoreV1Api();
// 删除service coreV1Api.deleteNamespacedService("nginx", NAMESPACE, null, null, null, null, null, null);
// 删除namespace try { coreV1Api.deleteNamespace(NAMESPACE, null, null, null, null, null, null); } catch (Exception e) { log.error("delete namespace error", e); }
return "clear finish, " + new Date(); }
复制代码


  1. 编码已完成,启动 fluent 工程,接下来开始验证功能是否正常;

验证

  1. 将 fluent 工程直接在 IEDA 环境启动;

  2. 浏览器访问:http://localhost:8080/fluent/createnamespace ,页面会展示 API Server 返回的完整 namespace 信息:

  1. 浏览器访问:http://localhost:8080/fluent/createservice ,页面会展示 API Server 返回的完整 service 信息:

  2. 浏览器访问:http://localhost:8080/fluent/createdeployment ,页面会展示 API Server 返回的完整 deployment 信息:


  1. 验证前面几个接口创建的服务是否可用,我这里 kubernetes 的 IP 地址是 192.168.50.135,因此访问:http://192.168.50.135:30103 ,可以正常显示 nginx 首页:


  1. SSH 登录 kubernetes 服务器查看,通过 kubernetes 的 java 客户端创建的资源都正常:


  1. 验证完成后,浏览器访问:http://localhost:8080/fluent/clear ,即可清理掉前面三个接口创建的资源;


  • 至此,基于 fluent style 调用 java 客户端的实战就完成了,希望您能熟练使用此风格的 API 调用,使得编码变得更加轻松流畅,顺便预告一下,下一篇继续做一些简单轻松的操作,目标是熟悉 java 客户端的常用操作;

欢迎关注 InfoQ:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...

发布于: 刚刚阅读数: 2
用户头像

搜索"程序员欣宸",一起畅游Java宇宙 2018.04.19 加入

前腾讯、前阿里员工,从事Java后台工作,对Docker和Kubernetes充满热爱,所有文章均为作者原创,个人Github:https://github.com/zq2599/blog_demos

评论

发布
暂无评论
Kubernetes官方java客户端之八:fluent style_四月月更_程序员欣宸_InfoQ写作平台