SpringBoot+Mybatis+Mycat+Apollo

2021-01-14 15:43:50 浏览数 (1)

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集成成功。下一次再进行数据库迁移时,就不需要再加班熬夜搞通宵啦。

0 人点赞