Zookeeper分布式唯一主键id生成

一、导入依赖包

使用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前缀+驼峰表名组成

原文地址:https://www.cnblogs.com/kt-ting/p/14871587.html