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; } }