美文网首页
由OSS文件上传“借题发挥”

由OSS文件上传“借题发挥”

作者: 苹果tree | 来源:发表于2018-10-11 15:05 被阅读94次

前言

功能实现思优化,程序员的奋斗路漫漫。
最近项目接入了oss文件上传功能,乍一听,这容易啊,文档不已经写的明明白白了。好嘛,文档给的通用方案好使是好使,但具体问题具体分析永远是王道。下面我就谈谈具体做的优化点,还请多多指教。


思路

沿袭一贯的自问自答式思维,捋一捋这次优化的思路。
实现一个基本的上传功能需要哪些步骤?
构造上传请求->设置回调->调用上传接口。有方案就有疑问。上传线程如何被创建?又将在何时被销毁呢?这涉及到app的内存占用,不得不多想一步,于是打开debugger,翻开源码。
调试可见,触发上传操作后多了1,2,3,4,5个线程



顺藤摸瓜,果然在源码里找到了ExecutorService,创建的线程数正是5个。

public static final int DEFAULT_BASE_THREAD_POOL_SIZE = 5;
private static ExecutorService executorService =
        Executors.newFixedThreadPool(OSSConstants.DEFAULT_BASE_THREAD_POOL_SIZE, new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "oss-android-api-thread");
            }
        });

查看调用

@Override
public OSSAsyncTask<PutObjectResult> asyncPutObject(
        PutObjectRequest request, final OSSCompletedCallback<PutObjectRequest, PutObjectResult> completedCallback) {
    return internalRequestOperation.putObject(request, completedCallback);
}

 public OSSAsyncTask<PutObjectResult> putObject(
            PutObjectRequest request, final OSSCompletedCallback<PutObjectRequest, PutObjectResult> completedCallback) {
    ··· ···
return OSSAsyncTask.wrapRequestTask(executorService.submit(callable), executionContext);
}

which means,oss内部维护了一个线程池,每次触发上传将从中获得相应线程执行任务。
既然这样,那不如··· ···创建一个远端Service,处于新的进程,用于上传的线程全都依附于这个新的进程,将上传任务真正隔离,既减少了对主进程的影响,逻辑上也更清晰。

<!-- 远端服务 -->
<service
    android:name="com.dasheng.b2s.service.RemoteService"
    android:process=":remoteservice" >
</service>

于是新建:远端任务管理类RemoteOssTaskManager和远程服务类RemoteService,在onStartCommand中对不同的指令做出响应,如下

switch (msg_type) {
    case REMOTE_OSS_UPLOAD_YY:
    case REMOTE_OSS_UPLOAD_AC:
        // 开启上传
        ··· ···
        break;
    case REMOTE_CHECK_STOP:
        // 遍历任务列表,全部执行完毕则停止服务
        ··· ···
        break;
}

注意在任务全部上传完毕后停止服务,以降低进程优先级。


实现

大致结构清楚了,聚焦实现,继续思考。
构造上传请求->设置回调->调用上传接口。这个流程中,上传任务都是并发么?如此,失败的任务如何记录?如何定义重试规则?等等
为了解决这些问题,我们来到之所以成为“借题发挥”的重点。
所谓“发挥”,即如下几点;
1.定义RemoteTaskItem对象(注意同管理类解耦),包含如下属性

String fileLocalPath;//文件本地存储路径
String ossSavedPath;// oss上的存储地址
PutObjectRequest request;
int retryCount = 0;// 重试次数
long nextUploadTime = 0;// 下次可执行时间 默认0
long lastUploadTime = 0;// 上次执行时间

2.对上传任务的操作都通过消息发送给handler进行处理,保证单线程和任务串行。处理的消息类型如下:

private static final int MSG_TASK_SUCCESSED = 1;
private static final int MSG_TASK_FAILED = 2;
private static final int MSG_TASK_ADD = 3;
private static final int MSG_TASK_CHECK = 4;

3.轮询任务列表,遍历列表检查任务状态(下次可执行时间)
   增加无网延迟检查
   遍历任务列表,找到最近时间可执行的任务,等待或直接执行
   任务执行中延迟10s检查当前任务执行状态,超过30min没执行完任务挂起
