Zookeeper作业题--实现简易版配置中心

需求

基于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

运行结果

 

posted @ 2021-07-01 09:07  咕噜_咕噜  阅读(263)  评论(0编辑  收藏  举报