• SpringBoot整合Nacos实现动态配置数据源


    1 Nacos说明

      是不是还有好多小伙伴不知道 nacos 是啥?nacos 是阿里巴巴的一个开源项目,官网给它的定义是:

    一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。

      SpringBoot/SpringCloud项目部署运行后,如果使用硬编码方式定义数据源,那么如果需要更换数据库,就只能通过更改源码并重启的方式来达成目的,而 nacos 配置中心这一组件,我们可以将数据源连接

    属性编写在配置中心中,需要修改连接属性就可以从配置中心中修改并发布,这样就可以热修改数据源位置无需重启服务。

      其实,目前 nacos 这个组件应用很广泛,很多项目都用它来做配置中心和注册中心,今天分享的内容就是 nacos 作为配置中心使用的。

      在做之前,我查了好多示例和博客,但是都没有找到符合我需求的,所以走了好多弯路,才让这个项目完整的跑起来,现在我们就来看下如何实现 SpringBoot + Nacos + Druid 动态获取数据库配置信息。

      今天的内容比较多,如果你恰好在学习 nacos,认真看完,肯定会有收获。

      官网地址:官网地址

      官网文档地址:官网文档地址

      Gitee地址:Springboot-nacos

    1.1 Nacos特性

      Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。

      Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。

      1、服务发现和服务健康监测

      2、动态配置服务

      3、动态 DNS 服务

      4、服务及其元数据管理

      等等。。。。

    1.2 Nacos相关页面展示(登录账号密码:nacos nacos)

    2 下载安装并启动Nacos服务

    2.1 下载Nacos

    进入官网,点击版本前往 github,你需要去 nacos 的 GitHub 发布列表中下载,不清楚的小伙伴直接访问下面的网址进入下载页选择适合的版本:GitHub下载地址

    最新版本是 2.0.4,目前最新的稳定版本是1.4.3

    点击Assets,选择 zip 文件,然后应该就开始下载了,下载可能比较慢

    如果 GitHub 打不开,可以从下面地址下载

      链接: https://pan.baidu.com/s/1bFLQ-2jnDg8Mm_bmMpePqw

      提取码: wbsu

    2.2 Windows启动关闭Nacos服务

    2.2.1 zip 文件结构是这样的,直接解压就行,也不需要配置环境变量

    2.2.2 启动Nacos服务,进入 bin 目录下直接双击 startup.cmd,窗口不要关闭,关闭服务也就停止了

     

    2.2.3 关闭Nacos服务,双击 shutdown.cmd 或者 直接关闭启动的 cmd 窗口

    2.3 修改Nacos服务端配置信息(可不修改)

    2.4 访问Nacos服务

    如果没有修改 nacos 的配置,直接浏览器打开如下地址即可,如果修改了端口,需要把端口改成你修改的端口:http://127.0.0.1:8848/nacos

     

    默认用户名和密码都是 nacos,登陆成功之后就可以发布你的配置信息了。

    2.5 启动后是没有配置的,点击 + 号发布配置信息

    输入Data ID,必须唯一,类似于主键,不能重复;选择配置格式,这里我选择YAML(内容如下);然后点击发布,一个配置信息就发布成功了

    # 数据源配置
    spring:
        datasource:
            type: com.alibaba.druid.pool.DruidDataSource
            driverClassName: com.mysql.cj.jdbc.Driver
            druid:
                # 主库数据源
                master:
                    url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
                    username: root
                    password: root
                # 从库数据源
                slave:
                    # 从数据源开关/默认关闭
                    enabled: false
                    url:
                    username:
                    password:
                # 初始连接数
                initialSize: 5
                # 最小连接池数量
                minIdle: 10
                # 最大连接池数量
                maxActive: 20
                # 配置获取连接等待超时的时间
                maxWait: 60000
                # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
                timeBetweenEvictionRunsMillis: 60000
                # 配置一个连接在池中最小生存的时间,单位是毫秒
                minEvictableIdleTimeMillis: 300000
                # 配置一个连接在池中最大生存的时间,单位是毫秒
                maxEvictableIdleTimeMillis: 900000
                # 配置检测连接是否有效
                validationQuery: SELECT 1 FROM DUAL
                testWhileIdle: true
                testOnBorrow: false
                testOnReturn: false
                webStatFilter:
                    enabled: true
                statViewServlet:
                    enabled: true
                    # 设置白名单,不填则允许所有访问
                    allow:
                    url-pattern: /druid/*
                    # 控制台管理用户名和密码
                    login-username: admin
                    login-password: admin123
                filter:
                    stat:
                        enabled: true
                        # 慢SQL记录
                        log-slow-sql: true
                        slow-sql-millis: 1000
                        merge-sql: true
                    wall:
                        config:
                            multi-statement-allow: true

    3 创建Springboot-nacos项目

    按下图步骤,我是创建了一个父子工程,创建了普通的maven项目,在项目下创建不同的springboot项目,也可以直接创建spring boot项目。

    项目结构:

    3.1 创建springboot项目

    3.2 导入需要的依赖(spring-boot-start-parent版本过高可能会导致启动失败)

    <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.3.8.RELEASE</version>
    </parent>
    <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <!--nacos配置-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
                <version>2.2.6.RELEASE</version>
            </dependency>
            <!--mysql驱动-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <scope>runtime</scope>
            </dependency>
            <!--jdbc 数据库连接-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
            </dependency>
            <!-- 引入阿里数据库连接池 -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.1.6</version>
            </dependency>
    </dependencies>

    3.3 resources目录下的application.yml内容注释掉

    注意:需要把 application.yml 中的内容注释掉或者删除就可以

    #server:
    #  port: 8091
    ## 数据源配置
    #spring:
    #  datasource:
    #    type: com.alibaba.druid.pool.DruidDataSource
    #    driverClassName: com.mysql.cj.jdbc.Driver
    #    druid:
    #      # 主库数据源
    #      master:
    #        url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
    #        username: root
    #        password: root
    #      # 从库数据源
    #      slave:
    #        # 从数据源开关/默认关闭
    #        enabled: false
    #        url:
    #        username:
    #        password:
    #   。。。。。。。
    #      filter:
    #        stat:
    #          enabled: true
    #          # 慢SQL记录
    #          log-slow-sql: true
    #          slow-sql-millis: 1000
    #          merge-sql: true
    #        wall:
    #          config:
    #            multi-statement-allow: true

    3.4 在resources目录下创建bootstrap.yml文件

    spring:
      application:
        name: springboot-nacos
      profiles:
        active: dev
      cloud:
        nacos:
          config:
            # 配置中心的地址
            server-addr: 127.0.0.1:8848
            # 配置文件prefix
            prefix: ${spring.application.name}
            # 配置文件的格式
            file-extension: yaml
            # 配置文件的环境
            group: DEFAULT_GROUP
            # 命名空间
            namespace:
    server:
      port: 8090

    注意:
      只能是 bootstrap.yml 文件,application 文件是整合不上的。 bootstrap.yml 先于 application.yml 加载

    bootstrap.yml(bootstrap.properties)用来在程序引导时执行,应用于更加早期配置信息读取,如可以使用来配置application.yml中使用到参数等

    application.yml(application.properties) 应用程序特有配置信息,可以用来配置后续各个模块中需使用的公共参数等。

    配置文件 prefix 是 nacos 服务器中 Data-ID 的前缀,若无此项配置,默认{spring.application.name}。项目启动时根据此配置文件拼接 nacos 配置中心的 Data-ID 的名称来查找配置文件

    1. 拼接规则标准:${prefix}-${spring.profile.active}.${file-extension}

    2. 如果 spring.cloud.nacos.config.prefix 省略的拼接规则:

    {spring.application.name}-${spring.profile.active}.${file-extension}

    建议: 以上配置配完整,不要省略,符合规范

    3.5 自定义DruidDatasouceWrapper

    DruidDataSourceWrapper 要继承 DruidDataSource

    RefreshScope 的作用是实现配置、实例热加载,也就是我们重写修改配置信息后,Spring 会销毁当前类的实例,然后重新创建一个新的实例放到容器中,也是实现数据源配置实时更新的关键

    注意:黄色代码根据自己在 nacos 配置修改

    package com.liyh.druid;
    
    import com.alibaba.druid.pool.DruidDataSource;
    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.cloud.context.config.annotation.RefreshScope;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    @RefreshScope
    public class DruidDataSourceWrapper extends DruidDataSource implements InitializingBean {
        @Value("${spring.datasource.druid.master.url}")
        private String url;
        @Value("${spring.datasource.druid.master.username}")
        private String username;
        @Value("${spring.datasource.druid.master.password}")
        private String password;
        @Value("${spring.datasource.driver-class-name}")
        private String driverClassName;
    
        private String passwordCallbackClassName;
    
        public void setMaxWait(int maxWait) {
            this.maxWait = maxWait;
        }
    
        public void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
            this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
        }
    
        public void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
            this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
        }
    
        @Override
        public void setUrl(String url) {
            this.url = url;
        }
    
        @Override
        public void setDriverClassName(String driverClassName) {
            this.driverClassName = driverClassName;
        }
    
        @Override
        public void setPasswordCallbackClassName(String passwordCallbackClassName) {
            this.passwordCallbackClassName = passwordCallbackClassName;
        }
    
        @Override
        public void afterPropertiesSet() throws Exception {
            // 如果未找到前缀“spring.datasource.druid”JDBC属性,将使用“Spring.DataSource”前缀JDBC属性。
            super.setUrl(url);
            super.setUsername(username);
            super.setPassword(password);
            super.setDriverClassName(driverClassName);
            super.setInitialSize(initialSize);
            super.setMinIdle(minIdle);
            super.setMaxActive(maxActive);
            super.setMaxWait(maxWait);
            super.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
            super.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
            super.setValidationQuery(validationQuery);
            super.setTestWhileIdle(testWhileIdle);
            super.setTestOnBorrow(testOnBorrow);
            super.setTestOnReturn(testOnReturn);
            super.setPoolPreparedStatements(poolPreparedStatements);
            super.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
            super.setDbType(dbType);
            super.setPasswordCallbackClassName(passwordCallbackClassName);
        }
    }

    3.6 创建配置文件

    这里关键的配置就一个,EnableAutoConfiguration 启动 SpringBoot 的自动自动配置,下面的方法是在初始化的时候,创建数据源实例,同样也启用了热加载

    package com.liyh.config;
    
    import com.alibaba.druid.pool.DruidDataSource;
    import com.liyh.druid.DruidDataSourceWrapper;
    import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.cloud.context.config.annotation.RefreshScope;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @EnableAutoConfiguration
    @Configuration
    public class NacosConfigConfiguration {
    
        @Bean(initMethod = "init")
        @ConditionalOnMissingBean
        @RefreshScope
        public DruidDataSource dataSource() {
            return new DruidDataSourceWrapper();
        }
    }

    3.7 创建测试数据库和用户表

    创建两个MySQL 数据库 test 和 test2,并导入用户表,不同的用户名方便测试

    SET NAMES utf8mb4;
    SET FOREIGN_KEY_CHECKS = 0;
    
    -- ----------------------------
    -- Table structure for sys_user
    -- ----------------------------
    DROP TABLE IF EXISTS `sys_user`;
    CREATE TABLE `sys_user`  (
      `user_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
      `user_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户账号',
      `nick_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户昵称',
      PRIMARY KEY (`user_id`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户信息表' ROW_FORMAT = DYNAMIC;
    
    -- ----------------------------
    -- Records of sys_user
    -- ----------------------------
    INSERT INTO `sys_user` VALUES (1, 'admin', '超级管理员');
    
    SET FOREIGN_KEY_CHECKS = 1;
    SET NAMES utf8mb4;
    SET FOREIGN_KEY_CHECKS = 0;
    
    -- ----------------------------
    -- Table structure for sys_user
    -- ----------------------------
    DROP TABLE IF EXISTS `sys_user`;
    CREATE TABLE `sys_user`  (
      `user_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
      `user_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户账号',
      `nick_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户昵称',
      PRIMARY KEY (`user_id`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户信息表' ROW_FORMAT = DYNAMIC;
    
    -- ----------------------------
    -- Records of sys_user
    -- ----------------------------
    INSERT INTO `sys_user` VALUES (1, 'nacos', '嘻嘻哈哈');
    
    SET FOREIGN_KEY_CHECKS = 1;

     

    3.8 启动项目

    如上日志信息,我们可以看到 SpringBoot 项目启动成功,成功读取到了 nacos 中的配置文件

    4 创建测试接口

    4.1 创建 TestController 接口

    package com.liyh.controller;
    
    import com.liyh.druid.DruidDataSourceWrapper;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.cloud.context.config.annotation.RefreshScope;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping("nacos")
    @RefreshScope
    public class TestController {
        @Autowired
        private DruidDataSourceWrapper dataSourceWrapper;
    
        @Value(value = "${spring.datasource.druid.master.url}")
        private String useLocalCache;
    
    }

    4.2 创建测试接口读取数据源配置信息

       @GetMapping("/get")
        @ResponseBody
        public String get() {
            System.out.println("url: " + useLocalCache);
            return useLocalCache;
        }

    4.2.1 重启项目测试接口

    4.2.2 修改Nacos配置文件,修改数据库

     

     发布成功后,无需重启项目会读取最新配置

    4.2.3 重新调取接口,可以获取信息数据库信息

     

    4.3 创建测试接口读取数据库用户信息

        @GetMapping("/query")
        public String testDruid() throws SQLException {
            DruidPooledConnection connection = dataSourceWrapper.getConnection();
            Statement statement = connection.createStatement();
            ResultSet resultSet = statement.executeQuery("SELECT * from sys_user where user_id = 1;");
            String result = "没有此用户!!!";
            while (resultSet.next()) {
                result =  resultSet.getString("user_name") + " : " + resultSet.getString("nick_name");
                System.out.println(result);
                return result;
            }
            return result;
        }

    4.3.1 重启项目测试接口

     

    4.3.2 修改Nacos配置文件,修改数据库

     

    4.3.3 重新调取接口,可以获取信息数据库信息

    4.4 完整的测试接口代码

    package com.liyh.controller;
    
    import com.alibaba.druid.pool.DruidPooledConnection;
    import com.liyh.druid.DruidDataSourceWrapper;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.cloud.context.config.annotation.RefreshScope;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    @RestController
    @RequestMapping("nacos")
    @RefreshScope
    public class TestController {
    
        @Autowired
        private DruidDataSourceWrapper dataSourceWrapper;
    
        @Value(value = "${spring.datasource.druid.master.url}")
        private String useLocalCache;
    
        @GetMapping("/get")
        @ResponseBody
        public String get() {
            System.out.println("url: " + useLocalCache);
            return useLocalCache;
        }
    
        @GetMapping("/query")
        public String testDruid() throws SQLException {
            DruidPooledConnection connection = dataSourceWrapper.getConnection();
            Statement statement = connection.createStatement();
            ResultSet resultSet = statement.executeQuery("SELECT * from sys_user where user_id = 1;");
            String result = "没有此用户!!!";
            while (resultSet.next()) {
                result =  resultSet.getString("user_name") + " : " + resultSet.getString("nick_name");
                System.out.println(result);
                return result;
            }
            return result;
        }
    }

    5 项目地址:springboot-nacos

  • 相关阅读:
    并发编程(五):设计原理
    并发编程(四):内存语义
    并发编程(三):内存模型基础
    并发编程(二):并发机制的实现
    并发编程(一):并发编程常见问题
    Jmeter学习前提:Jmeter安装
    Python语言学习:列表常用的方法
    Python语言学习:字符串常用的方法
    Python语言学习:homework1
    Python语言学习:pyc是什么
  • 原文地址:https://www.cnblogs.com/liyhbk/p/16077443.html
Copyright © 2020-2023  润新知