三丰 soft张三丰 java
zookeeper生成分布式自增IDnode
zookeeper: 3.6.0 windows springboot 2.2.6 jdk 11
<properties> <curator.version>4.2.0</curator.version> </properties> <!-- curator ZK 客户端 --> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>${curator.version}</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>${curator.version}</version> </dependency>
完整的pom.xml文件以下web
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.6.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>zookeeper</artifactId> <version>0.0.1-SNAPSHOT</version> <name>zookeeper</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <curator.version>4.2.0</curator.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <!-- curator ZK 客户端 --> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>${curator.version}</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>${curator.version}</version> </dependency> <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.10</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
配置文件为\src\main\resources\zookeeper.properties,存储内容以下:spring
# zk host地址 zk.host=127.0.0.1:2181 # zk自增存储node zk.sequence-path=/news/sequence/
建立com.example.zookeeper.sequence.ZkSequenceEnum文件,用于定义经过Zk生成自增ID的枚举,在项目中规范要求与物理表名项目,使用与当前项目阶段的枚举以下:数据库
public enum ZkSequenceEnum { AP_LIKES, AP_READ_BEHAVIOR, AP_COLLECTION, AP_USER_FOLLOW, AP_USER_FAN }
建立com.example.zookeeper.sequence.ZkSequence文件,用于封装程序在运行时每一个表对应的自增器,这里主要经过分布式原子自增类(DistributedAtomicLong)实现,注意每500毫秒重试3次后仍然生成失败则返回null,由上层处理,相关实现代码以下:apache
public class ZkSequence { 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; } } }
建立com.example.zookeeper.client.ZookeeperClient类,经过PostConstruct注解在内构器以后调用init方法初始化客户端链接,并调用initZkSequence方法初始项目所定义的ZkSequence,并存储在zkSequence的Map集合中,最终提供sequence方法来查询对应zkSequence获取自增ID,相关实现代码以下:windows
@Data public class ZookeeperClient { private static Logger logger = LoggerFactory.getLogger(ZookeeperClient.class); 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> zkSequence = Maps.newConcurrentMap(); public ZookeeperClient(String host, String sequencePath) { this.host = host; this.sequencePath = sequencePath; } @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(); 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); zkSequence.put(name, seq); } } /** * 生成SEQ * * @param name * @return * @throws Exception */ public Long sequence(ZkSequenceEnum name) { try { ZkSequence seq = zkSequence.get(name.name()); if (seq != null) { return seq.sequence(); } } catch (Exception e) { logger.error("获取[{}]Sequence错误:{}", name, e); } return null; } }
注:在这里ZookeeperClient是一个BeanFactory,ZkSequence是一个FactoryBean。springboot
建立com.example.zookeeper.config.ZkConfig类,用于自动化配置环境文件的导入,和zkClient定义Bean定义,其相关的实现代码以下:session
/** * 自动化配置核心数据库的链接配置 */ @Data @Configuration @ConfigurationProperties(prefix="zk") @PropertySource("classpath:zookeeper.properties") public class ZkConfig { String host; String sequencePath; /** * 这是最快的数据库链接池 * @return */ @Bean public ZookeeperClient zookeeperClient(){ return new ZookeeperClient(this.host,this.sequencePath); } }
为便于程序中调用,以及对自增生成失败的统一处理,项目中规范经过com.example.zookeeper.sequence.Sequences类统一暴露生成自增主键的功能,相关代码以下:maven
@Component public class Sequences { @Autowired private ZookeeperClient client; public Long sequenceApLikes() { return this.client.sequence(ZkSequenceEnum.AP_LIKES); } public Long sequenceApReadBehavior() { return this.client.sequence(ZkSequenceEnum.AP_READ_BEHAVIOR); } public Long sequenceApCollection() { return this.client.sequence(ZkSequenceEnum.AP_COLLECTION); } public Long sequenceApUserFollow() { return this.client.sequence(ZkSequenceEnum.AP_USER_FOLLOW); } public Long sequenceApUserFan() { return this.client.sequence(ZkSequenceEnum.AP_USER_FAN); } }
@SpringBootTest class ZookeeperApplicationTests { // 第一步,注入Sequences @Autowired private Sequences sequences; @Test void contextLoads() { for (int i = 0; i < 10; i++) { System.out.println("sequenceApCollection生成的自增id为:" + sequences.sequenceApCollection()); } } }
如后期须要新增ZkSequence自增表,可参考如下操做步骤,快速实现:
在`ZkSequenceEnum中定义对应的枚举项,规范要求枚举项与物理表名一致且大写在Sequences中定义对应的调用方法,规范要求方法由sequence前缀+驼峰表名组成