etcd做高可用流水号服务(java)

102 阅读1分钟

业务场景

生产车间的包装打印物流线上,获得流水号,作为条码数据的组成部分。 取号的终端可能小及数十几个,并发量不大,但要求稳定性,不得停服。

解决办法

部署3或5个节点ETCD,采用锁的机制,获得唯一流水号。

java版实现方案

依赖(采用jetcd)

implementation "io.etcd:jetcd-core:0.7.7"

配置文件

etcd: 
  endpoints: http://x.x.x.1:2379,http://x.x.x.2:2379,http://x.x.x.3:2379

实现类


@Slf4j
@Configuration
@Component
@Data
public class JetcdClient {
    @Value("${etcd.endpoints:127.0.0.1:2379}")
    private String endpoints;

    private Client client;

    private KV kvClient;

    private Lock lockClient;

    private Lease leaseClient;
//    private Watch watchClient;
//    private Election electionClient;
    @PostConstruct
    private void init() {
        log.info("init jetcd:{}", endpoints);
        if (endpoints.startsWith("http:")){
            client=Client.builder().endpoints(endpoints.split(",")).build();
        }else if(endpoints.startsWith("ip:///")){ //此格式未经验证
            client=Client.builder().endpoints(endpoints).build();
        }else {
            client=Client.builder().endpoints("ip:///" + endpoints).build();
        }
        kvClient=client.getKVClient();
        lockClient=client.getLockClient();
        leaseClient=client.getLeaseClient();
    }

    @PreDestroy
    private void destroy() {
        kvClient.close();
        leaseClient.close();
        lockClient.close();
        client.close();
    }


    String getSerialNum(String key) throws ExecutionException, InterruptedException {
        CompletableFuture<LeaseGrantResponse> grant = leaseClient.grant(30);
        long leaseId = grant.get().getID();
        CompletableFuture<LockResponse> lockResp = lockClient.lock(ByteSequence.from(key.getBytes()), leaseId);
        assert !lockResp.get().getKey().isEmpty();
        CompletableFuture<GetResponse> kresp = kvClient.get(ByteSequence.from(key.getBytes()));
        if (kresp.get().getKvs().size() == 0){
            //init 1
            CompletableFuture<PutResponse> putResp = kvClient.put(ByteSequence.from(key.getBytes()), ByteSequence.from("1".getBytes()));
            if (putResp.isDone()){
                return "1";
            }
            return "0"; //or throw Exception
        }else {
            KeyValue keyValue = kresp.get().getKvs().get(0);
            int nextValue = Integer.parseInt(keyValue.getValue().toString()) + 1;
            CompletableFuture<PutResponse> putResp = kvClient.put(ByteSequence.from(key.getBytes()), ByteSequence.from(String.valueOf(nextValue).getBytes()));
            if (putResp.isDone()){
                return String.valueOf(nextValue);
            }
            return "0";
        }
    }
}

其它

若在put时获得前值,可在put方法中增加参数PutOption(支持链式调用)

PutOption.builder().withPrevKV().build()