1、SpringBoot整合mysql配置多数据源
注意事项:
1、 引入dynamic-datasource-spring-boot-starter依赖
<!--启用@DS实现数据源切换-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>2.5.4</version>
</dependency>
2、在yml文件中配置数据库信息,并区分主从数据库
spring:
datasource:
#配置hikari连接池
hikari:
minimum-idle: 4
maximum-pool-size: 16
connection-timeout: 10000
idle-timeout: 30000
connection-init-sql: set names utf8mb4
#动态数据源配置
dynamic:
#主数据源,默认启用
primary: reader-db
datasource:
#数据源1
reader-db:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/guli?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8
username: root
password: 123456
#数据源2
writer-db:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/myshop?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8
username: root
password: 123456
3、在mapper文件中采用注解的方式使用
@DS("reader-db")
List<EduTeacher> selectAll();
@DS("writer-db")
void insertBatch(List<EduTeacher> teacherLasts);
2、 @Retryable重试机制
在很多业务场景中,我们需要用重试机制。比如说在微服务中,服务之间的调用有时候会因为网络波动导致调用失败,这时候代码中进行失败重试就ok了,当然这种重试机制通常会和异步结合使用,否则因为重试导致响应时间变长,这肯定是不行的。至于异步,无论是spring自带的异步机制,还是整合消息中间件。具体异步方式这里不重点讲。当然我们也可以自己用代码逻辑实现重试,不过这种方式不统一,要写一些非业务代码,对代码的侵入比较严重。既然spring已经支持重试,我们就没必要自己重复造轮子了。
- 添加依赖 前面也说到,我们要使用spring自带的重试机制,所以需要在项目的依赖加上spring重试的依赖包。
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
//重试 最多重试15次,每2秒重试1次;maxAttempts 最大重试次数 delay 重试间隔时间 multiplier 间隔时间倍数
@Retryable(value = Exception.class, maxAttempts = 2, backoff = @Backoff(delay = 300000, multiplier = 1))
public void sendDingding() throws Exception {
String startTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(System.currentTimeMillis()));
log.info("任务开始: " + startTime);
try {
operationService.queryOperation();
} catch (Exception e) {
e.printStackTrace();
log.error(e.getLocalizedMessage());
this.dingDingTransfer(DingToken, jsonObject);
throw new Exception(e.getLocalizedMessage());
}
String endTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(System.currentTimeMillis()));
log.info("发送微信公众号信息任务结束: " + endTime);
}
private int i = 1;
@Recover
public void recover(Exception e) {
log.error(e.getLocalizedMessage());
}
public String dingDingTransfer(String DingToken, JSONObject jsonParam) {
Unirest.setTimeouts(0, 0);
HttpResponse<String> response = null;
try {
String tokenUrl = DingUrl+"?token="+DingToken;
log.info(tokenUrl);
response = Unirest.post(tokenUrl)
.header("Content-Type", "application/json")
.body(jsonParam.toString())
.asString();
log.info(JSONUtil.toJsonStr(response));
return response.toString();
} catch (UnirestException e) {
log.error(e.getLocalizedMessage());
}
return null;
}
- 注意事项
- 1、由于aspect机制,不要在同一个类中调用加上@Retryable注解的方法,会使aspect增强失效,那么retry当然也会失效。
- 2、在实际项目中,如果是为了避免网络波动而加上的重试机制,在重试的时候需要加上延迟,也就是后面的重试时间间隔设置大一点,这样效果可能好一点,否则你每隔1秒重试一次,这样的重试很多都是徒劳的。上面的代码第一次重试是延迟3秒,第二次6秒,multiplier 设置了延迟翻倍。 3、@Recover注解是假如经过重试之后,还是调用失败了,这时候就需要发短信报警了,开发,运维就要忙起来了。我们可以根据自己的业务场景来发送短信或者邮件通知相关开发和运维。当然你也可以简单记录下日志,不做其他操作。 4、@Recover生效的前提是在@Retryable注解的方法和@Recover注解的方法的返回值保持一致。否则会在报错日志中看到这句话org.springframework.retry.ExhaustedRetryException: Cannot locate recovery method。
3、SpringBoot整合redis
redis的java客户端有jedis和redisTemplete两种方式,个人偏向于使用jedis的方式
- 1、添加依赖
<!--redis连接-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.0.0</version>
</dependency>
- 2、连接方式:
- 2.1、单个redis
public Jedis singleRedis() {
Jedis jedis = new Jedis("127.0.0.1",6379);
//jedis.auth(password);
// 如果redis是集群模式会报错:JedisMovedDataException , 需要采用jedisCluster来进行连接
jedis.set("first", "Hello single Jedis, finish study-work of Redis in one day");
System.err.println(jedis.get("first"));
return jedis;
}
- 2.2、连接redis集群
public class RedisClusterUtil {
private static Logger logger = LoggerFactory.getLogger(RedisClusterUtil.class);
/**
* 集群地址
*/
@Value("${redis.host}")
private String host;
@Value("${redis.user}")
private String user;
@Value("${redis.password}")
private String password;
private JedisCluster jedis;
/* public RedisClusterUtil(String host, String user, String password) {
this.host = host;
this.user = user;
this.password = password;
}*/
private void init() throws Exception {
Set<HostAndPort> hostAndPortsSet = new HashSet<HostAndPort>();
String[] hosts = host.split(",");
for (String s : hosts) {
hostAndPortsSet.add(new HostAndPort(s.split(":")[0], Integer.valueOf(s.split(":")[1])));
}
// Jedis连接池配置
class JedisPoolConfig extends GenericObjectPoolConfig {
public JedisPoolConfig() {
setTestWhileIdle(true);
setMinEvictableIdleTimeMillis(60000);
setTimeBetweenEvictionRunsMillis(30000);
setNumTestsPerEvictionRun(-1);
setMaxIdle(10);
// 最大连接数, 默认8个
setMaxTotal(100);
// 最小空闲连接数, 默认0
setMinIdle(0);
// 对拿到的connection进行validateObject校验
setTestOnBorrow(true);
}
}
// 设置用户名密码
JedisClientConfig clietnConfig = new JedisClientConfig() {
@Override
public String getUser() {
return user;
}
@Override
public String getPassword() {
return password;
}
};
jedis = new JedisCluster(hostAndPortsSet, clietnConfig, 3, new JedisPoolConfig());
logger.info("redis 集群已连接");
}
public JedisCluster getJedis() {
if (null == jedis) {
try {
init();
} catch (Exception e) {
logger.info("连接redis失败", e);
}
}
return jedis;
}
public void close() {
if (null != jedis) {
jedis.close();
}
}
}
- 3、具体使用:
@Autowired
RedisClusterUtil redisUtil;
JedisCluster jedisCluster = null;
jedisCluster = redisUtil.getJedis();
jedisCluster.close();
log.info("关闭redis服务连接");