前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >jetcd实战之一:极速体验

jetcd实战之一:极速体验

原创
作者头像
程序员欣宸
修改2021-09-24 11:03:38
1.1K0
修改2021-09-24 11:03:38
举报
文章被收录于专栏:实战docker实战docker

欢迎访问我的GitHub

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

关于jetcd

关于jetcd实战系列

《jetcd实战系列》是欣宸新的原创系列,旨在与大家一起学习如何用jetcd操作etcd,除了基本增删改查,还会涉及到version、监听、租约等etcd特有功能;

系列文章链接

  1. jetcd实战之一:极速体验
  2. jetcd实战之二:基本操作
  3. jetcd实战之三:进阶操作(事务、监听、租约)

本篇概览

作为《jetcd实战系列》的开篇,主要是为整个系列做准备工作,包括以下内容:

  1. 梳理实战涉及到的应用和库的版本信息;
  2. 基于docker-compose部署etcd集群;
  3. 新建gradle工程,作为整个实战系列的父工程;
  4. 编写helloworld应用,验证jetcd可以正常访问etcd集群;

源码下载

名称

链接

备注

项目主页

该项目在GitHub上的主页

git仓库地址(https)

该项目源码的仓库地址,https协议

git仓库地址(ssh)

git@github.com:zq2599/blog_demos.git

该项目源码的仓库地址,ssh协议

  • 这个git项目中有多个文件夹,kubebuilder相关的应用在jetcd-tutorials文件夹下,如下图红框所示:
    在这里插入图片描述
    在这里插入图片描述
  • jetcd-tutorials文件夹下有多个子项目,本篇的是helloworld
    在这里插入图片描述
    在这里插入图片描述

版本信息

实战系列所用的etcd是部署在docker环境下的三个实例组建的集群:

  1. etcd:3.4.7
  2. docker:20.10.5(Community)
  3. docker-compose:1.28.5
  4. jetcd:0.5.0
  5. jdk:1.8.0_271
  6. springboot:2.4.4
  7. IDEA:2020.2.3 (Ultimate Edition)
  8. gradle:6.8.3
  9. 电脑操作系统:macOS Big Sur 11.2.3

部署集群

  • 确认docker和docker-compose已正常运行
  • 新建docker-compose.yml文件:
代码语言:txt
复制
version: '3'
services:
  etcd1:
    image: "quay.io/coreos/etcd:v3.4.7"
    entrypoint: /usr/local/bin/etcd
    command:
      - '--name=etcd1'
      - '--data-dir=/etcd_data'
      - '--initial-advertise-peer-urls=http://etcd1:2380'
      - '--listen-peer-urls=http://0.0.0.0:2380'
      - '--listen-client-urls=http://0.0.0.0:2379'
      - '--advertise-client-urls=http://etcd1:2379'
      - '--initial-cluster-token=etcd-cluster'
      - '--heartbeat-interval=250'
      - '--election-timeout=1250'
      - '--initial-cluster=etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380'
      - '--initial-cluster-state=new'
    ports:
      - 2379:2379
    volumes:
      - ./store/etcd1/data:/etcd_data
  etcd2:
    image: "quay.io/coreos/etcd:v3.4.7"
    entrypoint: /usr/local/bin/etcd
    command:
      - '--name=etcd2'
      - '--data-dir=/etcd_data'
      - '--initial-advertise-peer-urls=http://etcd2:2380'
      - '--listen-peer-urls=http://0.0.0.0:2380'
      - '--listen-client-urls=http://0.0.0.0:2379'
      - '--advertise-client-urls=http://etcd2:2379'
      - '--initial-cluster-token=etcd-cluster'
      - '--heartbeat-interval=250'
      - '--election-timeout=1250'
      - '--initial-cluster=etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380'
      - '--initial-cluster-state=new'
    ports:
      - 2380:2379
    volumes:
      - ./store/etcd2/data:/etcd_data
  etcd3:
    image: "quay.io/coreos/etcd:v3.4.7"
    entrypoint: /usr/local/bin/etcd
    command:
      - '--name=etcd3'
      - '--data-dir=/etcd_data'
      - '--initial-advertise-peer-urls=http://etcd3:2380'
      - '--listen-peer-urls=http://0.0.0.0:2380'
      - '--listen-client-urls=http://0.0.0.0:2379'
      - '--advertise-client-urls=http://etcd3:2379'
      - '--initial-cluster-token=etcd-cluster'
      - '--heartbeat-interval=250'
      - '--election-timeout=1250'
      - '--initial-cluster=etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380'
      - '--initial-cluster-state=new'
    ports:
      - 2381:2379
    volumes:
      - ./store/etcd3/data:/etcd_data
  • 从上述脚本可见,宿主机的2379、2380、2381三个端口被用来映射三个etcd容器的2379端口;
  • 执行命令docker-compose up -d,如下,可见三个容器都已经创建,名称分别是28_etcd1_128_etcd2_128_etcd3_1,容器名和当前目录名有关:
