背景说明
- 公司有十几个服务,预测随着业务的发展,服务数量可能会越来越多,而为了更快的进行服务开发和集中管理,提出将配置抽取到数据库的方案。达到配置和业务剥离又可以集中管理的效果,比如许多服务都会用到像kafka,mysql,redis,hbase等一些配置,这些配置本身其实和业务没有关系,如果将这些配置抽取出来复用,是不是很完美?
- 为啥不配合使用git,而是mysql?
技术点
springcloud config + mysql + eureka
实现思路
- 各微服务获取配置时经过注册中心
- 注册中心分配配置中心服务的某个节点
- 分配的节点从数据库获取配置返回
代码实现
搭建配置中心服务
- 引入config-server,mysql,jdbc等相关依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
- application.yml配置文件
#基础信息
info:
service:
name: ${spring.application.name}
des: 配置中心服务
spring:
#数据源配置
datasource:
url: jdbc:mysql://你的ip:3306/你的数据库?useSSL=false&serverTimezone=UTC
username: root
password: 你的密码
driver-class-name: com.mysql.jdbc.Driver
hikari:
connection-test-query: SELECT 1 FROM DUAL
connection-timeout: 30000
maximum-pool-size: 23
max-lifetime: 1800000
minimum-idle: 5
#springcloud config 配置
profiles:
active: jdbc
application:
name: config-server
cloud:
config:
server:
jdbc: true
spring.cloud.config.server.jdbc.sql: SELECT
t.configuration_key,
t.configuration_value
FROM
(
SELECT
t3.configuration_key,
t3.configuration_value,
t1.NAME,
t1.env,
t1.label
FROM
service_info t1
INNER JOIN service_basis t2 ON t1.id = t2.ser_id
INNER JOIN configuration t3 ON t2.cf_nickname = t3.nickname UNION ALL
SELECT
t2.configuration_key,
t2.configuration_value,
t1.NAME,
t1.env,
t1.label
FROM
service_info t1
INNER JOIN customcf t2 ON t1.id = t2.ser_id
) t
WHERE
t.`name` = ?
AND t.`env` = ?
AND t.`label` = ?
#注册中心配置
eureka:
instance:
instance-id: ${spring.cloud.client.ipAddress}:${server.port}
prefer-ip-address: true
client:
healthcheck:
enabled: true
serviceUrl:
defaultZone: 注册中心地址
#配置中心端口
server:
port: 18000
- 这里的sql是根据具体的业务。
@Override
public Environment findOne(String application, String profile, String label) {
String config = application;
if (StringUtils.isEmpty(label)) {
label = "master";
}
if (StringUtils.isEmpty(profile)) {
profile = "default";
}
if (!profile.startsWith("default")) {
profile = "default," + profile;
}
String[] profiles = StringUtils.commaDelimitedListToStringArray(profile);
Environment environment = new Environment(application, profiles, label, null,
null);
if (!config.startsWith("application")) {
config = "application," + config;
}
List<String> applications = new ArrayList<String>(new LinkedHashSet<>(
Arrays.asList(StringUtils.commaDelimitedListToStringArray(config))));
List<String> envs = new ArrayList<String>(new LinkedHashSet<>(Arrays.asList(profiles)));
Collections.reverse(applications);
Collections.reverse(envs);
for (String app : applications) {
for (String env : envs) {
Map<String, String> next = (Map<String, String>) jdbc.query(this.sql,
new Object[] { app, env, label }, this.extractor);
if (!next.isEmpty()) {
environment.add(new PropertySource(app + "-" + env, next));
}
}
}
return environment;
}
- 服务如果启动的时候打断点会进到JdbcEnvironmentRepository类的findOne方法中,其实就是把KEY,VALUE取出来放到Map再放到Environment中。数据库查询操作是通过JdbcTemplate来实现的,所以也就印证了为啥要引入jdbc依赖。
- 启动类中加入注解
@SpringBootApplication
@EnableConfigServer
@EnableDiscoveryClient
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
创建表
- JdbcEnvironmentRepository类中有DEFAULT_SQL,所以新建个PROPERTIES表再加上KEY,VALUE,APPLICATION,PROFILE,LABEL这几个字段完全可以
@ConfigurationProperties("spring.cloud.config.server.jdbc")
public class JdbcEnvironmentRepository implements EnvironmentRepository, Ordered {
private static final String DEFAULT_SQL = "SELECT KEY, VALUE from PROPERTIES where APPLICATION=? and PROFILE=? and LABEL=?";
搭建客户端服务
-
引入config,eureka依赖
-
bootstrap.yml(一定要是bootstrap,加载优先级高)
#配置中心连接配置
spring:
application:
name: 你的服务名
cloud:
config:
fail-fast: true
#应用名
name: ${spring.application.name}
#环境 dev test pro
profile: test
#分支
label: master
discovery:
enabled: true
#高可用
service-id: config-server
#注册中心配置
eureka:
instance:
instance-id: ${spring.cloud.client.ipAddress}:${server.port}
prefer-ip-address: true
appname: ${spring.application.name}
client:
serviceUrl:
defaultZone: 注册中心地址
- application.yml
#健康检查
#eureka.client.healthcheck.enabled=true should only be set in application.yml. Setting the value in bootstrap.yml will cause undesirable side effects like registering in eureka with an UNKNOWN status.
eureka:
client:
healthcheck:
enabled: true
参考文档
- https://forezp.blog.csdn.net/article/details/87866560
- https://cloud.spring.io/spring-cloud-config/single/spring-cloud-config.html#_jdbc_backend
源码
https://github.com/forezp/SpringCloudLearning/tree/master/chapter10-5-jdbc