当前位置: 首页 > article >正文

springboot 集成 etcd

springboot 集成 etcd

往期内容

  • ETCD 简介
  • docker部署ETCD

前言

好久不见各位小伙伴们,上两期内容中,我们对于分布式kv存储中间件有了简单的认识,完成了docker-compose 部署etcd集群以及可视化工具 etcd Keeper,既然有了认识,完成了部署,那么当然要用起来啦

那么本期我们简单使用springboot 集成etcd 实现一些简单的数据操作


1-创建springboot工程

对于java开发的小伙伴来说,springboot项目的创建这块,我们不在过多赘述,简单贴一下我的版本依赖,我们直接跳转至下一环节

<properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.6.13</spring-boot.version>
    </properties>

2-集成依赖

简单的开发需要集成ETCD的依赖

那么直接pom.xml

<!-- https://mvnrepository.com/artifact/io.etcd/jetcd-core 主要依赖-->
        <dependency>
            <groupId>io.etcd</groupId>
            <artifactId>jetcd-core</artifactId>
            <version>0.7.5</version>
        </dependency>


        <!-- (可选依赖)->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.26</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.21</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.21</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
            <version>2.0.52</version>
        </dependency>

3-全局配置

application.yml

#etcd
etcd:
  host: http://XXXXXX #你自己的宿主机
  port: 12379  #etcd的宿主机port 上一节中我的容器中的2379映射到了宿主机上的12379 所以配置12379

增加全局配置文件读取yml配置信息

@Configuration
@Data
public class EtcdConfig {

    @Value("${etcd.host}")
    private String etcdHost;

    @Value("${etcd.port}")
    private int etcdPort;

}

创建etcd连接工厂

 @Bean
    public Client etcdFactory() {
        return Client.builder().endpoints(etcdHost + ":" + etcdPort).build();
    }

4-接入使用

创建service类进行简单的操作,这里简单的存储和redis一样,也是key-value的形式,不过多赘述

对于租约lease 而言类似一redis的键设置过期时间,过期时间内可以续期,过期时间之后etcd将会删除存储的内容

package com.jerry.springetcd.service;

import com.alibaba.fastjson2.JSON;
import io.etcd.jetcd.ByteSequence;
import io.etcd.jetcd.Client;
import io.etcd.jetcd.Lease;
import io.etcd.jetcd.kv.GetResponse;
import io.etcd.jetcd.kv.PutResponse;
import io.etcd.jetcd.lease.LeaseTimeToLiveResponse;
import io.etcd.jetcd.options.LeaseOption;
import io.etcd.jetcd.options.PutOption;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

@Service

public class EtcdService {

    @Resource
    private Client etcdClient;

    /***
     * Put a key-value pair to etcd
     * @param key
     * @param value
     * @return
     */
    public CompletableFuture<PutResponse> putValueWithOutLease(String key, String value) {
        ByteSequence keyByte = ByteSequence.from(key, StandardCharsets.UTF_8);
        ByteSequence valueByte = ByteSequence.from(value, StandardCharsets.UTF_8);

        return etcdClient.getKVClient().put(keyByte, valueByte);
    }

    /***
     * Get the value of a key from etcd
     * @param key
     * @return
     */
    public CompletableFuture<String> getValue(String key) {
        ByteSequence keyByte = ByteSequence.from(key, StandardCharsets.UTF_8);
        CompletableFuture<GetResponse> getFuture = etcdClient.getKVClient().get(keyByte);

        return getFuture.thenApply(getResponse -> {
            if (getResponse.getKvs().isEmpty()) {
                return null;
            }
            return getResponse.getKvs().get(0).getValue().toString(StandardCharsets.UTF_8);
        });
    }

    /***
     * Delete a key-value pair from etcd
     * @param key
     * @return
     */
    public CompletableFuture<Void> deleteValue(String key) {
        ByteSequence keyByte = ByteSequence.from(key, StandardCharsets.UTF_8);
        return etcdClient.getKVClient().delete(keyByte).thenAccept(deleteResponse -> {
        });
    }


    /***
     * Put a key-value pair to etcd with a lease
     * @param key
     * @param value
     * @param leaseTime
     * @param timeout
     * @return
     * @throws ExecutionException
     * @throws InterruptedException
     * @throws TimeoutException
     */
    public CompletableFuture<PutResponse> putValueWithLease(String key, String value, long leaseTime, long timeout) throws ExecutionException, InterruptedException, TimeoutException {
        ByteSequence keyByte = ByteSequence.from(key, StandardCharsets.UTF_8);
        ByteSequence valueByte = ByteSequence.from(value, StandardCharsets.UTF_8);
        Lease leaseClient = etcdClient.getLeaseClient();
        long leaseId = leaseClient.grant(leaseTime).get(timeout, TimeUnit.SECONDS).getID();
        System.out.println("Lease ID: " + leaseId);
        PutOption putOption = PutOption.newBuilder().withLeaseId(leaseId).build();
        // put value with lease
        CompletableFuture<PutResponse> putResponse = etcdClient.getKVClient().put(keyByte, valueByte, putOption);
       return putResponse;
    }




