yml
+spring.profiles.active
的方式进行配置的切换和管理后期上生产环境部署发下如下问题
Filter
中 @RefreshScope
注解和@Bean
有冲突,会导致@Bean
注入的Filter
不生效具体情况可以参考 Nacos配置中心,Filter中动态获取参数
官网:http://nacos.io
官网的教程 Nacos 快速开始
官网的教程通俗易懂,本文就不赘述了
nocos
服务端启动成功后,nocos
后台管理登录地址 http://127.0.0.1:8848/nacos/#/login
毕业版本依赖关系(推荐使用)
Spring Cloud Version | Spring Cloud Alibaba Version | Spring Boot Version |
---|---|---|
Spring Cloud Greenwich | 2.1.0.RELEASE | 2.1.X.RELEASE |
Spring Cloud Finchley | 2.0.0.RELEASE | 2.0.X.RELEASE |
Spring Cloud Edgware | 1.5.0.RELEASE | 1.5.X.RELEASE |
客户端就是我们自己项目的应用,这里我们新建一个springboot测试的项目
添加依赖
这里和官网有点不同,我们是普通的sc项目,并不是spring-cloud-alibaba,需要引入下面的依赖和版本
spring-boot-starter-web
非必要是为了做测试
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
<version>2.1.0.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
bootstrap.yml
application.yml
文件中添加不会生效
,必须要bootstrap.yml
properties
文件可以自己类比
spring:
application:
name: nacos-config
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
file-extension: yaml
启动类
可以动态的获取nacos配置中心的值,进行打印
此代码为官网的测试代码
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import java.util.concurrent.TimeUnit;
@SpringBootApplication
public class ConfigBootstrap {
public static void main(String[] args) throws InterruptedException {
ConfigurableApplicationContext applicationContext = SpringApplication.run(ConfigBootstrap.class,args);
while(true) {
String userName = applicationContext.getEnvironment().getProperty("user.name");
String userAge = applicationContext.getEnvironment().getProperty("user.age");
//获取当前部署的环境
String currentEnv = applicationContext.getEnvironment().getProperty("current.env");
System.err.println("in "+currentEnv+" enviroment; "+"user name :" + userName + "; age: " + userAge);
TimeUnit.SECONDS.sleep(1);
}
}
}
新增测试类
正常配置中心的用法
@RefreshScope
来进行实时刷新
@Value("${user.name}")
对应配置中心的值
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.RestController;
@RestController
@RefreshScope
public class ConfigServiceController {
@Value("${user.name}")
private String username;
@GetMapping("/hello")
public String hello() {
return "Hello:" + username;
}
}
可支持profile粒度的配置
spring-cloud-starter-alibaba-nacos-config 在加载配置的时候,不仅仅加载了以 dataid 为 ${spring.application.name}.${file-extension:properties}
为前缀的基础配置,还加载了dataid为 ${spring.application.name}-${profile}.${file-extension:properties}
的基础配置。在日常开发中如果遇到多套环境下的不同配置,可以通过Spring 提供的 ${spring.profiles.active}
这个配置项来配置。
登录 http://127.0.0.1:8848/nacos/#/login 后台管理
新增配置 Data ID : nacos-config.yaml
user.name: xwf
user.age: 11
打印:
in null enviroment; user name :xwf; age: 11
浏览器输入http://127.0.0.1:8080/hello
了解了nacos
的基本用法,下面我们来对真实的项目进行改造
激活版本@profileActive@ 可以在maven中进行设置
dev
表示测试版本,prod
表示生产版本, 本文就不多做介绍了
server:
port: 8180
spring:
profiles:
active: @profileActive@
spring:
zipkin:
base-url: http://192.168.1.87:9411
sender:
type: web
#数据库连接信息
datasource:
name: zjiaoEvalweb
url: jdbc:mysql://192.168.1.87:3306/**
username: **
password: **
# 使用druid数据源
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
filters: stat
maxActive: 20
initialSize: 1
maxWait: 60000
minIdle: 1
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxOpenPreparedStatements: 20
redis:
database: 0
timeout: 3000ms
host: 192.168.1.87
port: 6379
password: huajie
# 连接池最大连接数(使用负值表示没有限制)
jedis:
pool:
max-idle: 500
min-idle: 50
max-active: 2000
max-wait: 1000ms
rabbitmq:
addresses: 192.168.1.87:5672 #MQ IP 和 端口
username: huajie #MQ登录名
password: huajie #MQ登录密码
virtual-host: /zjiaoMobile #MQ的虚拟主机名称
xxl:
sso:
server: http://127.0.0.1:8083
logout:
path: /logout
excluded:
paths: /zjiaoweb/api/**,/hystrix.stream
redis:
address: redis://192.168.1.87:6379
eureka:
client:
serviceUrl:
defaultZone: http://127.0.0.1:8761/eureka/
instance:
instance-id: ${spring.cloud.client.ip-address}:${server.port}
prefer-ip-address: true
这里就不做展示了,和application-dev.yml
类似,只是ip为真实地址
这里我们主要是将application-dev.yml
中的配置迁移到nacos
中
application-dev.yml
配置文件可以分为六部分
1.zipkin
和6.eureka
为基础组件,目前还未考虑如何进行迁移,如果以后了解到会更新在本文中
本文只讨论一般情况,及datasource
/redis
/rabbitmq
/xxl-sso
的配置
将传统yml
配置文件迁移到nacos
中
本项目服务名为eval-zjiaoweb-xwf
配置Data ID : {应用服务名}-{激活版本}.{配置文件类型} --> eval-zjiaoweb-xwf-dev.yaml
Group : 可以自行配置 默认也可以
bootstrap.yml 增加
spring:
application:
name: eval-zjiaoweb-xwf
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
file-extension: yaml
group: zjiao_group
pom.xml
依赖,父项目中进行了依赖版本管理,所以这边不需要写版本号
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
dependency>
@Value("${xxx.xx}")
的形式,进行组件的初始化@RefreshScope
,实时对配置进行刷新DruidConfig.java
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
import java.sql.SQLException;
/**
* @author lc
* @date: 2017/11/16 19:29
* @description: druid数据库连接池配置加载类
*/
@Configuration
@RefreshScope
public class DruidConfig {
private Logger logger = LoggerFactory.getLogger(getClass());
@Value("${druid.login.enabled}")
private boolean druidLoginEnabled;
@Value("${druid.login.username}")
private String druidLoginUsername;
@Value("${druid.login.password}")
private String druidLoginPassword;
@Value("${spring.datasource.url}")
private String dbUrl;
@Value("${spring.datasource.username}")
private String username;
@Value("${spring.datasource.password}")
private String password;
@Value("${spring.datasource.driver-class-name}")
private String driverClassName;
@Value("${spring.datasource.initialSize}")
private int initialSize;
@Value("${spring.datasource.minIdle}")
private int minIdle;
@Value("${spring.datasource.maxActive}")
private int maxActive;
@Value("${spring.datasource.maxWait}")
private int maxWait;
@Value("${spring.datasource.timeBetweenEvictionRunsMillis}")
private int timeBetweenEvictionRunsMillis;
@Value("${spring.datasource.minEvictableIdleTimeMillis}")
private int minEvictableIdleTimeMillis;
@Value("${spring.datasource.validationQuery}")
private String validationQuery;
@Value("${spring.datasource.testWhileIdle}")
private boolean testWhileIdle;
@Value("${spring.datasource.testOnBorrow}")
private boolean testOnBorrow;
@Value("${spring.datasource.testOnReturn}")
private boolean testOnReturn;
@Value("${spring.datasource.poolPreparedStatements}")
private boolean poolPreparedStatements;
@Value("${spring.datasource.filters}")
private String filters;
@Bean
public ServletRegistrationBean druidServlet() {
ServletRegistrationBean reg = new ServletRegistrationBean();
reg.setServlet(new StatViewServlet());
reg.addUrlMappings("/druid/*");
//web访问是否需要登录
if(druidLoginEnabled) {
reg.addInitParameter("loginUsername", druidLoginUsername);
reg.addInitParameter("loginPassword", druidLoginPassword);
}
return reg;
}
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new WebStatFilter());
filterRegistrationBean.addUrlPatterns("/*");
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
filterRegistrationBean.addInitParameter("profileEnable", "true");
filterRegistrationBean.addInitParameter("principalCookieName", "USER_COOKIE");
filterRegistrationBean.addInitParameter("principalSessionName", "USER_SESSION");
return filterRegistrationBean;
}
@Bean
@Primary
public DataSource druidDataSource() {
logger.info("开始配置druidDataSource");
DruidDataSource datasource = new DruidDataSource();
datasource.setUrl(this.dbUrl);
datasource.setUsername(username);
datasource.setPassword(password);
datasource.setDriverClassName(driverClassName);
datasource.setInitialSize(initialSize);
datasource.setMinIdle(minIdle);
datasource.setMaxActive(maxActive);
datasource.setMaxWait(maxWait);
datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
datasource.setValidationQuery(validationQuery);
datasource.setTestWhileIdle(testWhileIdle);
datasource.setTestOnBorrow(testOnBorrow);
datasource.setTestOnReturn(testOnReturn);
datasource.setPoolPreparedStatements(poolPreparedStatements);
try {
datasource.setFilters(filters);
} catch (SQLException e) {
logger.error("druid configuration initialization filter", e);
}
logger.info("druidDataSource配置成功");
return datasource;
}
}
redis有两个类RedisConfig
和RedisTemplateConfig
之前可以写在一个类里面,但是加入nacos之后会出现循环依赖的问题,所以拆成两个配置类
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import redis.clients.jedis.JedisPoolConfig;
/**
* Redis缓存配置类
*/
@Configuration
@RefreshScope
public class RedisConfig {
@Value("${spring.redis.host}")
public String host;
@Value("${spring.redis.port}")
public int port;
@Value("${spring.redis.password}")
public String password;
@Value("${spring.redis.database}")
public int database;
@Value("${spring.redis.jedis.pool.max-idle}")
public int maxIdle;
@Value("${spring.redis.jedis.pool.min-idle}")
public int minIdle;
@Value("${spring.redis.jedis.pool.max-active}")
public int maxActive;
@Value("${spring.redis.jedis.pool.max-wait}")
public String maxWait;
@Value("${spring.redis.timeout}")
public String timeout;
@Bean
public JedisConnectionFactory jedisConnectionFactory() {
RedisStandaloneConfiguration rf = new RedisStandaloneConfiguration();
rf.setDatabase(database);
rf.setHostName(host);
rf.setPort(port);
rf.setPassword(RedisPassword.of(password));
int to = Integer.parseInt(timeout.substring(0, timeout.length() - 2));
//JedisClientConfiguration.JedisClientConfigurationBuilder jedisClientConfiguration = JedisClientConfiguration.builder();
//jedisClientConfiguration.connectTimeout(Duration.ofMillis(to));
JedisClientConfiguration.JedisPoolingClientConfigurationBuilder jpb =
(JedisClientConfiguration.JedisPoolingClientConfigurationBuilder) JedisClientConfiguration.builder();
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMinIdle(minIdle);
jedisPoolConfig.setMaxTotal(maxActive);
int l = Integer.parseInt(maxWait.substring(0, maxWait.length() - 2));
jedisPoolConfig.setMaxWaitMillis(l);
jpb.poolConfig(jedisPoolConfig);
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(rf, jpb.build());
return jedisConnectionFactory;
}
}
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* RedisTemplate配置类
*/
@Configuration
public class RedisTemplateConfig {
@Autowired
JedisConnectionFactory jedisConnectionFactory;
@Bean
public RedisTemplate redisTemplate() {
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(jedisConnectionFactory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
@Data注解是lombok中的,不需要可以去掉
import com.huajie.utils.RabbitUtil;
import lombok.Data;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitMessagingTemplate;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
/**
* Created by admin on 2017/6/1 11:26.
* 容器中要有RabbitAdmin和RabbitTemplate实例
*/
@Configuration
@Data
@RefreshScope
public class RabbitMQConfig {
/**
* 注入配置文件属性
*/
@Value("${spring.rabbitmq.addresses}")
String addresses;//MQ地址
@Value("${spring.rabbitmq.username}")
String username;//MQ登录名
@Value("${spring.rabbitmq.password}")
String password;//MQ登录密码
@Value("${spring.rabbitmq.virtual-host}")
String vHost;//MQ的虚拟主机名
@Bean
public ConnectionFactory connectionFactory() throws Exception {
return RabbitUtil.connectionFactory(addresses, username, password, vHost);
}
@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) throws Exception {
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
return rabbitAdmin;
}
/**
* 消费消息,设置序列化
*/
@Bean
public RabbitListenerContainerFactory<?> rabbitListenerContainerFactory(ConnectionFactory connectionFactory){
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setMessageConverter(new Jackson2JsonMessageConverter());
return factory;
}
/**
* 发送消息,设置序列化
*/
@Bean
public static RabbitMessagingTemplate simpleMessageTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate template = new RabbitTemplate(connectionFactory);
RabbitMessagingTemplate rabbitMessagingTemplate = new RabbitMessagingTemplate();
rabbitMessagingTemplate.setMessageConverter(new MappingJackson2MessageConverter());
rabbitMessagingTemplate.setRabbitTemplate(template);
return rabbitMessagingTemplate;
}
}
import com.huajie.conf.Conf;
import com.huajie.filter.XxlSsoWebFilter;
import com.huajie.util.JedisUtil;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author xuxueli 2018-11-15
*/
@Configuration
@RefreshScope
public class XxlSsoConfig implements DisposableBean {
@Value("${xxl.sso.server}")
private String xxlSsoServer;
@Value("${xxl.sso.logout.path}")
private String xxlSsoLogoutPath;
@Value("${xxl.sso.excluded.paths}")
private String xxlSsoExcludedPaths;
@Value("${xxl.sso.redis.address}")
private String xxlSsoRedisAddress;
@Value("${spring.redis.password}")
private String redisPassword;
@Bean
public FilterRegistrationBean xxlSsoFilterRegistration() {
// xxl-sso, redis init
JedisUtil.init(xxlSsoRedisAddress,redisPassword);
// xxl-sso, filter init
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setName("XxlSsoWebFilter");
registration.setOrder(1);
registration.addUrlPatterns("/*");
registration.setFilter(new XxlSsoWebFilter());
registration.addInitParameter(Conf.SSO_SERVER, xxlSsoServer);
registration.addInitParameter(Conf.SSO_LOGOUT_PATH, xxlSsoLogoutPath);
registration.addInitParameter(Conf.SSO_EXCLUDED_PATHS, xxlSsoExcludedPaths);
return registration;
}
@Override
public void destroy() throws Exception {
JedisUtil.close();
}
}
spring:
zipkin:
base-url: http://192.168.1.87:9411
sender:
type: web
eureka:
client:
serviceUrl:
defaultZone: http://127.0.0.1:8761/eureka/
instance:
instance-id: ${spring.cloud.client.ip-address}:${server.port}
prefer-ip-address: true