需求
基于Zookeeper实现简易版配置中心要求实现以下功能:
1. 创建一个Web项目,将数据库连接信息交给Zookeeper配置中心管理,即:当项目Web项目启动时,从Zookeeper进行MySQL配置参数的拉取
2. 要求项目通过数据库连接池访问MySQL(连接池可以自由选择熟悉的)
3. 当Zookeeper配置信息变化后Web项目自动感知,正确释放之前连接池,创建新的连接池
需求分析
1.启动SpringBoot项目,启动时,从Zookeeper拉取配置信息
2.获取配置,创建对应的数据库连接池,访问mysql
3.注册监听Zookeeper对应节点数据变化,发生变化时,释放连接池 或 创建新的连接池。重复2的步骤。
实现代码
1.maven配置
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </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> <dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>3.4.5</version> <scope>compile</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.11</version> </dependency> <!-- zookeeper --> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.14</version> </dependency> <dependency> <groupId>com.101tec</groupId> <artifactId>zkclient</artifactId> <version>0.2</version> </dependency> </dependencies>
2.SpringBoot代码
package com.donaldy.zkpractice; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import lombok.Data; import lombok.ToString; import org.I0Itec.zkclient.IZkDataListener; import org.I0Itec.zkclient.ZkClient; import org.I0Itec.zkclient.exception.ZkMarshallingError; import org.I0Itec.zkclient.serialize.ZkSerializer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @SpringBootApplication public class Application { private static HikariDataSource hikariDataSource; private static ZkClient zkClient; private static ObjectMapper mapper = new ObjectMapper(); /** * 启动服务 * * 1. 启动 web 容器 * 2. 初始化 zookeeper * 3. 配置数据库连接池 * * @param args 参数 */ public static void main(String[] args) { SpringApplication.run(Application.class, args); initZk(); configHikariSource(); } private static void initZk() { zkClient = new ZkClient("172.16.64.121:2181"); zkClient.setZkSerializer(new ZkStrSerializer()); zkClient.subscribeDataChanges("/jdbc", new IZkDataListener() { public void handleDataChange(String path, Object data) { System.out.println(path + " data is changed, new data " + data); hikariDataSource.close(); configHikariSource(); } public void handleDataDeleted(String path) { System.out.println(path + " is deleted!!"); hikariDataSource.close(); } }); } /** * 配置数据库连接池 * * 1. 从 zookeeper 中获取配置信息 * 2. 更新 hikari 配置 * 3. 执行测试 sql */ private static void configHikariSource(){ JDBCConfig myConfig = getJDBCConfig(); updateHikariConfig(myConfig); try { executeTestSQL(); } catch (SQLException e) { e.printStackTrace(); } } private static void executeTestSQL() throws SQLException { Connection connection = hikariDataSource.getConnection(); PreparedStatement pst = connection.prepareStatement( "SELECT id, username FROM user;" ); ResultSet rs = pst.executeQuery(); while (rs.next()) { System.out.println("id : " + rs.getString(1) + " , username : " + rs.getString(2)); } } private static void updateHikariConfig(JDBCConfig myConfig) { HikariConfig config = new HikariConfig(); config.setJdbcUrl(myConfig.getUrl()); config.setUsername(myConfig.getUsername()); config.setPassword(myConfig.getPassword()); config.addDataSourceProperty( "driverClassName" , myConfig.getDriver()); config.addDataSourceProperty( "cachePrepStmts" , "true" ); config.addDataSourceProperty( "prepStmtCacheSize" , "250" ); config.addDataSourceProperty( "prepStmtCacheSqlLimit" , "2048" ); hikariDataSource = new HikariDataSource(config); } private static JDBCConfig getJDBCConfig() { Object data = zkClient.readData("/jdbc"); try { JDBCConfig myConfig = mapper.readValue(data.toString(), JDBCConfig.class); System.out.println(myConfig.toString()); return myConfig; } catch (JsonProcessingException e) { return new JDBCConfig(); } } } class ZkStrSerializer implements ZkSerializer { @Override public byte[] serialize(Object o) throws ZkMarshallingError { return String.valueOf(o).getBytes(); } @Override public Object deserialize(byte[] bytes) throws ZkMarshallingError { return new String(bytes); } } @Data @ToString class JDBCConfig { private String url; private String driver = "com.mysql.jdbc.Driver"; private String username; private String password; }
结果展示
1.在zk中创建节点
create /jdbc {"url":"jdbc:mysql://localhost:3306/test?useUnicode=true&useSSL=false","username":"root","password":"root","driver":"com.mysql.jdbc.Driver"}
2.更改节点中的数据
# 驱动:改变为 com.mysql.cj.jdbc.Driver # 因为 mysql 版本使用 8.0 set /jdbc {"url":"jdbc:mysql://localhost:3306/test?useUnicode=true&useSSL=false","username":"root","password":"root","driver":"com.mysql.cj.jdbc.Driver"}
3.删除数据
delete /jdbc
运行结果