一、导入依赖包
使用Curator客户端连接ZK
<!-- curator ZK 客户端 -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.2.0</version>
</dependency>
二、配置文件
在resources目录下薪资zookeeper.properties 文件
# zk host地址
zk.host=192.168.xxx.xxx:2181
# zk自增存储node
zk.sequence-path=/carpxt/sequence/
三、枚举封装
/**
* <p>用于定义通过Zk生成自增ID的枚举,</p>
*
* @date: 2021/6/10 13:55
*/
public enum ZkSequenceEnum {
T_USER,T_ORG,T_APP,T_FALLOW,T_ATTENTION
}
四、序列封装
/**
* <p>用于封装在程序运行时每个表对应的自增器,主要采用分布式原子自增类 `DistributedAtomicLong`</p>
* <p>每500ms重试3次之后还是生成失败 就返回null</p>
* @date 2021/6/10 13:55
* @version: 1.0
*/
public class ZkSequence {
//每500ms重试3次
RetryPolicy retryPolicy = new ExponentialBackoffRetry(500, 3);
DistributedAtomicLong distAtomicLong;
public ZkSequence(String sequenceName, CuratorFramework client){
distAtomicLong = new DistributedAtomicLong(client,sequenceName,retryPolicy);
}
/**
* 生成序列
* @return:
* @throws Exception
*/
public Long sequence() throws Exception{
AtomicValue<Long> sequence = this.distAtomicLong.increment();
if(sequence.succeeded()){
return sequence.postValue();
}else{
return null;
}
}
}
五、Client封装
@Data
@Slf4j
public class ZookeeperClient {
private String host;
private String sequencePath;
// 重试休眠时间
private final int SLEEP_TIME_MS = 1000;
// 最大重试1000次
private final int MAX_RETRIES = 1000;
//会话超时时间
private final int SESSION_TIMEOUT = 30 * 1000;
//连接超时时间
private final int CONNECTION_TIMEOUT = 3 * 1000;
//创建连接实例
private CuratorFramework client = null;
// 序列化集合
private Map<String, ZkSequence> zkSequenceMap = Maps.newConcurrentMap();
public ZookeeperClient(String host,String sequencePath){
this.host = host;
this.sequencePath = sequencePath;
}
// 通过PostConstruct注解在内构器之后调用init方法初始化客户端连接
@PostConstruct
public void init() throws Exception{
this.client = CuratorFrameworkFactory.builder()
.connectString(this.getHost())
.connectionTimeoutMs(CONNECTION_TIMEOUT)
.sessionTimeoutMs(SESSION_TIMEOUT)
.retryPolicy(new ExponentialBackoffRetry(SLEEP_TIME_MS,
MAX_RETRIES)).build();
this.client.start();
//初始化 自定义的 ZkSequence
this.initZkSequence();
}
public void initZkSequence(){
ZkSequenceEnum[] list = ZkSequenceEnum.values();
for (int i = 0; i < list.length; i++) {
String name = list[i].name();
String path = this.sequencePath+name;
ZkSequence seq = new ZkSequence(path,this.client);
zkSequenceMap.put(name,seq);
}
}
/**
* 生成SEQ
* @param name
* @return
* @throws Exception
*/
public Long sequence(ZkSequenceEnum name){
try {
ZkSequence seq = zkSequenceMap.get(name.name());
if (seq != null) {
return seq.sequence();
}
}catch (Exception e){
log.error("获取[{}]Sequence错误:{}",name,e);
}
return null;
}
}
六、配置封装
@Getter
@Setter
@Configuration
@ConfigurationProperties(prefix = "zk")
@PropertySource("classpath:zookeeper.properties")
public class ZkConfig {
String host;
String sequencePath;
@Bean
public ZookeeperClient zookeeperClient(){
return new ZookeeperClient(this.host,this.sequencePath);
}
}
七、调用Sequences封装
/**
* <p>统一暴露生成自增主键的功能</p>
* @author tianjie
* @date: 2021/6/10 14:30
*/
@Component
public class Sequences {
@Autowired
private ZookeeperClient client;
public Long sequenceUser() {
return this.client.sequence(ZkSequenceEnum.T_USER);
}
public Long sequenceOrg() {
return this.client.sequence(ZkSequenceEnum.T_ORG);
}
public Long sequenceAPP() {
return this.client.sequence(ZkSequenceEnum.T_APP);
}
public Long sequenceFallow() {
return this.client.sequence(ZkSequenceEnum.T_FALLOW);
}
public Long sequenceAttention() {
return this.client.sequence(ZkSequenceEnum.T_ATTENTION);
}
}
八、使用
1、包扫描配置类
@Configuration
@ComponentScan("com.carpxt.common.zookeeper")
public class ZookeeperConfig {
}
2、注入Sequences
@Autowired
private Sequences sequences;
3、方法中调用
xxx.setId(sequences.sequenceUser());
如后期需要新增ZkSequence自增表,可参考以下操作步骤,快速实现: 在ZkSequenceEnum中定义对应的枚举项,规范要求枚举项与物理表名一致且大写 在Sequences中定义对应的调用方法,规范要求方法由sequence前缀+驼峰表名组成