关于mycat这个中间件,我就不多写了,网上褒贬不一。但是我们作为学习来说,是学的一个思维能力,动手能力,理解能力。这里做一下笔记,方便以后查看吧。
这里说一下我这边的环境:
数据库:mysql5.7以上
mycat:1.65
首先我们完成mysql的主从同步,这里就不说怎么完成的了,在之前文章里讲到过。
安装mycat也不多说了,大家可以去网上找教程什么的,都很方便。
安装完成之后,我们打开mycat路径里的conf配置文件目录,在里面我们要注意两个配置文件:
1.schema.xml Mycat对应的物理数据库和数据库表的配置
2.server.xml Mycat的配置文件,设置账号、参数等
我们这里要对这俩配置文件进行配置:
schema.xml:
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<!-- TESTDB1 是mycat的逻辑库名称,链接需要用的 -->
<schema name="mycat_zyx" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1"></schema>
<!-- database 是MySQL数据库的库名 -->
<dataNode name="dn1" dataHost="localhost1" database="zyx" />
<!--
上面的dataNode对应这里的dataNode的name,也就是dn1,这里相同即可,可以随便取名
dataNode节点中各属性说明:
name:指定逻辑数据节点名称;
dataHost:指定逻辑数据节点物理主机节点名称;也就是下面的datahost的name,也就是localhost1对面下面的localhost1,这里相同即可
database:指定物理主机节点上。如果一个节点上有多个库,可使用表达式db$0-99, 表示指定0-99这100个数据库;dataHost 节点中各属性说明:
name:物理主机节点名称;
maxCon:指定物理主机服务最大支持1000个连接;
minCon:指定物理主机服务最小保持10个连接;
writeType:指定写入类型;
0,只在writeHost节点写入;
1,在所有节点都写入。慎重开启,多节点写入顺序为默认写入根据配置顺序,第一个挂掉切换另一个;
dbType:指定数据库类型;
dbDriver:指定数据库驱动;
balance:指定物理主机服务的负载模式。
0,不开启读写分离机制;
1,全部的readHost与stand by writeHost参与select语句的负载均衡,简单的说,当双主双从模式(M1->S1,M2->S2,并且M1与 M2互为主备),正常情况下,M2,S1,S2都参与select语句的负载均衡;
2,所有的readHost与writeHost都参与select语句的负载均衡,也就是说,当系统的写操作压力不大的情况下,所有主机都可以承担负载均衡;
-->
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="3" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<!-- 可以配置多个主从 -->
<writeHost host="hostM1" url="192.168.1.133:3306" user="root" password="root">
<!-- 可以配置多个从库 -->
<readHost host="hostS2" url="192.168.1.132:3306" user="root" password="root" />
</writeHost>
</dataHost>
</mycat:schema>
不要管它的配置,直接把我这段内容覆盖进去即可。
这里大致说一下
mysql主库ip:192.168.1.133
mysql从库ip:192.168.1.132
mycat安装的服务器ip:192.168.1.133也就是安装在主库服务器下
server.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!-- - - Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License. - You
may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0
- - Unless required by applicable law or agreed to in writing, software -
distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the
License for the specific language governing permissions and - limitations
under the License. -->
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
<!-- 读写都可用的用户 -->
<user name="root" defaultAccount="true">
<property name="password">root</property>
<property name="schemas">mycat_zyx</property>
</user> <!-- 只读用户 -->
<user name="user">
<property name="password">user</property>
<property name="schemas">mycat_zyx</property>
<property name="readOnly">true</property>
</user></mycat:server>
关于更多配置,这里就不写了,可以自行查询,我这么配,主要是为了完成集成mycat中间件来实现读写分离。
就这么简单,配置就完成了。我们进入mycat目录启动mycat即可
mycat目录启动mycat:./mycat start
关于是否启动成功,进入mycat的logs目录下查看wrapper.log。里面如果有successful就说明启动成功了。
实在不放心,我们可以利用navicat工具去外部连接mycat虚拟数据库去看看是否成功
这里要注意一下,mycat的端口号为8066,如果连不上,10060报错,注意一下端口号是否开放,或者说是否关闭防火墙。
到此处,mycat全部配置结束了。我们进入下一步看如何结合springboot来实现动态数据源切换。
首先,我们在maven引入以下包,如果有了的,可以忽略,重点就是aop这个包:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency> <dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency> <dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.23</version>
</dependency>
</dependencies>
之后我们在springboot的yml配置文件里加入以下配置:
这里的username和password对应的是你在server.xml里配置的用户名和密码
然后在config包里创建配置类:
DataSourceConfig.java
import javax.sql.DataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class DataSourceConfig { // 创建可读数据源
@Bean(name = "selectDataSource")
@ConfigurationProperties(prefix = "spring.datasource.select") // application.properteis中对应属性的前缀
public DataSource dataSource1() {
return DataSourceBuilder.create().build();
} // 创建可写数据源
@Bean(name = "updateDataSource")
@ConfigurationProperties(prefix = "spring.datasource.update") // application.properteis中对应属性的前缀
public DataSource dataSource2() {
return DataSourceBuilder.create().build();
}}
DataSourceContextHolder.java
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;@Component
@Lazy(false)
public class DataSourceContextHolder {
// 采用ThreadLocal 保存本地多数据源
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>(); // 设置数据源类型
public static void setDbType(String dbType) {
contextHolder.set(dbType);
} public static String getDbType() {
return contextHolder.get();
} public static void clearDbType() {
contextHolder.remove();
}}
DynamicDataSource.java
import java.util.HashMap;
import java.util.Map;import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.stereotype.Component;//在Spring 2.0.1中引入了AbstractRoutingDataSource, 该类充当了DataSource的路由中介, 能有在运行时, 根据某种key值来动态切换到真正的DataSource上。
@Component
@Primary
public class DynamicDataSource extends AbstractRoutingDataSource {
@Autowired
@Qualifier("selectDataSource")
private DataSource selectDataSource; @Autowired
@Qualifier("updateDataSource")
private DataSource updateDataSource; /**
* 这个是主要的方法,返回的是生效的数据源名称
*/
@Override
protected Object determineCurrentLookupKey() {
System.out.println("DataSourceContextHolder:::" + DataSourceContextHolder.getDbType());
return DataSourceContextHolder.getDbType();
} /**
* 配置数据源信息
*/
@Override
public void afterPropertiesSet() {
Map<Object, Object> map = new HashMap<>();
map.put("selectDataSource", selectDataSource);
map.put("updateDataSource", updateDataSource);
setTargetDataSources(map);
setDefaultTargetDataSource(updateDataSource);
super.afterPropertiesSet();
}
}
配置完成之后,我们利用springboot的aop技术,来完成动态数据源切换。
SwitchDataSourceAOP.java
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;import com.zyx.db.config.DataSourceContextHolder;
// 使用AOP动态切换不同的数据源
@Aspect
@Component
@Lazy(false)
@Order(0) // Order设定AOP执行顺序 使之在数据库事务上先执行
public class SwitchDataSourceAOP {
// 这里切到你的方法目录
@Before("execution(* com.zyx.service.*.*(..))")
public void process(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
if (methodName.startsWith("get") || methodName.startsWith("count") || methodName.startsWith("find")
|| methodName.startsWith("list") || methodName.startsWith("select") || methodName.startsWith("check")) {
DataSourceContextHolder.setDbType("selectDataSource");
} else {
// 切换dataSource
DataSourceContextHolder.setDbType("updateDataSource");
}
}
}
我们利用aop拦截service方法前置名来拦截service方法进行动态数据源切换。
所以这个事提醒了我们编程要规范。方法名最好不要随意乱取,要根据一定的规范来命名。
到这里,就全部结束了,至于是否成功,可以写一个controller,service,dao来做个测试,这里就不放测试结果了。
注意service的方法名前缀,包地址要和SwitchDataSourceAOP.java里的@Before("execution(* com.zyx.service.*.*(..))")地址对应
至于验证过程,大可以把yml里的配置改一下,读写数据源都用那个mycat的读数据源,然后用service里的写数据操作。看看是否能写即可。