nacos作为配置中心
分布式配置中心
在微服务架构中,为什么需要一个统一的配置中心呢?如果用一句话来说那就是方便管理,降低出错的可能。比如:你开发环境是一套配置,测试环境是一套,生产环境又是一套。你如果手动去修改,难免会出错吧。
Nacos
阿里开源的产品,可以作为配置中心,也可以代替Zookeeper作为服务注册中心。
下载地址:https://github.com/alibaba/nacos/releases
正题
为了方便,我在本地建立三个不同的数据库,分别代表开发环境、测试环境、生产环境的数据库。
--
新建一个Springboot工程,修改pom文件,引入相关依赖
<?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 http://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.1.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <mysql.version>5.1.47</mysql.version> <druid.version>1.1.15</druid.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba.boot</groupId> <artifactId>nacos-config-spring-boot-starter</artifactId> <version>0.2.1</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.0.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>${druid.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> </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> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.7.0</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.7</version> <configuration> <verbose>true</verbose> <overwrite>true</overwrite> </configuration> <executions> <execution> <id>mybatis-generator</id> <goals> <goal>generate</goal> </goals> </execution> </executions> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> </dependencies> </plugin> </plugins> </build> <profiles> <profile> <id>dev</id> <properties> <spring.profiles>dev</spring.profiles> </properties> <activation> <activeByDefault>true</activeByDefault> </activation> </profile> <profile> <id>test</id> <properties> <spring.profiles>test</spring.profiles> </properties> </profile> <profile> <id>pro</id> <properties> <spring.profiles>pro</spring.profiles> </properties> </profile> </profiles> </project>
先来看一下最终目录结构:
generatorConfig.xml是用来自动生成Mapper文件的,logback是用来生成日志的,config包里面是读取nocas上面的配置的,其他都是简单的东西。
-------------------------------------------
generatorConfig.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" > <generatorConfiguration> <context id="context1"> <!-- 这里引入扩展插件 --> <plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin"/> <commentGenerator> <!-- 关闭自动生成的注释 --> <property name="suppressAllComments" value="true"/> </commentGenerator> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/mytest?useSSL=false" userId="root" password="1234"/> <javaModelGenerator targetPackage="com.example.demo.model" targetProject="src/main/java" /> <sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources" /> <javaClientGenerator targetPackage="com.example.demo.dao" targetProject="src/main/java" type="XMLMAPPER" /> <!--数据库的表名--> <table tableName="user"> <generatedKey column="id" sqlStatement="MySql" identity="true" /> </table> </context> </generatorConfiguration>
在这里双击运行即可生成需要的java类。
选择这里可以切换环境
logback-spring.xml
<?xml version="1.0" encoding="UTF-8" ?> <configuration scan="true" scanPeriod="60 seconds"> <springProperty scope="context" name="LOG_HOME" source="logging.path"/> <springProfile name="dev,test,pro"> <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 --> <property name="log_pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger Line:%-3L - %msg%n"/> </springProfile> <appender name="app" class="ch.qos.logback.core.rolling.RollingFileAppender"> <File>${LOG_HOME}/app.log</File> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>${log_pattern}</pattern> </encoder> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>INFO</level> </filter> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <FileNamePattern>${LOG_HOME}/app.%d{yyyy-MM-dd}.log</FileNamePattern> <!--日志文件保留天数 --> <MaxHistory>30</MaxHistory> </rollingPolicy> </appender> <appender name="app-error" class="ch.qos.logback.core.rolling.RollingFileAppender"> <File>${LOG_HOME}/app_error.log</File> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] [%-5level] [%logger:%L] %msg [TxId:%X{PtxId},SpanId:%X{PspanId}]%n</pattern> </encoder> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>ERROR</level> </filter> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <FileNamePattern>${LOG_HOME}/app_error.%d{yyyy-MM-dd}.log</FileNamePattern> </rollingPolicy> </appender> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <outputPatternAsHeader>true</outputPatternAsHeader> <pattern>%black(%d{yyyy-MM-dd HH:mm:ss}) %highlight(%-6level) %black([%thread]) %boldCyan(%logger) %boldMagenta(#%method %-3L) : %black(%msg%n)</pattern> </encoder> </appender> <!-- 开发环境 --> <springProfile name="dev"> <logger name="org" level="INFO"/> <!-- level: FATAL,ERROR,WARN,INFO,DEBUG,TRACE --> <root level="DEBUG"> <appender-ref ref="CONSOLE"/> <appender-ref ref="app"/> <appender-ref ref="app-error"/> </root> </springProfile> <!-- 测试&生产环境 --> <springProfile name="test,pro"> <logger name="org" level="INFO"/> <root level="INFO"> <appender-ref ref="app"/> <appender-ref ref="app-error"/> </root> </springProfile> </configuration>
application.yml
spring: profiles: active: @spring.profiles@ logging: config: classpath:logback-spring.xml
下面打开nacos,做一下不同环境的配置
新建三个命名空间,之后进入配置管理页面,可以看到多了三个页签。
在每一个页签下添加我们需要的配置即可。(注意不同环境的数据库不一样)
url=jdbc:mysql://localhost:3306/mytest_pro?useSSL=false&useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&rewriteBatchedStatements=true
user_name=root
password=1234
driver_class_name=com.mysql.jdbc.Driver
不同的命名空间有不同的命名空间ID,这个有用,我们下一步会用到
属性文件的namespace填写对应的命名空间ID
application-dev.yml
server:
port: 8080
address: 0.0.0.0
nacos:
config:
server-addr: localhost:8848
namespace: 0d5b0e7c-485c-469b-9b3c-eb20dc9d9bb1
logging:
path: "./logs"
application-test.yml
server:
port: 8080
address: 0.0.0.0
nacos:
config:
server-addr: localhost:8848
namespace: b49147ed-72de-45e9-8d90-34644585a000
logging:
path: /AppLogs/boot-nacos-test
application-pro.yml
server:
port: 8080
address: 0.0.0.0
nacos:
config:
server-addr: localhost:8848
namespace: 5fb0883c-2db7-4344-9883-0394edb5c858
logging:
path: /AppLogs/boot-nacos
DatabaseConfig
package com.example.demo.config; import com.alibaba.nacos.api.config.annotation.NacosValue; import com.alibaba.nacos.spring.context.annotation.config.NacosPropertySource; import org.springframework.stereotype.Component; @Component @NacosPropertySource(dataId = "database",groupId = "DEFAULT_GROUP",autoRefreshed = true) public class DatabaseConfig { @NacosValue(value = "${driver_class_name}", autoRefreshed = true) private String driverClassName; @NacosValue(value = "${url}", autoRefreshed = true) private String url; @NacosValue(value = "${user_name}", autoRefreshed = true) private String username; @NacosValue(value = "${password}", autoRefreshed = true) private String password; public String getDriverClassName() { return driverClassName; } public String getUrl() { return url; } public String getUsername() { return username; } public String getPassword() { return password; } }
MybatisConfiguration
package com.example.demo.config; import com.alibaba.druid.pool.DruidDataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import javax.sql.DataSource; @Configuration @MapperScan(basePackages={"com.example.demo.dao"}) public class MybatisConfiguration { @Autowired private DatabaseConfig dataBaseConfig; @Bean public DataSource dataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(dataBaseConfig.getDriverClassName()); dataSource.setUrl(dataBaseConfig.getUrl()); dataSource.setUsername(dataBaseConfig.getUsername()); dataSource.setPassword(dataBaseConfig.getPassword()); return dataSource; } @Bean public SqlSessionFactory sqlSessionFactoryBean() throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource()); ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath:mapper/*.xml")); return sqlSessionFactoryBean.getObject(); } }
UserService
package com.example.demo.service; import com.example.demo.model.User; import java.util.List; public interface UserService { List<User> getUserList(); }
UserServiceImpl
package com.example.demo.service.impl; import com.example.demo.dao.UserMapper; import com.example.demo.model.User; import com.example.demo.model.UserExample; import com.example.demo.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override public List<User> getUserList() { UserExample example = new UserExample(); return userMapper.selectByExample(example); } }
UserController
package com.example.demo.controller; import com.example.demo.model.User; import com.example.demo.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController public class UserController { @Autowired private UserService userService; @RequestMapping("/users") public List<User> getUsers(){ return userService.getUserList(); } }
其它文件由generator负责生成。下面测试,我们先本地运行。
日志在项目根目录:
下面访问:
这个是开发环境的数据。现在我们切换到测试环境,重启。
日志在当前项目磁盘的根目录
访问:数据是测试环境的数据
最后切换生产环境。日志文件:
访问:
完整地址:GitHub