SpringBoot+Jectd实现服务的注册、发现和选主

项目场景:

使用ETCD服务,在Java中用springboot+jetcd实现分布式管理的服务的注册、发现和选主


代码展示:

直接上干货,添加MAVEN依赖,代码复制到自己的工程里面,配置下文件即可使用。

maven

  <dependency>
  	<groupId>org.springframework.boot</groupId>
  	<artifactId>spring-boot-starter</artifactId>
  	<version>2.1.17.RELEASE</version>
  </dependency>
  
  <dependency>
 	<groupId>io.etcd</groupId>
  	<artifactId>jetcd-core</artifactId>
  	<version>0.5.7</version>
  </dependency>
  

package jetcd;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

import io.etcd.jetcd.Client;
import io.etcd.jetcd.Election;
import io.etcd.jetcd.KV;
import io.etcd.jetcd.Lease;
import io.etcd.jetcd.Lock;
import io.etcd.jetcd.Watch;

/**
 * jectd 构建与etcd的操作客户端
 * @author luzl
 *
 */
@Component
@PropertySource(value= {"file:./config/jetcd.properties"},ignoreResourceNotFound=true)
public class JetcdOperatorClient {

	@Value("${endpoints:http://127.0.0.1:2379}")
	private String endpoints;
	
	private Client client;
	
	private KV kv;
	
	private Lock lock;
	
	private Lease lease;
	
	private Watch watch;
	
	private Election election;
	
	private final String DOUHAO = ",";
	
	@PostConstruct
	private void init() {
		client=Client.builder().endpoints(endpoints.split(DOUHAO)).build();
		kv=client.getKVClient();
		lock=client.getLockClient();
		lease=client.getLeaseClient();
		watch=client.getWatchClient();
		election=client.getElectionClient();		
	}
	
	@PreDestroy
	private void destroy() {
		election.close();
		kv.close();
		lease.close();
		watch.close();
		lock.close();
		client.close();
	}

	public KV getKv() {
		return kv;
	}

	public void setKv(KV kv) {
		this.kv = kv;
	}

	public Lock getLock() {
		return lock;
	}

	public void setLock(Lock lock) {
		this.lock = lock;
	}

	public Lease getLease() {
		return lease;
	}

	public void setLease(Lease lease) {
		this.lease = lease;
	}

	public Watch getWatch() {
		return watch;
	}

	public void setWatch(Watch watch) {
		this.watch = watch;
	}

	public Election getElection() {
		return election;
	}

	public void setElection(Election election) {
		this.election = election;
	}
}

package jetcd.registry;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import io.etcd.jetcd.ByteSequence;
import io.etcd.jetcd.Election;
import io.etcd.jetcd.KV;
import io.etcd.jetcd.KeyValue;
import io.etcd.jetcd.Lease;
import io.etcd.jetcd.Watch;
import io.etcd.jetcd.Watch.Watcher;
import io.etcd.jetcd.election.LeaderResponse;
import io.etcd.jetcd.kv.DeleteResponse;
import io.etcd.jetcd.kv.GetResponse;
import io.etcd.jetcd.lease.LeaseKeepAliveResponse;
import io.etcd.jetcd.options.DeleteOption;
import io.etcd.jetcd.options.GetOption;
import io.etcd.jetcd.options.PutOption;
import io.etcd.jetcd.options.WatchOption;
import io.etcd.jetcd.watch.WatchResponse;
import io.grpc.stub.StreamObserver;
import io.netty.util.internal.StringUtil;
import jetcd.JetcdOperatorClient;

/**
 * 注册管理中心
 * @author luzl
 *
 */
@Service
public class EtcdRegistryCenter {

	@Autowired
	private  JetcdOperatorClient jetcdOperatorClient; 
	
	@Value("${root:root}")
	private String ETCD_REGISTRY_DIR;
	@Value("${namespace:namespace}")
	private String NAMESPACE;
	@Value("${group:group}")
	private String GROUP;
	@Value("${server_name:serverName}")
	private String SERVER_NAME;
	
	public String SERVER_REGISTRY_PATH; 
	public String GROUP_REGISTRY_PATH; 
	
	private String XIE_XIAN="/";
	
	@PostConstruct
	private void init() {
		GROUP_REGISTRY_PATH=ETCD_REGISTRY_DIR+XIE_XIAN+NAMESPACE+XIE_XIAN+GROUP+XIE_XIAN;
		SERVER_REGISTRY_PATH=GROUP_REGISTRY_PATH+SERVER_NAME;
		//服务注册
		registerInstance(SERVER_REGISTRY_PATH, "127.0.0.1:2379", null);
		
		//服务监听
		watchInstance(GROUP_REGISTRY_PATH, onNext->{
			onNext.getEvents().forEach(action->{
				switch (action.getEventType()) {
				case PUT:{
					System.out.println("服务注册"+action.getKeyValue().getKey().toString(StandardCharsets.UTF_8));					
				}
					break;
				case DELETE:{
					System.out.println("服务注销"+action.getKeyValue().getKey().toString(StandardCharsets.UTF_8));	
				}
					break;
				default:
					break;
				}
			});
		});
	}
	