    /***
     * 续租一个已存在的租约
     * @param leaseId
     * @return
     */
    public CompletableFuture<Void> renewLease(long leaseId) {
        Lease leaseClient = etcdClient.getLeaseClient();
        return leaseClient.keepAliveOnce(leaseId).thenAccept(keepAliveResponse -> {
            if (keepAliveResponse.getTTL() == -1) {
                // lease has expired
                System.out.println("Lease has expired");
            }else {
                System.out.println("Lease is renewed");
            }
        });
    }

    /***
     * 获取租约的信息
     * @param leaseId
     * @return
     */
    public CompletableFuture<LeaseTimeToLiveResponse> getLeaseInfo(long leaseId) throws ExecutionException, InterruptedException {
        Lease leaseClient = etcdClient.getLeaseClient();
        LeaseTimeToLiveResponse lTRes = leaseClient.timeToLive(leaseId, LeaseOption.newBuilder().withAttachedKeys().build()).get();
        return CompletableFuture.completedFuture(lTRes);
    }
}

创建简单的controller 进行请求尝试

//存 
@PostMapping("/putValueWithOutLease")
    public CompletableFuture<PutResponse> putValueWithOutLease(@RequestParam String key, @RequestParam String value) {
        return etcdService.putValueWithOutLease(key, value);
    }
//取
@GetMapping("/get")
    public CompletableFuture<String> getValueWithOutLease(@RequestParam String key) {
        return etcdService.getValue(key);
    }
//删除
 @DeleteMapping("/deleteValue")
    public CompletableFuture<Void> deleteValue(@RequestParam String key) {
        return etcdService.deleteValue(key);
    }
//带租约的存储
 @PostMapping("/putValueWithLease")
    public CompletableFuture<PutResponse> putValueWithLease(
            @RequestParam String key,
            @RequestParam String value,
            @RequestParam Long leaseTime,
            @RequestParam Long timeOut
    ) throws ExecutionException, InterruptedException, TimeoutException {
        return etcdService.putValueWithLease(key, value,leaseTime, timeOut);
    }
//顺序存储
 @PutMapping("/writeWithOutLease")
    public String writeValue(@RequestParam int num) {
        logger.info("write value to etcd");
        StopWatch stopWatch = new StopWatch();
        stopWatch.start("write");

        for (int i = 0; i < num; i++) {
            etcdService.putValueWithOutLease("/test/" + i, String.valueOf(i));
        }

        stopWatch.stop();
        logger.info("write value to etcd, time: " + stopWatch.prettyPrint());
        return "success";
    }
//查看租约状态
 @PostMapping("/viewLeaseDetail")
    public CompletableFuture<LeaseTimeToLiveResponse> getLeaseInfo(@RequestParam Long leaseId) throws ExecutionException, InterruptedException {
        return etcdService.getLeaseInfo(leaseId);
    }
//续租
 @PostMapping("/renewLease")
    public CompletableFuture<Void> renewLease(@RequestParam long leaseId) throws ExecutionException, InterruptedException, TimeoutException {
        return etcdService.renewLease(leaseId);
    }

5-接口模拟验证

使用apipost 进行接口调用,查看执行情况

  • 不带租约存储
    请添加图片描述

请添加图片描述

  • 不带租约查询

请添加图片描述

  • 手动删除key

请添加图片描述

  • 删除后重新查询,返回空值

请添加图片描述

  • 带租约存储值

设置key=/acc/test val=hello etcd 租约时间为2min的数据存储

请添加图片描述

请添加图片描述

  • 查询数据信息
    请添加图片描述

demo gitee地址


http://www.kler.cn/a/473073.html

相关文章:

  • uniapp获取安卓与ios的唯一标识
  • linux音视频采集技术: v4l2
  • 【网络安全 | 漏洞挖掘】通过监控调试模式实现价值$15k的RCE
  • 【Arm】Arm 处理器的半主机(semihosting)机制
  • 【权限管理】Apache Shiro学习教程
  • 关于物联网的基础知识(二)——物联网体系结构分层
  • YCM上告警项怎么修改
  • 【转载记录】Scikit-learn 报错:’super’ object has no attribute ‘__sklearn_tags__’.
  • Perl语言的语法糖
  • 【HTML+CSS+JS+VUE】web前端教程-2-HTML5介绍和基础骨架
  • 【2024华为OD-E卷-100分-boss的收入】(题目+思路+JavaC++Python解析)
  • 【并发篇】CompletableFuture学习
  • JavaScript 正则表达式
  • 代码随想录算法训练营第1天(数组1)| 704. 二分查找、27. 移除元素、977.有序数组的平方
  • 【数据库】SQL相关知识点总结1(数据库约束、三大范式、关系模型、聚合函数)
  • 为什么页面无法正确显示?都有哪些HTML和CSS相关问题?
  • PHP语言的函数实现
  • svelte5中使用react组件
  • 跨界融合:人工智能与区块链如何重新定义数据安全?
  • MATLAB语言的软件工程
  • c#13新特性
  • 推动多语言语音科技迈向新高度:INTERSPEECH 2025 ML-SUPERB 2.0 挑战赛
  • JAVA常见问题解答
  • 【LeetCode Hot100 贪心算法】 买卖股票的最佳时机、跳跃游戏、划分字母区间
  • 【网络云SRE运维开发】2025第2周-每日【2025/01/08】小测-【第8章 STP生成树协议】理论和实操
  • 【Linux】shell脚本编程