流程如下

private long uploadAndCheck() {
        if (!hasTask()) {   
            // 没有待执行任务 退出轮询
            ··· ···
            return 0;
        }
        if (网络状况异常) { 
            return 30000; // 无网状态重试时间30s
        }
        // 当前任务超过最长等待时间 
        long currTime = System.currentTimeMillis();
        if (mCurrTaskItem != null) {
            if (mCurrTaskItem.lastUploadTime + MAX_EXECUTING_TIME < currTime) {
                // 任务挂起或移除
                ··· ···
            } else {
                // 任务执行中
                return 10000; // 每10s检查当前任务执行状态
            }
        }
        // 遍历检查任务队列
        long nextUploadTime = Long.MAX_VALUE;
        for (RemoteTaskItem item : mRemoteTasks) {
            if (item.nextUploadTime <= currTime) {// 任务初次执行或已为可执行状态
                // 执行该任务并返回
                ··· ···
                return 10000; // 任务执行中,每10s检查当前任务执行状态
            } else if (nextUploadTime > item.nextUploadTime) {
                nextUploadTime = item.nextUploadTime;
            }
        }
        return nextUploadTime - currTime;
    }

下面重点看一看handler回调方法,主要流程都蕴含其中:

@Override
    public boolean handleMessage(Message msg) {
        Object obj = msg.obj;
        switch (msg.what) {
            case MSG_TASK_ADD:
                // 新任务加入上传队列
                ··· ···
                break;
            case MSG_TASK_CHECK:
                // 轮询
                long ms = uploadAndCheck();
                if (ms > 0) {
                    sendUploadAndCheckAction(ms);
                }
                break;
            case MSG_TASK_SUCCESSED:
                    // 上传成功 当前任务置空并从列表中移除
                    ··· ···
                break;
            case MSG_TASK_FAILED:
                // 上传失败 当前执行任务置空若超过最大重试次数或错误不可修复移除任务
                ··· ···
                break;
        }
        return true;
    }

最后

我们写一个功能时除了逻辑层面,必须有业务层面的考量。就oss上传功能而言,oss自身提供了retry接口,支持重试,但是如何设置等待时间,对因网络情况,文件过大或服务器原因等造成的失败如何区别处理,分配不同的优先级?多想一步总会多条思路。

相关文章

  • 由OSS文件上传“借题发挥”

    前言 功能实现思优化,程序员的奋斗路漫漫。最近项目接入了oss文件上传功能,乍一听,这容易啊,文档不已经写的明明白...

  • 阿里oss文件分片上传

    OSS文件分片上传 依赖 基础参数dto 具体上传方法 小文件上传 大文件上传,分片oss自己处理 处理逻辑:前段...

  • 阿里云开发日志

    OSS备份 ./ossutil64 cp {上传的文件} oss://{bucket名称}/{存储的文件名} --...

  • http jar 冲突导致oss上传失败

    使用oss上传文件到阿里云oss报错: While executing [invoke] encountered ...

  • iOS OSS上传文件问题

    iOS OSS上传文件问题 最近接入OSS SDK做资源上传到OSS,期间遇到的一些问题,做一下记录 1. OSS...

  • Vue上传文件到OSS并校验文件的md5值

    Vue上传文件到OSS并校验文件的md5值 最近在做的项目中需要在Vue中上传文件到阿里云OSS,还需要在上传之前...

  • OSS上传文件

    阿里云OSS上传文件[https://help.aliyun.com/document_detail/32058....

  • 小程序上传图片跳坑

    背景:小程序上传图片,调用后台接口,后台接口获得文件后将该文件上传到oss上,最后返回给小程序前端oss图片路径。...

  • 客户端上传文件失败

    背景 小程序上传图片,调用后台接口,后台接口获得文件后将该文件上传到oss上,最后返回给小程序前端oss图片路径。...

  • OSS文件同步(备份)方案

    最近更新:2017.04.27 pm, by very80 需求 文件上传到OSS并转码完成后,由程序及时将预览文...

网友评论

      本文标题:由OSS文件上传“借题发挥”

      本文链接:https://www.haomeiwen.com/subject/xudyaftx.html