User_Model Day2
依赖中添加
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
</dependency>
application.properties中添加
spring.redis.host=CentOS
spring.redis.port=6379
spring.redis.timeout=5s
spring.redis.lettuce.pool.max-active=10
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.max-wait=5ms
spring.redis.lettuce.pool.min-idle=1
UserModelApplication中添加
@Bean
public RedisTemplate<Object,Object>redisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException
{
RedisTemplate<Object,Object> redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new JdkSerializationRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return redisTemplate;
}
logback.xml中添加
<logger name="com.baizhi.cache" level="DEBUG" additivity="false">
<appender-ref ref="STDOUT" />
</logger>
二级缓存-在UserDao.xml添加
<cache type="com.baizhi.cache.UserDefineCache">
<property name="timeout" value="60"/>
</cache>
小窍门:开个工厂的后门拿到springBean工厂内的redisTempl
package com.baizhi;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class ApplicationContextHold implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext=applicationContext;
}
public static Object getBean(String beanName){
return applicationContext.getBean(beanName);
}
}
com.baizhi.cache
package com.baizhi.cache;
import com.baizhi.ApplicationContextHold;
import org.apache.ibatis.cache.Cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class UserDefineRedisCache implements Cache {
private Logger logger = LoggerFactory.getLogger(UserDefineRedisCache.class);
private String namespace;
private RedisTemplate redisTemplate= (RedisTemplate)ApplicationContextHold.getBean("redisTemplate");
private long timeout = 300;
private ReadWriteLock readWriteLock=new ReentrantReadWriteLock();
public UserDefineRedisCache(String namespace){
this.namespace=namespace;
}
@Override
public String getId() {
return namespace;
}
@Override
public void putObject(Object key, Object value) {
logger.debug("将查询结果存储到cache.key:"+key+".value"+value);
ValueOperations opsForValue = redisTemplate.opsForValue();
opsForValue.set(key,value,timeout, TimeUnit.SECONDS);
}
@Override
public Object getObject(Object key) {
logger.debug("从缓存中读取结果.key"+key);
ValueOperations opsForValue = redisTemplate.opsForValue();
return opsForValue.get(key);
}
@Override
public Object removeObject(Object key) {
logger.debug("从缓存中删除.key"+key);
ValueOperations opsForValue = redisTemplate.opsForValue();
Object value = opsForValue.get(key);
redisTemplate.delete(key);
return value;
}
@Override
public void clear() {
logger.debug("清空缓存区");
redisTemplate .execute((RedisConnection connection)-> {
connection.flushAll();
return null;
});
}
@Override
public int getSize() {
return 0;
}
@Override
public ReadWriteLock getReadWriteLock() {
return readWriteLock;
}
}
设置读写分离
数据库主从配置:https://blog.csdn.net/weixin_38231448/article/details/104932764
安装数据库:https://blog.csdn.net/weixin_38231448/article/details/103858887
注意:可能会报错需要:修改相关路径和文件
主:master
修改/etc/my.cnf
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
symbolic-links=0
server-id=1
log-bin=mysql-bin
binlog-do-db=zpark
binlog-do-db=baizhi
binlog-ignore-db=mysql
binlog-ignore-db=test
expire_logs_days=10
auto_increment_increment=2
auto_increment_offset=1
[mysqld_safe]
log-error=/var/log/mariadb/mariadb.log
pid-file=/var/run/mariadb/mariadb.pid
--------------------------------------
mysql> show master status\G
--------------------------------------
从:slave
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
symbolic-links=0
server-id=2
log-bin=mysql-bin
replicate-do-db=zpark
replicate-do-db=baizhi
replicate-ignore-db=mysql
replicate-ignore-db=test
expire_logs_days=10
auto_increment_increment=2
auto_increment_offset=2
[mysqld_safe]
log-error=/var/log/mariadb/mariadb.log
pid-file=/var/run/mariadb/mariadb.pid
--------------------------------------
mysql> stop slave;
mysql> change master to master_host='主机IP',master_user='root',master_password='root',master_log_file='mysql-bin.00000x',master_log_pos=xxxxx;
mysql> start slave;
mysql> show slave status\G
可以重置slave:
reset slave;
配置datasouce
在application.properties中修改配置:
#数据源
#spring.datasource.username=root
#spring.datasource.password=root
#spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#spring.datasource.url=jdbc:mysql://CentOSM:3306/test?useUnicode=true&characterEncoding=UTF8&serverTimezone=UTC&useSSL=false
spring.datasource.master.username=root
spring.datasource.master.password=root
spring.datasource.master.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.master.jdbcurl=jdbc:mysql://CentOSM:3306/baizhiuseUnicode=true&characterEncoding=UTF8&serverTimezone=UTC&useSSL=false
spring.datasource.slave1.username=root
spring.datasource.slave1.password=root
spring.datasource.slave1.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.slave1.jdbcurl=jdbc:mysql://CentOSA:3306/baizhiuseUnicode=true&characterEncoding=UTF8&serverTimezone=UTC&useSSL=false
spring.datasource.slave2.username=root
spring.datasource.slave2.password=root
spring.datasource.slave2.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.slave2.jdbcurl=jdbc:mysql://CentOSB:3306/baizhiuseUnicode=true&characterEncoding=UTF8&serverTimezone=UTC&useSSL=false
spring.datasource.slave3.username=root
spring.datasource.slave3.password=root
spring.datasource.slave3.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.slave3.jdbcurl=jdbc:mysql://CentOSC:3306/baizhiuseUnicode=true&characterEncoding=UTF8&serverTimezone=UTC&useSSL=false
## MyBatis 配置信息
#mybatis.type-aliases-package=com.baizhi.entities
#mybatis.mapper-locations=classpath*:mappers
#创建dataSource类:
package com.baizhi.datasource;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class UserDefineDatasourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.slave1")
public DataSource slave1DataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.slave2")
public DataSource slave2DataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.slave3")
public DataSource slave3DataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public DataSource proxyDataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
@Qualifier("slave1DataSource") DataSource slave1DataSource,
@Qualifier("slave2DataSource") DataSource slave2DataSource,
@Qualifier("slave3DataSource") DataSource slave3DataSource){
DataSourceProxy proxy = new DataSourceProxy();
proxy.setDefaultTargetDataSource(masterDataSource);
Map<Object,Object> mappedDataSource=new HashMap<>();
mappedDataSource.put("master",masterDataSource);
mappedDataSource.put("slave1",slave1DataSource);
mappedDataSource.put("slave2",slave2DataSource);
mappedDataSource.put("slave3",slave3DataSource);
proxy.setTargetDataSources(mappedDataSource);
return proxy;
}
@Bean
public SqlSessionFactory sqlSessionFactory(@Qualifier("proxyDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setTypeAliasesPackage("com.baizhi.entities");
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mappers/*.xml"));
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBean.getObject();
return sqlSessionFactory;
}
@Bean
public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory, ExecutorType.BATCH);
}
@Bean
public PlatformTransactionManager platformTransactionManager(@Qualifier("proxyDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
创建代理数据源:
package com.baizhi.datasource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class DataSourceProxy extends AbstractRoutingDataSource {
private static final Logger logger= LoggerFactory.getLogger(DataSourceProxy.class);
private String masterDBKey="master";
private List<String> slaveDBKeys= Arrays.asList("slave1","slave2","slave3");
private static final AtomicInteger round=new AtomicInteger(0);
@Override
protected Object determineCurrentLookupKey() {
String dbKey=null;
OperType operType = OperTypeContextHolder.getOperType();
if(operType.equals(OperType.WRITE)){
dbKey=masterDBKey;
}else{
int value = round.getAndIncrement();
if(value < 0){
round.set(0);
}
Integer index=round.get()%slaveDBKeys.size();
dbKey=slaveDBKeys.get(index);
}
logger.debug("当前的DBkey:"+dbKey);
return dbKey;
}
}
创建读写操作的类型,由于后续进行判断:
public enum OperType {
WRITE,READ;
}
创建中间件,记录操作类型:
public class OperTypeContextHolder {
private static final ThreadLocal<OperType> OPER_TYPE_THREAD_LOCAL=new ThreadLocal<>();
public static void setOperType(OperType operType){
OPER_TYPE_THREAD_LOCAL.set(operType);
}
public static OperType getOperType(){
return OPER_TYPE_THREAD_LOCAL.get();
}
public static void clear(){
OPER_TYPE_THREAD_LOCAL.remove();
}
}
创建切面编程:
package com.baizhi.datasource;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Order(0)
@Component
public class ServiceMethodAOP {
private static final Logger logger= LoggerFactory.getLogger(ServiceMethodAOP.class);
@Around("execution(* com.baizhi.service..*.*(..))")
public Object methodInterceptor(ProceedingJoinPoint pjp){
Object result = null;
try {
MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
Method method = methodSignature.getMethod();
boolean present = method.isAnnotationPresent(SlaveDB.class);
OperType operType=null;
if(!present){
operType=OperType.WRITE;
}else{
operType=OperType.READ;
}
OperTypeContextHolder.setOperType(operType);
logger.debug("当前操作:"+operType);
result = pjp.proceed();
OperTypeContextHolder.clear();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return result;
}
}
创建一个从服务DB接口,用于标记多读操作:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD})
public @interface SlaveDB {
}
修改logback.xml:
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender" >
<encoder>
<pattern>%p %c#%M %d{yyyy-MM-dd HH:mm:ss} %m%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/userLoginFile-%d{yyyyMMdd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%p %c#%M %d{yyyy-MM-dd HH:mm:ss} %m%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 控制台输出⽇志级别 -->
<root level="ERROR">
<appender-ref ref="STDOUT" />
</root>
<logger name="org.springframework.jdbc" level="DEBUG" additivity="false">
<appender-ref ref="STDOUT" />
</logger>
<logger name="com.baizhi.dao" level="TRACE" additivity="false">
<appender-ref ref="STDOUT" />
</logger>
<logger name="com.baizhi.controller" level="DEBUG" additivity="false">
<appender-ref ref="STDOUT" />
</logger>
<logger name="com.baizhi.cache" level="DEBUG" additivity="false">
<appender-ref ref="STDOUT" />
</logger>
<logger name="com.baizhi.datasource" level="DEBUG" additivity="false">
<appender-ref ref="STDOUT" />
</logger>
</configuration>