	@PreDestroy
	private void destroy() {
		deregisterInstance(SERVER_REGISTRY_PATH);
	}
	
	/***
	 * 
	 * @param serverNamePath 服务记录的KEY
	 * @param metadata 需要保留的元数据
	 * @param electionName 选举范围定义
	 */
	public void registerInstance(String serverNamePath,String metadata,String electionName) {
		ByteSequence servreKey=ByteSequence.from(serverNamePath, StandardCharsets.UTF_8);
		ByteSequence metadataValue=ByteSequence.from(metadata, StandardCharsets.UTF_8);
		ByteSequence electionNameKey=ByteSequence.from(electionName, StandardCharsets.UTF_8);
		Lease lease=jetcdOperatorClient.getLease();		
		long leaseId=0;
		try {
			//租约时间
			leaseId = lease.grant(60,60,TimeUnit.SECONDS).get().getID();
		} catch (InterruptedException | ExecutionException e) {
			e.printStackTrace();
		}
		StreamObserver<LeaseKeepAliveResponse> observer=new StreamObserver<LeaseKeepAliveResponse>() {	
			@Override
			public void onNext(LeaseKeepAliveResponse arg0) {
				System.out.println("心跳");
			}
			@Override
			public void onError(Throwable arg0) {	
			}
			@Override
			public void onCompleted() {		
			}
		};
		//续约
		lease.keepAlive(leaseId, observer);
		KV kv=jetcdOperatorClient.getKv();
		//绑定租约
		PutOption option=PutOption.newBuilder().withLeaseId(leaseId).build();
		//注册
		kv.put(servreKey, metadataValue, option);
		//参与选举
		if(!StringUtil.isNullOrEmpty(electionName)) {
			Election election=jetcdOperatorClient.getElection();
			election.campaign(electionNameKey, leaseId, servreKey);
		}
	}
	
	/**
	 * 服务注销
	 * @param serverNamePath
	 */
	public void deregisterInstance(String serverNamePath) {
		ByteSequence servreKey=ByteSequence.from(serverNamePath, StandardCharsets.UTF_8);
		KV kv=jetcdOperatorClient.getKv();
		Lease lease=jetcdOperatorClient.getLease();
		DeleteOption option=DeleteOption.newBuilder().withPrevKV(true).build();
		CompletableFuture<DeleteResponse> deleteFuture = kv.delete(servreKey, option);
		try {
			long leaseId= deleteFuture.get().getPrevKvs().get(0).getLease();
			lease.revoke(leaseId);
		} catch (InterruptedException | ExecutionException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 获取分组下所有实例
	 * @param groupRegistryPath
	 * @return
	 */
	public List<KeyValue> getGroupAllInstance(String groupRegistryPath){
		KV kv=jetcdOperatorClient.getKv();
		ByteSequence groupKey=ByteSequence.from(groupRegistryPath, StandardCharsets.UTF_8);
		GetOption option=GetOption.newBuilder().withPrefix(groupKey).build();
		CompletableFuture<GetResponse> getfuture = kv.get(groupKey, option);
		List<KeyValue> listInstance=new ArrayList<KeyValue>();
		try {
			listInstance= getfuture.get().getKvs();
		} catch (InterruptedException | ExecutionException e) {
			e.printStackTrace();
		}
		return listInstance;
	}
	
	/**
	 * 获取注册实例
	 * @param serverPath
	 * @return
	 */
	public KeyValue getInstance(String serverPath) {
		KV kv=jetcdOperatorClient.getKv();
		ByteSequence servreKey=ByteSequence.from(serverPath, StandardCharsets.UTF_8);
		CompletableFuture<GetResponse> getfuture = kv.get(servreKey);
		KeyValue keyValue=null;
		try {
			List<KeyValue> listInstance= getfuture.get().getKvs();
			keyValue=listInstance.size()>0? listInstance.get(0):null;
		} catch (InterruptedException | ExecutionException e) {
			e.printStackTrace();
		}	
		return keyValue;
	}
	
	/**
	 * 注册监听
	 * @param watchPath
	 * @param onNext
	 * @return
	 */
	public Watcher watchInstance(String watchPath,Consumer<WatchResponse> onNext) {
		ByteSequence watchKey=ByteSequence.from(watchPath, StandardCharsets.UTF_8);
		Watch watch=jetcdOperatorClient.getWatch();
		WatchOption option=WatchOption.newBuilder().withPrefix(watchKey).build();
		return watch.watch(watchKey, option, onNext);	
	}
	
	/**
	 * 获取领导节点信息
	 * @param electionName
	 * @return
	 */
	public KeyValue getLeader(String electionName) {
		ByteSequence electionKey=ByteSequence.from(electionName, StandardCharsets.UTF_8);
		Election election=jetcdOperatorClient.getElection();
		 CompletableFuture<LeaderResponse> leadFuture = election.leader(electionKey);
		KeyValue keyValue=null;
		try {
			keyValue=leadFuture.get().getKv();
		} catch (InterruptedException | ExecutionException e) {
			e.printStackTrace();
		}
		return keyValue;
	}
}


后续的发布到gitee:https://gitee.com/luzl1991/etcd-use?_from=gitee_search

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值