Apollo配置中心
Apollo是什么?
携程中间件团队研发的分布式配置中心。
什么是配置中心?
1.够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端。
2.具备规范的权限、流程治理等特性,适用于微服务配置管理场景。
Apollo简介
1. Apollo服务端基于Spring Boot和Spring Cloud开发,可以直接运行,不需额外安装应用容器。
2.Apollo的Java客户端不依赖任何框架,同时对Spring/Spring Boot环境也有较好的支持。
3.Net客户端不依赖任何框架,能够运行于所有.Net运行时环境。
Apollo的特性
1.统一管理不同环境、不同集群的配置
2.配置修改实时生效(热发布)
3.版本发布管理:所有的配置发布都有版本概念,从而可以方便的支持配置的回滚。
4.灰度发布:只对部分应用实例生效,观察一段时间确认没问题后再推给所有应用实例
5.权限管理、发布审核、操作审计
6.客户端配置信息监控:可以方便的看到配置在被哪些实例使用
7.提供Java和.Net原生客户端
8.提供开放平台API
9.部署简单
Apollo的安装
一、准备工作: 1. JDK——Apollo服务端JDK1.8 ,Apollo客户端JDK1.7 2. MySQL——5.6.5 3. 必备的Linux操作技能
二、下载地址
代码语言:javascript复制https://github.com/nobodyiam/apollo-build-scripts
三、配置信息 3.1 创建数据库 (1)创建ApolloPortalDB (2)创建ApolloConfigDB (3)验证创建结果 (4)select `Id`, `AppId`, `Name` from ApolloPortalDB.App; (5)select `NamespaceId`, `Key`, `Value`, `Comment` from ApolloConfigDB.Item; 3.2 配置数据库连接
代码语言:javascript复制###修改build.sh配置
(1)apollo config db info
apollo_config_db_url=jdbc:mysql://localhost:3306/ApolloConfigDB?characterEncoding=utf8
apollo_config_db_username=用户名
apollo_config_db_password=密码(如果没有密码,留空即可)
(2)apollo portal db info
apollo_portal_db_url=jdbc:mysql://localhost:3306/ApolloPortalDB?characterEncoding=utf8
apollo_portal_db_username=用户名
apollo_portal_db_password=密码(如果没有密码,留空即可)
四、获取安装包通过编译Apollo源码的方式获取安装包。Apollo Portal需要在不同的环境访问不同的meta service(apollo-configservice)地址,所以需要在打包时提供这些信息。假设DEV的apollo-configservice未绑定域名,地址是1.1.1.1:8080,FAT的apollo-configservice绑定了域名apollo.fat.xxx.com,UAT的apollo-configservice绑定了域名apollo.uat.xxx.com,PRO的apollo-configservice绑定了域名apollo.xxx.com,那么编辑scripts/build.sh,如下修改各环境meta service服务地址,格式为{env}_meta=http://{config-service-url:port},如果某个环境不需要,也可以直接删除对应的配置项
代码语言:javascript复制dev_meta=http://1.1.1.1:8080
fat_meta=http://apollo.fat.xxx.com
uat_meta=http://apollo.uat.xxx.com
pro_meta=http://apollo.xxx.com
META_SERVERS_OPTS="-Ddev_meta=$dev_meta -Dfat_meta=$fat_meta -Duat_meta=$uat_meta -Dpro_meta=$pro_meta"
五、打包编译
代码语言:javascript复制执行./build.sh
六、获取各个安装包 6.1 获取apollo-configservice安装包 apollo-configservice位于apollo-configservice/target/目录下的apollo-configservice-x.x.x-github.zip 6.2 获取apollo-adminservice安装包 位于apollo-adminservice/target/目录下的apollo-adminservice-x.x.x-github.zip 6.3 获取apollo-portal安装包 位于apollo-portal/target/目录下的apollo-portal-x.x.x-github.zip
七、启动apollo 将对应环境的安装包上传到服务器上,解压后执行scripts/startup.sh即可。如需停止服务,执行scripts/shutdown.sh。为了方便起见,可以将多个执行脚本打包成一个执行脚本。如下:
代码语言:javascript复制vim startall.sh
sh ~/Downloads/code/github/apollo/apollo-adminservice/target/apollo-adminservice-1.4.0-SNAPSHOT-github/scripts/startup.sh
sh ~/Downloads/code/github/apollo/apollo-configservice/target/apollo-configservice-1.4.0-SNAPSHOT-github/scripts/startup.sh
sh ~/Downloads/code/github/apollo/apollo-portal/target/apollo-portal-1.4.0-SNAPSHOT-github/scripts/startup.sh
vim stopall.sh
sh ~/Downloads/code/github/apollo/apollo-adminservice/target/apollo-adminservice-1.4.0-SNAPSHOT-github/scripts/shutdown.sh
sh ~/Downloads/code/github/apollo/apollo-configservice/target/apollo-configservice-1.4.0-SNAPSHOT-github/scripts/shutdown.sh
sh ~/Downloads/code/github/apollo/apollo-portal/target/apollo-portal-1.4.0-SNAPSHOT-github/scripts/shutdown.sh
运行Apollo
./startall.sh
运行Apollo
代码语言:javascript复制./startall.sh
启动结果如下:
Apollo的使用
1. 查看配置demo
代码语言:javascript复制http://localhost:8070
输入用户名apollo,密码admin后登录
点击SampleApp进入配置界面,可以看到当前有一个配置timeout=100
2. 创建自己的个人项目并创建配置
此处让Apollo管理JDBC连接。我们的程序中不直接配置JDBC连接。目的是为了接下来验证项目中没有JDBC连接也可以实现数据库操作。
Spring集成Apollo
我们都遇到过项目越长越大,越来越膨胀,需要将数据库进行分库分表的需求。下面以SpringBoot Mybatis Mycat Apollo实现一个从单体数据库向分库分表数据源切换的动态切换数据源的Demo。
1.准备单库单表的建表语句
代码语言:javascript复制#############################单表单库测试##################
DROP DATABASE IF EXISTS test;
CREATE DATABASE test;
USE test;
CREATE TABLE customer (
id INT NOT NULL AUTO_INCREMENT COMMENT '客户id',
name VARCHAR(20) DEFAULT '' COMMENT '客户姓名',
phone VARCHAR(11) DEFAULT '' COMMENT '客户手机号',
adddate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
updatedate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE item (
id INT NOT NULL AUTO_INCREMENT,
value INT NOT NULL default 0,
adddate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
updatedate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE customer_order (
id INT NOT NULL AUTO_INCREMENT,
amount INT NOT NULL default 0,
adddate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
updatedate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
2. 创建与以上实体表对应的Java代码
2.1 创建Model
代码语言:javascript复制public class Customer {
private int id;
private String name;
private String phone;
private Date addDate;
private Date updateDate;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public Date getAddDate() {
return addDate;
}
public void setAddDate(Date addDate) {
this.addDate = addDate;
}
public Date getUpdateDate() {
return updateDate;
}
public void setUpdateDate(Date updateDate) {
this.updateDate = updateDate;
}
}
代码语言:javascript复制public class CustomerOrder {
private int id;
private int amount;
private Date addDate;
private Date updateDate;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
public Date getAddDate() {
return addDate;
}
public void setAddDate(Date addDate) {
this.addDate = addDate;
}
public Date getUpdateDate() {
return updateDate;
}
public void setUpdateDate(Date updateDate) {
this.updateDate = updateDate;
}
}
代码语言:javascript复制public class Item {
private int id;
private int value;
private Date addDate;
private Date updateDate;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public Date getAddDate() {
return addDate;
}
public void setAddDate(Date addDate) {
this.addDate = addDate;
}
public Date getUpdateDate() {
return updateDate;
}
public void setUpdateDate(Date updateDate) {
this.updateDate = updateDate;
}
}
2.2 创建DAO和Mybatis Mapper文件
代码语言:javascript复制@Mapper
public interface CustomerDao {
int save(Customer customer);
Customer query(int id);
}
代码语言:javascript复制@Mapper
public interface CustomerOrderDao {
int save(CustomerOrder customer);
CustomerOrder query(int id);
}
代码语言:javascript复制@Mapper
public interface ItemDao {
int save(Item customer);
Item query(int id);
}
代码语言:javascript复制<mapper namespace="com.yunxi.apollodemo.test.dao.CustomerDao">
<resultMap id="BaseResultMap" type="com.yunxi.apollodemo.test.model.Customer">
<id column="id" jdbcType="INTEGER" property="id" />
<result column="name" jdbcType="VARCHAR" property="name" />
<result column="phone" jdbcType="VARCHAR" property="phone" />
<result column="adddate" jdbcType="TIMESTAMP" property="addDate" />
<result column="updatedate" jdbcType="TIMESTAMP" property="updateDate" />
</resultMap>
<select id="query" parameterType="java.lang.Integer" resultMap="BaseResultMap">
select
*
from customer
where id = #{id,jdbcType=BIGINT}
</select>
<insert id="save" parameterType="com.yunxi.apollodemo.test.model.Customer">
insert into customer (id,name, phone)
values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR}, #{phone,jdbcType=VARCHAR})
</insert>
</mapper>
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yunxi.apollodemo.test.dao.CustomerOrderDao">
<resultMap id="BaseResultMap" type="com.yunxi.apollodemo.test.model.CustomerOrder">
<id column="id" jdbcType="INTEGER" property="id" />
<result column="amount" jdbcType="INTEGER" property="amount" />
<result column="adddate" jdbcType="TIMESTAMP" property="addDate" />
<result column="updatedate" jdbcType="TIMESTAMP" property="updateDate" />
</resultMap>
<select id="query" parameterType="java.lang.Integer" resultMap="BaseResultMap">
select
*
from customer_order
where id = #{id,jdbcType=BIGINT}
</select>
<insert id="save" parameterType="com.yunxi.apollodemo.test.model.CustomerOrder">
insert into customer_order (id,amount)
values (#{id,jdbcType=INTEGER}, #{amount,jdbcType=INTEGER})
</insert>
</mapper>
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yunxi.apollodemo.test.dao.ItemDao">
<resultMap id="BaseResultMap" type="com.yunxi.apollodemo.test.model.Item">
<id column="id" jdbcType="INTEGER" property="id" />
<result column="value" jdbcType="INTEGER" property="value" />
<result column="adddate" jdbcType="TIMESTAMP" property="addDate" />
<result column="updatedate" jdbcType="TIMESTAMP" property="updateDate" />
</resultMap>
<select id="query" parameterType="java.lang.Integer" resultMap="BaseResultMap">
select
*
from item
where id = #{id,jdbcType=BIGINT}
</select>
<insert id="save" parameterType="com.yunxi.apollodemo.test.model.Item">
insert into item (id,value)
values (#{id,jdbcType=INTEGER}, #{value,jdbcType=INTEGER})
</insert>
</mapper>
2.3 创建Controller
代码语言:javascript复制@RestController
@RequestMapping("customer")
public class CustomerController {
@Autowired
private CustomerDao customerDao;
@RequestMapping("/save")
public void save() {
Customer customer_1 = new Customer();
customer_1.setId(1);
customer_1.setName("yunxi");
customer_1.setPhone("3344625292");
customerDao.save(customer_1);
Customer customer_2 = new Customer();
customer_2.setId(2);
customer_2.setName("wushuang");
customer_2.setPhone("3190976240");
customerDao.save(customer_2);
}
@RequestMapping("/query")
public void query() {
System.out.println("用户1=" JSON.toJSONString(customerDao.query(1)));
System.out.println("用户2=" JSON.toJSONString(customerDao.query(2)));
}
}
代码语言:javascript复制@RestController
@RequestMapping("customerOrder")
public class CustomerOrderController {
@Autowired
private CustomerOrderDao customerOrderDao;
@RequestMapping("save")
public void save() {
CustomerOrder customerOrder_1 = new CustomerOrder();
customerOrder_1.setId(1);
customerOrder_1.setAmount(100);
customerOrderDao.save(customerOrder_1);
CustomerOrder customerOrder_2 = new CustomerOrder();
customerOrder_2.setId(2);
customerOrder_2.setAmount(200);
customerOrderDao.save(customerOrder_2);
CustomerOrder customerOrder_3 = new CustomerOrder();
customerOrder_3.setId(3);
customerOrder_3.setAmount(300);
customerOrderDao.save(customerOrder_3);
}
@RequestMapping("query")
public void query() {
System.out.println("订单1=" JSON.toJSONString(customerOrderDao.query(1)));
System.out.println("订单2=" JSON.toJSONString(customerOrderDao.query(2)));
System.out.println("订单3=" JSON.toJSONString(customerOrderDao.query(3)));
}
}
代码语言:javascript复制@RestController
@RequestMapping("item")
public class ItemController {
@Autowired
private ItemDao itemDao;
@RequestMapping("save")
public void ave() {
Item item_1 = new Item();
item_1.setId(1);
item_1.setValue(100);
itemDao.save(item_1);
Item item_2 = new Item();
item_2.setId(2);
item_2.setValue(200);
itemDao.save(item_2);
Item item_3 = new Item();
item_3.setId(3);
item_3.setValue(300);
itemDao.save(item_3);
}
@RequestMapping("query")
public void query() {
System.out.println("商品1=" JSON.toJSONString(itemDao.query(1)));
System.out.println("商品2=" JSON.toJSONString(itemDao.query(2)));
System.out.println("商品3=" JSON.toJSONString(itemDao.query(3)));
}
}
2.4 配置Apollo集成
【注】此时的SpringBoot项目并没有配置JDBC连接。SpringBoot配置文件如下:
代码语言:javascript复制app.id=yunxi-java-apollo
server.port=9999
#Apollo Meta Server
apollo.bootstrap.enabled = true
apollo.meta=http://127.0.0.1:8080
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
logging.level.com.yunxi.apollodemo = debug
mybatis.type-aliases-package=com.yunxi.apollodemo.test.model
mybatis.mapper-locations=classpath:mapper/*.xml
配置SpringBoot集成Apollo:
代码语言:javascript复制@Slf4j
@Configuration
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceConfig {
@Autowired
ApplicationContext context;
@Autowired
private org.springframework.cloud.context.scope.refresh.RefreshScope refreshScope;
@ApolloConfigChangeListener
private void onChange(ConfigChangeEvent changeEvent) {
DataSourceProperties dataSourceProperties = context.getBean(DataSourceProperties.class);
changeEvent.changedKeys().stream().forEach(s -> {
if (s.contains("spring.datasource.url")) {
dataSourceProperties.setUrl(changeEvent.getChange(s).getNewValue());
}
});
refreshScope.refresh("dataSource");
}
@RefreshScope
@Bean
public DataSource dataSource(DataSourceProperties dataSourceProperties) {
return dataSourceProperties.initializeDataSourceBuilder().build();
}
}
2.5 启动SpringBoot应用程序
代码语言:javascript复制 . ____ _ __ _ _
/\ / ___'_ __ _ _(_)_ __ __ _
( ( )___ | '_ | '_| | '_ / _` |
\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |___, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.4.RELEASE)
2019-05-12 18:33:46.524 INFO 2135 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.17]
2019-05-12 18:33:46.613 INFO 2135 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2019-05-12 18:33:46.613 INFO 2135 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1017 ms
2019-05-12 18:33:47.119 INFO 2135 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2019-05-12 18:33:47.456 INFO 2135 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 9999 (http) with context path ''
2019-05-12 18:33:47.459 INFO 2135 --- [ main] c.y.apollodemo.ApolloDemoApplication : Started ApolloDemoApplication in 13.665 seconds (JVM running for 19.471)
2.6 测试单库单表下集成Apollo
分别在浏览器中执行输入以下连接,验证执行结果。
代码语言:javascript复制http://localhost:9999/customer/save
http://localhost:9999/customer/query
http://localhost:9999/customerOrder/save
http://localhost:9999/customerOrder/query
http://localhost:9999/item/save
http://localhost:9999/item/query
执行结果无异常,说明Apollo集成的单库单表的JDBC连接生效了。
此时的JDBC连接为如下配置项。
2.7 配置Mycat服务
参考本公众号Mycat安装
2.8 修改Apollo配置
将数据源修改为Mycat
再次重复执行2.6节中的操作,发现分库分表后的数据源生效。至此验证了SpringBoot Mybatis Mycat Apollo集成成功。下一次再进行数据库迁移时,就不需要再加班熬夜搞通宵啦。