为什么要强制主库查询?
对于必须要拿到最新结果的请求,强制将其发到主库上。比如,在一个交易平台上,卖家发布商品以后,马上要返回主页面,看商品是否发布成功。那么,这个请求需要拿到最新的结果,就必须走主库。>对于可以读到旧数据的请求,才将其发到从库上。在这个交易平台上,买家来逛商铺页面,就算晚几秒看到最新发布的商品,也是可以接受的。那么,这类请求就可以走从库。
方法一:
public User getByIdFromMaster(Long id) {
return Optional.ofNullable(id).map(uid -> {
User user = null;
HintManager.clear();
try (HintManager hintManager = HintManager.getInstance()) {
hintManager.setMasterRouteOnly();
user = userMapper.getUserById(uid);
}
return user;
}).orElse(null);
}
方法二:
使用 AOP
思想,自定义注解 DBMaster
,代码如下:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface DBMaster {
}
在定义注解 Handler,代码如下:
@Slf4j
@Component
@Aspect
public class DBMasterHandler {
@Around("execution(* cn.beckbi.service.impl.*.*(..))")
public Object master(ProceedingJoinPoint joinPoint){
Object[] args = joinPoint.getArgs();
Object ret = null;
log.info(joinPoint.toShortString());
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
DBMaster dBMaster = method.getAnnotation(DBMaster.class);
HintManager hintManager = null;
try {
if (Objects.nonNull(dBMaster)) {
HintManager.clear();
hintManager = HintManager.getInstance();
hintManager.setMasterRouteOnly();
}
ret = joinPoint.proceed(args);
}catch (Exception ex){
log.error("exception error",ex);
}catch (Throwable ex2){
log.error("Throwable",ex2);
}finally {
if (Objects.nonNull(dBMaster) && Objects.nonNull(hintManager)) {
hintManager.close();
}
}
return ret;
}
}
使用注解,代码如下:
@DBMaster
public User getById(Long id) {
return this.userMapper.selectByid(id);
}
注意这里的一个方法,HintManager.getInstance()
, 进入方法内部,如下:
public static HintManager getInstance() {
// 直接 new 了一个强制路由管理器
HintManager result = new HintManager();
// 注意 HintManagerHolder 是一个 ThreadLocal 类型
// 所以在使用完之后一定一定要记得把值 remove 掉,否则会出现意想不到的错误哦
HintManagerHolder.setHintManager(result);
return result;
}
public static void setHintManager(final HintManager hintManager) {
// 如果你使用完不清空,这里就会帮你拦截直接抛异常,说你上面存在一个值在使用,没有被销毁。
Preconditions.checkState(null == HINT_MANAGER_HOLDER.get(), "HintManagerHolder has previous value, please clear first.");
HINT_MANAGER_HOLDER.set(hintManager);
}