基于redis实现阿里云oss存储的简易负载均衡器

时间:2024-01-20 22:54:55

在使用阿里云的oss存储时,我突然想到,如果同时有100个请求往oss的Bucket容器中存储文件,因为阿里云的每个容器有着同时上传次数的限制,为了避免因为这种情况导致效率的下降,所以我觉得在oss中定义一个主容器,多个子容器,来避免次数限制而无法快速上传文件的弊端(这里不讨论的money的消耗)。所以我想了一个低端,但是简单的方法,来让系统对容器进行自主选择,实现简易的负载均衡器。我使用aop的方式,在每次上传方法被调用时,执行系统的容器选择,不能够修改参数,最优选择的方式就是查看哪个容器接受的上传文件请求最少,就是用哪个容器。

基于redis实现阿里云oss存储的简易负载均衡器_oss

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->{});
    }

基于redis实现阿里云oss存储的简易负载均衡器_redis_02