代码语言:txt
复制
zhaoqin@zhaoqindeMacBook-Pro-2 28 % docker-compose up -d
Creating network "28_default" with the default driver
Creating 28_etcd2_1 ... done
Creating 28_etcd3_1 ... done
Creating 28_etcd1_1 ... done
  • etcd集群已启动成功,来试试基本操作命令是否正常,执行以下命令新建一个键值对,键是/aaa/foo,值是111
代码语言:txt
复制
docker exec 28_etcd1_1 /usr/local/bin/etcdctl put /aaa/foo 111
  • 查看命令如下(我这换了个容器):
代码语言:txt
复制
docker exec 28_etcd2_1 /usr/local/bin/etcdctl get /aaa/foo -w fields
  • 得到的是完整的结果,除了键值还有Revision、ModRevision、Version、Lease等字段:
代码语言:txt
复制
"ClusterID" : 10316109323310759371
"MemberID" : 15168875803774599630
"Revision" : 2
"RaftTerm" : 2
"Key" : "/aaa/foo"
"CreateRevision" : 2
"ModRevision" : 2
"Version" : 1
"Value" : "111"
"Lease" : 0
"More" : false
"Count" : 1

新建gradle工程,作为整个实战系列的父工程

  • 接下来新建一个gradle工程,整个实战系列都是在此父工程下开发;
  • 新建gradle工程名为jetcd-tutorials,其build.gradle内容如下:
代码语言:txt
复制
import java.time.OffsetDateTime
import java.time.format.DateTimeFormatter

// gradle自身会用到的相关设置
buildscript {
    // 仓库
    repositories {
        // 本地
        mavenLocal()
        // 如果有私服就在此配置,如果没有请注释掉
        maven {
            url 'http://192.168.50.43:8081/repository/aliyun-proxy/'
        }
        // 阿里云
        maven {
            url 'http://maven.aliyun.com/nexus/content/groups/public/'
        }
        // 中央仓库
        mavenCentral()
        // grandle插件
        maven {
            url 'https://plugins.gradle.org/m2/'
        }
    }

    // 子模块会用到的变量
    ext {
        springBootVersion = '2.4.4'
    }
}

// 插件
plugins {
    id 'java'
    id 'java-library'
    // 有这个声明,子模块可以使用org.springframework.boot插件而无需指定版本,但是apply=false表示当前模块不使用此插件
    id 'org.springframework.boot' version "${springBootVersion}" apply false
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
}

// gradle wrapper指定版本
wrapper {
    gradleVersion = '6.8.3'
}

// 取当前时间
def buildTimeAndDate = OffsetDateTime.now()

// 根据时间生成字符串变量
ext {
    projectVersion = project.version
    buildDate = DateTimeFormatter.ISO_LOCAL_DATE.format(buildTimeAndDate)
    buildTime = DateTimeFormatter.ofPattern('HH:mm:ss.SSSZ').format(buildTimeAndDate)
}


// 针对所有project的配置,包含根项目
allprojects {
    group 'com.bolingcavalry'
    version '1.0-SNAPSHOT'

    apply plugin: 'java'
    apply plugin: 'idea'
    apply plugin: 'io.spring.dependency-management'

    // 编译相关参数
    compileJava {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
        options.encoding = 'UTF-8'
        options.compilerArgs =  [
                '-Xlint:all', '-Xlint:-processing'
        ]
    }

    // Copy LICENSE
    tasks.withType(Jar) {
        from(project.rootDir) {
            include 'LICENSE'
            into 'META-INF'
        }
    }

    // 生成jar文件时,MANIFEST.MF的内容如下
    jar {
        manifest {
            attributes(
                    'Created-By': "${System.properties['java.version']} (${System.properties['java.vendor']} ${System.properties['java.vm.version']})".toString(),
                    'Built-By': 'travis',
                    'Build-Date': buildDate,
                    'Build-Time': buildTime,
                    'Built-OS': "${System.properties['os.name']}",
                    'Specification-Title': project.name,
                    'Specification-Version': project.version,
                    'Specification-Vendor': 'Will Zhao',
                    'Implementation-Title': project.name,
                    'Implementation-Version': project.version,
                    'Implementation-Vendor': 'Will Zhao'
            )
        }
    }

    // 仓库
    repositories {
        // 本地
        mavenLocal()
        // 如果有私服就在此配置,如果没有请注释掉
        maven {
            url 'http://192.168.50.43:8081/repository/aliyun-proxy/'
        }
        // 阿里云
        maven {
            url 'http://maven.aliyun.com/nexus/content/groups/public/'
        }
        // 中央仓库
        mavenCentral()
        // grandle插件
        maven {
            url "https://plugins.gradle.org/m2/"
        }
    }
}

// 类似maven的dependencyManagement,这里将所有jar的版本指定好,子模块在依赖时可以不用指定版本
allprojects { project ->
    buildscript {
        dependencyManagement {
            imports {
                mavenBom "org.springframework.boot:spring-boot-starter-parent:${springBootVersion}"
                mavenBom "org.junit:junit-bom:5.7.0"
            }

            dependencies {
                dependency 'org.projectlombok:lombok:1.16.16'
                dependency 'org.apache.commons:commons-lang3:3.11'
                dependency 'commons-collections:commons-collections:3.2.2'
                dependency 'io.etcd:jetcd-core:0.5.0'
                dependency 'org.slf4j:slf4j-log4j12:1.7.30'
            }
        }
    }
}

