业务场景
生产车间的包装打印物流线上,获得流水号,作为条码数据的组成部分。 取号的终端可能小及数十几个,并发量不大,但要求稳定性,不得停服。
解决办法
部署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()