在使用阿里云的oss存储时,我突然想到,如果同时有100个请求往oss的Bucket容器中存储文件,因为阿里云的每个容器有着同时上传次数的限制,为了避免因为这种情况导致效率的下降,所以我觉得在oss中定义一个主容器,多个子容器,来避免次数限制而无法快速上传文件的弊端(这里不讨论的money的消耗)。所以我想了一个低端,但是简单的方法,来让系统对容器进行自主选择,实现简易的负载均衡器。我使用aop的方式,在每次上传方法被调用时,执行系统的容器选择,不能够修改参数,最优选择的方式就是查看哪个容器接受的上传文件请求最少,就是用哪个容器。
public String loadBalancer(String bucketName){
Map<String,Long> map =new HashMap<>();
//获取所有的bucket
try{
List<Bucket> buckets = oss.listBuckets();
//获取所有的bucket名称
buckets.stream().map(
bucket -> {
Long size = redis.opsForList().size(bucketLoadName+":" + bucket.getName());
map.put(bucket.getName(),size);
return null;
}
).forEach(e->{});
// 获取最小的容器名称
Optional<Map.Entry<String,Long>> minEnity = map.entrySet().stream().min(Map.Entry.comparingByValue());
if(minEnity.isPresent()){
String key = minEnity.get().getKey();
log.info("将{}容器负载均衡到{}容器",bucketName,key);
//往redis中添加一个负载均衡计数器
redis.opsForList().rightPush(bucketLoadName+":"+key,key);
return key;
}
}catch (Exception e){
log.error("查询出现异常,{}",e.getMessage());
throw new RuntimeException(e.getMessage());
}
return null;
}
/**
* 除copyFile方法外的其他方法,都切面
* */
@Around("execution(* org.tamall.fileioservice.service.fileOptions.*(..)) &&" +
" !execution(* org.tamall.fileioservice.service.fileOptions.copyFile(..))" +
" && !execution(* org.tamall.fileioservice.service.fileOptions.deleteFile(..))")
public Object pointCut(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
MethodSignature signature =(MethodSignature) proceedingJoinPoint.getSignature();
//获取到切入的方法中的形参
List<String> list = Arrays.asList(signature.getParameterNames());
//获取到切入方法的实参
Object[] args = proceedingJoinPoint.getArgs();
List<Object> list1 = Arrays.asList(args);
//获取指定个参数参数的索引
int index = IntStream.range(0, list.size())
.filter(i -> "bucketName".equals(list.get(i))).findFirst().orElse(-1);
boolean b = queryExistence(CannedAccessControlList.PublicRead, (String) list1.get(index));
if(!b){
// 负载均衡后的新容器名
String s = loadBalancer((String) list1.get(index));
// 修改容器名称
args[index] = s;
}
return proceedingJoinPoint.proceed(args);
}
/**
* ordinaryUpdataFile方法,切面
* 用于删除在redis中的负载均衡计数器
* */
@AfterReturning(pointcut = "execution(* org.tamall.fileioservice.service.fileOptions.ordinaryUpdataFile(..))", returning = "result")
public void afterPointCut(JoinPoint proceedingJoinPoint, Object result) throws InterruptedException {
MethodSignature signature =(MethodSignature) proceedingJoinPoint.getSignature();
List<Class> list1 = Arrays.asList(signature.getParameterTypes());
list1.stream().filter(i -> i.equals(MultipartFile.class)).map(e->{
//获取到切入的方法中的形参
List<String> list = Arrays.asList(signature.getParameterNames());
//获取到切入方法的实参
Object[] args = proceedingJoinPoint.getArgs();
//获取指定个参数参数的索引
int index = IntStream.range(0, list.size())
.filter(i -> "bucketName".equals(list.get(i))).findFirst().orElse(-1);
// 删除redis计数器中存储的容器名,是容器名下的参数数量减一,必须是ordinaryUpdataFile携带MultipartFile参数的方法,用于区别普通上传和批量上传
redis.opsForList().rightPop(bucketLoadName+":"+args[index]);
return null;
}).forEach(e->{});
}