// 坐标信息
group 'com.bolingcavalry'
version '1.0-SNAPSHOT'
  • 现在根模块已经建好,后面整个系列的代码都会写在这个根模块下面;

编写helloworld应用

  • 接下来写个helloworld应用验证jetcd能不能操作etcd集群;
  • 在根模块下面新建名为helloworld的gradle子模块,其build.gradle内容如下:
代码语言:txt
复制
plugins {
    id 'java'
}

// 子模块自己的依赖
dependencies {
    implementation 'io.etcd:jetcd-core'
    implementation 'org.projectlombok:lombok'
    // annotationProcessor不会传递,使用了lombok生成代码的模块,需要自己声明annotationProcessor
    annotationProcessor 'org.projectlombok:lombok'
    implementation 'org.slf4j:slf4j-log4j12'
    testImplementation('org.junit.jupiter:junit-jupiter')
}

test {
    useJUnitPlatform()
}

group 'com.bolingcavalry'
version '1.0-SNAPSHOT'
  1. 新增HelloWorld.java,代码很简单,getKVClient方法用来生成客户端实例,put方法向etcd写入键值对,get方法去etcd查询指定键的值:
代码语言:txt
复制
package com.bolingcavalry;

import io.etcd.jetcd.ByteSequence;
import io.etcd.jetcd.Client;
import io.etcd.jetcd.KV;
import io.etcd.jetcd.kv.GetResponse;
import io.etcd.jetcd.kv.PutResponse;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import static com.google.common.base.Charsets.UTF_8;

@Slf4j
public class HelloWorld {

    /**
     * 新建key-value客户端实例
     * @return
     */
    private KV getKVClient(){
        String endpoints = "http://192.168.50.239:2379,http://192.168.50.239:2380,http://192.168.50.239:2381";
        Client client = Client.builder().endpoints(endpoints.split(",")).build();
        return client.getKVClient();
    }

    /**
     * 将字符串转为客户端所需的ByteSequence实例
     * @param val
     * @return
     */
    private static ByteSequence bytesOf(String val) {
        return ByteSequence.from(val, UTF_8);
    }

    /**
     * 查询指定键对应的值
     * @param key
     * @return
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public String get(String key) throws ExecutionException, InterruptedException{
        log.info("start get, key [{}]", key);
        GetResponse response = getKVClient().get(bytesOf(key)).get();

        if (response.getKvs().isEmpty()) {
            log.error("empty value of key [{}]", key);
            return null;
        }

        String value = response.getKvs().get(0).getValue().toString(UTF_8);
        log.info("finish get, key [{}], value [{}]", key, value);
        return value;
    }

    /**
     * 创建键值对
     * @param key
     * @param value
     * @return
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public PutResponse put(String key, String value) throws ExecutionException, InterruptedException {
        log.info("start put, key [{}], value [{}]", key, value);
        return getKVClient().put(bytesOf(key), bytesOf(value)).get();
    }
}
  1. 接下来咱们写个单元测试类来验证上述代码是否有效:
代码语言:txt
复制
package com.bolingcavalry;

import io.etcd.jetcd.kv.PutResponse;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import java.util.concurrent.ExecutionException;
import static org.junit.jupiter.api.Assertions.*;

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class HelloWorldTest {
    // 用与测试的键
    private static final String KEY = "/abc/foo-" + System.currentTimeMillis();

    // 用于测试的值
    private static final String VALUE = "/abc/foo";

    @org.junit.jupiter.api.Test
    @Order(2)
    void get() throws ExecutionException, InterruptedException {
        String getResult = new HelloWorld().get(KEY);
        assertEquals(VALUE, getResult);
    }

    @Test
    @Order(1)
    void put() throws ExecutionException, InterruptedException {
        PutResponse putResponse = new HelloWorld().put(KEY, VALUE);
        assertNotNull(putResponse);
        assertNotNull(putResponse.getHeader());
    }
}
  1. 代码写完后按照下图红框指示执行单元测试,可见jetcd操作etcd成功:
在这里插入图片描述
在这里插入图片描述
  • 至此,《jetcd实战》系列的开篇就完成了,咱们搭建好了etcd集群,还初步体验过jetcd的基本功能,接下来的章节会从基本操作开始,由浅入深的学习jetcd;

你不孤单,欣宸原创一路相伴

  1. Java系列
  2. Spring系列
  3. Docker系列
  4. kubernetes系列
  5. 数据库+中间件系列
  6. DevOps系列

欢迎关注公众号:程序员欣宸

微信搜索「程序员欣宸」,我是欣宸,期待与您一同畅游Java世界...

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 欢迎访问我的GitHub
  • 关于jetcd
  • 关于jetcd实战系列
  • 系列文章链接
  • 本篇概览
  • 源码下载
  • 版本信息
  • 部署集群
  • 新建gradle工程,作为整个实战系列的父工程
  • 编写helloworld应用
  • 你不孤单,欣宸原创一路相伴
  • 欢迎关注公众号:程序员欣宸
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档