葡京在线开户SDWebImage源码解读

摘要:
EagleEye作为阿里集团有名的链路跟踪系统,其本身工作便非以贸易链路上,但可监控在都集团的链路状态,特别是在中间件的长距离调用上,覆盖了集团绝大部分的景,在题目排查和固化及发表在英雄的来意,保障了各个系统的安居乐业,为一切技术集团由赢就会战役保驾护航。

SDWebImage是一个完美的开源的老三方库,它具备以下职能:
提供UIImageView的一个分拣,以支撑网络图片的加载与缓存管理

背景 
双十一一直是阿里巴巴集团每年要由之同样街大战役。要打赢就会战役,技术上,不仅仅是几只应用、几只网的从事,也无是有点个出+多少个测试就能够形成的从,而是需要各个大体系并作战、每个应用各司其职、技术人员通力合作才能够取最后之大胜。

  1. 一个异步的图片加载器
  2. 一个异步的内存+磁盘图片缓存
  3. 支持GIF图片
  4. 支持WebP图片
  5. 后台图片解压缩处理
  6. 管与一个URL的图样未被下载多次
  7. 保险虚假的URL不会见让反复加载
  8. 保证下载和缓存时,主线程不吃死

EagleEye作为阿里集团资深的链路跟踪系统,其自己工作就是非以交易链路上,但可监控着都集团的链路状态,特别是在中间件的长距离调用上,覆盖了集团绝大部分之光景,在问题排查和固化及表达在伟大的打算,保障了各个系统的安居,为总体技术团队于赢就会战役保驾护航。

SDWebImage的基本类即是三独:

葡京在线开户 1

  • SDImageCache
    它们至关重要负责图片的缓存

  • SDWebImageDownloader
    它主要担负图片的下载

  • SDWebImageManager
    它们根本承担图片的下载操作的治本。而且我们常采取的诸如UIImageView+WebCache等控件的分类且是因SDWebImageManager对象的。该目标将一个下载器和一个图缓存绑定在一块儿,并对外提供个别只就读属性来获得她

图1 EagleEye系统整体状况

下我们就算因为我们平常之运也输入,来针对SDWebImage的源码进行解析。

身临其境两年集团工作及局面一直保在快速的滋长,纵深上,交易量屡攀新大,双十一零点的贸易峰值为还同次等刷新了历史;横向上,集团涉及的行业以及领域呢不断的进行,各行各业在时时刻刻进入阿里(高德、优酷、友盟及大麦等等),共同前进。

诚如我们使用SDWebImage用得太多的即使是这函数
- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options

给数规模持续多,如何应对在工作快速发展之背景下网采集的多寡量级的穿梭提高,如何以一发老之数据规模面前保障EagleEye自身工作的安宁,成为EagleEye今年双十一面临的英雄挑战。

options参数是独NS_OPTIONS类型,其中有12独参数,你可以根据你的种求开展精选,具体每个参数的来意我不怕不一一解释了,因为作者注已经说得挺理解。

葡京在线开户 2

脚我们尽管从这个函数入口开始同步一步分析。
查源码可以发现,最终函数掉于是了
其一函数

祈求2 EagleEye支持的事体情况

- (void)sd_internalSetImageWithURL:(nullable NSURL *)url
                  placeholderImage:(nullable UIImage *)placeholder
                           options:(SDWebImageOptions)options
                      operationKey:(nullable NSString *)operationKey
                     setImageBlock:(nullable SDSetImageBlock)setImageBlock
                          progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                         completed:(nullable SDExternalCompletionBlock)completedBlock {
    NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]);
    //如果改队列已经存在,则取消下载
    [self sd_cancelImageLoadOperationWithKey:validOperationKey];
    objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);  //保存url
    //如果不需要延迟加载默认图片,则先显示默认图片
    if (!(options & SDWebImageDelayPlaceholder)) {
        dispatch_main_async_safe(^{
            [self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
        });
    }

    if (url) {
        // check if activityView is enabled or not
        if ([self sd_showActivityIndicatorView]) {
            [self sd_addActivityIndicator];
        }
        //1-------开始下载
        __weak __typeof(self)wself = self;
        id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
           //下载完成的回调
            __strong __typeof (wself) sself = wself;
            [sself sd_removeActivityIndicator];
            //如果视图已经被销毁了,则直接return
            if (!sself) {
                return;
            }
          //2--------这就是一个宏,切换到主线程
            dispatch_main_async_safe(^{
                if (!sself) {
                    return;
                }
                //如果image下载成功且设置了不自动给image添加上去,就直接把数据传给completeBlock处理
                if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) {
                    completedBlock(image, error, cacheType, url);
                    return;
                } else if (image) {
                    [sself sd_setImage:image imageData:data basedOnClassOrViaCustomSetImageBlock:setImageBlock];
                    [sself sd_setNeedsLayout];
                } else {
                    //没下载成功,如果设置了延迟加载默认图片,则设置默认图片
                    if ((options & SDWebImageDelayPlaceholder)) {
                        [sself sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
                        [sself sd_setNeedsLayout];
                    }
                }
                if (completedBlock && finished) {
                    completedBlock(image, error, cacheType, url);
                }
            });
        }];
        //3-------将operation保存起来,并且如果存在的话会取消
        [self sd_setImageLoadOperation:operation forKey:validOperationKey];
    } else {
        dispatch_main_async_safe(^{
            [self sd_removeActivityIndicator];
            if (completedBlock) {
                NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
                completedBlock(nil, error, SDImageCacheTypeNone, url);
            }
        });
    }
}

全链路压测一直是阿里巴巴集团保持双十一的怪杀器之一,通过在线上环境全真模拟双十一当天底流量来视察各个应用系统的负荷能力。EagleEye在全链路压测中当了主要之义务,透传压测标记实现流量的分,压测数据的征集和见用以帮助业务方的开支同学发现同定位系统的问题。所以,保障全链路压测也是EagleEye的显要使命之一。 
今年的EagleEye 
无论是常态、全链路压测或者是双十一当天,EagleEye面临的重点问题是怎样保障自身系统于海量数据冲击下的安居乐业,以及哪些更快之展现各个系统的状态与重新好的佑助开发同学发现及定位问题。今年,EagleEye通过了同等密密麻麻改造升级提高了系的安居,实现了再度好重快之帮助业务方定位与排查问题。

如出一辙步一步看下要不行简单的,继续向下看。

葡京在线开户 3

SDWebImageManager

脚还省标记1底代码,这是SD中极其紧要的一个类SDWebImageManager,它负责管理下载和缓存

//标记1---------------
- (id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url
                                     options:(SDWebImageOptions)options
                                    progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                   completed:(nullable SDInternalCompletionBlock)completedBlock {
    // Invoking this method without a completedBlock is pointless
    NSAssert(completedBlock != nil, @"If you mean to prefetch the image, use -[SDWebImagePrefetcher prefetchURLs] instead");

    if ([url isKindOfClass:NSString.class]) {
        url = [NSURL URLWithString:(NSString *)url];

    if (![url isKindOfClass:NSURL.class]) {
        url = nil;
    }
    //前面主要是对URL进行一个转换,防止了边界情况

    //加上block以在后面修改
    __block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];
    //加上weak防止引用循环
    __weak SDWebImageCombinedOperation *weakOperation = operation;

    BOOL isFailedUrl = NO;
    if (url) {
        //因为有可能同时有多个线程操作,所以需要线程安全访问
        @synchronized (self.failedURLs) {
            //有个数组保存了所有请求失败过的URL,所以判断一下是否是请求失败过的url
            isFailedUrl = [self.failedURLs containsObject:url];
        }
    }
    //如果url不正确或者是已经访问失败过的url且没有设置了重新请求失败的url
    //直接返回
    if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {
        [self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil] url:url];
        return operation;
    }

    @synchronized (self.runningOperations) {
        [self.runningOperations addObject:operation];
    }
    NSString *key = [self cacheKeyForURL:url];//根据url设置key

  //a------根据缓存策略进行查找
    operation.cacheOperation = [self.imageCache queryCacheOperationForKey:key done:^(UIImage *cachedImage, NSData *cachedData, SDImageCacheType cacheType) {
        if (operation.isCancelled) {
            [self safelyRemoveOperationFromRunning:operation];
            return;
        }

        //如果没有找到缓存的image或者找到了image但是设置了需要更新缓存。    且delegate没有实现回调方法。或者delegate实现了回调方法但是返回yes
        //也就是说默认情况下没有找到缓存图片都是需要下载的
        if ((!cachedImage || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) {
            if (cachedImage && options & SDWebImageRefreshCached) {
               // 如果缓存图片找到且需要更新缓存则先把缓存的数据传过去
                [self callCompletionBlockForOperation:weakOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
            }

            // 根据用户的的设置设置下载的一些参数配置
            SDWebImageDownloaderOptions downloaderOptions = 0;
            if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority;
            if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload;
            if (options & SDWebImageRefreshCached) downloaderOptions |= SDWebImageDownloaderUseNSURLCache;
            if (options & SDWebImageContinueInBackground) downloaderOptions |= SDWebImageDownloaderContinueInBackground;
            if (options & SDWebImageHandleCookies) downloaderOptions |= SDWebImageDownloaderHandleCookies;
            if (options & SDWebImageAllowInvalidSSLCertificates) downloaderOptions |= SDWebImageDownloaderAllowInvalidSSLCertificates;
            if (options & SDWebImageHighPriority) downloaderOptions |= SDWebImageDownloaderHighPriority;
            if (options & SDWebImageScaleDownLargeImages) downloaderOptions |= SDWebImageDownloaderScaleDownLargeImages;

            if (cachedImage && options & SDWebImageRefreshCached) {
                // force progressive off if image already cached but forced refreshing
                downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload;
                // ignore image read from NSURLCache if image if cached but force refreshing
                downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse;
            }
            //b---------通过SDWebImageDownloader类开始下载,
            SDWebImageDownloadToken *subOperationToken = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *downloadedData, NSError *error, BOOL finished) {
                 //下载完成的回调
                __strong __typeof(weakOperation) strongOperation = weakOperation;   //防止operation提前被释放,需要强引用一下,但是这不会造成引用循环,改引用会自动释放
                if (!strongOperation || strongOperation.isCancelled) {
                   //如果已经取消则什么都不做
                } else if (error) {
                    [self callCompletionBlockForOperation:strongOperation completion:completedBlock error:error url:url];

                    if (   error.code != NSURLErrorNotConnectedToInternet
                        && error.code != NSURLErrorCancelled
                        && error.code != NSURLErrorTimedOut
                        && error.code != NSURLErrorInternationalRoamingOff
                        && error.code != NSURLErrorDataNotAllowed
                        && error.code != NSURLErrorCannotFindHost
                        && error.code != NSURLErrorCannotConnectToHost) {
                        @synchronized (self.failedURLs) {
                          //如果不是因为网络等一些原因引起的错误,就把url加入failedURLS中
                            [self.failedURLs addObject:url];
                        }
                    }
                }
                else {
                    //正常下载的情况
                    if ((options & SDWebImageRetryFailed)) {
                        //如果是重新下载的之前一个标示了错误的URL,就把它移除
                        @synchronized (self.failedURLs) {
                            [self.failedURLs removeObject:url];
                        }
                    }

                    BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly);

                    if (options & SDWebImageRefreshCached && cachedImage && !downloadedImage) {
                        // 如果是前面那种情况,就不需要调用completeBlock了,因为前面已经调用了
                    } else if (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) {

                //如果下载成功且  不是GIF图片或者用户设置了即使是GIF也可以转换且       下载的图片是GIF但是用户实现了对图片就行转换的方法                   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
                            UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url];

                            if (transformedImage && finished) {
                                BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];
                                // 如果转换成功imageData就存一个nil
                                [self.imageCache storeImage:transformedImage imageData:(imageWasTransformed ? nil : downloadedData) forKey:key toDisk:cacheOnDisk completion:nil];
                            }
                            //调用completeBlock把数据传回去
                            [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:transformedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
                        });
                    } else {
                        //下载完成,存储正常图片
                        if (downloadedImage && finished) {
                            [self.imageCache storeImage:downloadedImage imageData:downloadedData forKey:key toDisk:cacheOnDisk completion:nil];
                        }
                        //即使下载没有完成也需要把数据传过去,也需要把数据传过去以实现渐变效果,但是只需要在完成后再存储
                        [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:downloadedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
                    }
                }
                //完成后把operation移除
                if (finished) {
                    [self safelyRemoveOperationFromRunning:strongOperation];
                }
            }];
            //设置operation的取消Block
            operation.cancelBlock = ^{
                [self.imageDownloader cancel:subOperationToken];
                __strong __typeof(weakOperation) strongOperation = weakOperation;
                [self safelyRemoveOperationFromRunning:strongOperation];
            };
        } else if (cachedImage) {
            //如果从缓存中找到了image且不需要更新,则直接把数据传过去,注意前面不一样的就是先把数据传了过去再开启下载,下载完成后还要更新缓存,再把数据传一遍
            __strong __typeof(weakOperation) strongOperation = weakOperation;
            [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
            [self safelyRemoveOperationFromRunning:operation];
        } else {
            // 如果缓存中没有找到,且用户实现了[self.delegate imageManager:self shouldDownloadImageForURL:url]方法并返回NO
            __strong __typeof(weakOperation) strongOperation = weakOperation;
            [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:nil data:nil error:nil cacheType:SDImageCacheTypeNone finished:YES url:url];
            [self safelyRemoveOperationFromRunning:operation];
        }
    }];

    return operation;
}

//标记2
#ifndef dispatch_main_async_safe
#define dispatch_main_async_safe(block)\
    if (strcmp(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL), dispatch_queue_get_label(dispatch_get_main_queue())) == 0) {\
        block();\
    } else {\
        dispatch_async(dispatch_get_main_queue(), block);\
    }
#endif
//这里只是使用了一个宏定义来把代码交到主队列去执行

再度探标记3怎么样保存的operation

//标记3
- (void)sd_setImageLoadOperation:(nullable id)operation forKey:(nullable NSString *)key {
    if (key) {
        [self sd_cancelImageLoadOperationWithKey:key];
        if (operation) {
            SDOperationsDictionary *operationDictionary = [self operationDictionary];
            operationDictionary[key] = operation;
        }
    }
}

- (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key {
    // Cancel in progress downloader from queue
    SDOperationsDictionary *operationDictionary = [self operationDictionary];
    id operations = operationDictionary[key];
    if (operations) {
        if ([operations isKindOfClass:[NSArray class]]) {
            for (id <SDWebImageOperation> operation in operations) {
                if (operation) {
                    [operation cancel];
                }
            }
        } else if ([operations conformsToProtocol:@protocol(SDWebImageOperation)]){
            [(id<SDWebImageOperation>) operations cancel];
        }
        [operationDictionary removeObjectForKey:key];
    }
}
//这两个方法很简单,就是每新添一个operation都会先看该opertion是否存在,如果存在就先取消并且删除,再重新添加进去

图3 系统架构图

SDImageCache

标记a,就是由缓存中读取了,所以尽管就此到了SDImageCache类,该类是特别负责缓存的

- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock {
    if (!key) {
        if (doneBlock) {
            doneBlock(nil, nil, SDImageCacheTypeNone);
        }
        return nil;
    }

    // 先从内存中查找
    UIImage *image = [self imageFromMemoryCacheForKey:key];
    if (image) {
        NSData *diskData = nil;
        if ([image isGIF]) {
            //如果是GIF图片,就要去缓存路径中查找,具体查找方式就不展开说了,看看源码很容易理解
            diskData = [self diskImageDataBySearchingAllPathsForKey:key];
        }
        if (doneBlock) {
            doneBlock(image, diskData, SDImageCacheTypeMemory);
        }
        return nil;
    }

    NSOperation *operation = [NSOperation new];
    //在自己创建的队列中异步执行
    dispatch_async(self.ioQueue, ^{
        if (operation.isCancelled) {
            // 如果operation已经取消,就直接返回,不用传值回去
            return;
        }
        //加入自动释放池。因为这些图片数据可能很大,有点占内存,加入自动释放池可以尽早释放这些对象
        //以免占用大量内存,因为在子线程中默认是不开启runloop的,所以就没有自动释放池,
        //只能等到线程结束时才会释放对象

        @autoreleasepool {
            NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key];
            UIImage *diskImage = [self diskImageForKey:key];
            if (diskImage && self.config.shouldCacheImagesInMemory) {
                NSUInteger cost = SDCacheCostForImage(diskImage);
                [self.memCache setObject:diskImage forKey:key cost:cost];
            }

            if (doneBlock) {
                //切换回主线程再传值,方便用户直接对UI进行操作
                dispatch_async(dispatch_get_main_queue(), ^{
                    doneBlock(diskImage, diskData, SDImageCacheTypeDisk);
                });
            }
        }
    });

    return operation;
}

计算能力下沉 
初的EagleEye在链路跟及数额统计还是因明细日志完成,实时采集全量的密切日志并以流计算吃举行聚合,随着业务量的增强,日志的数据量也于激烈上升,计算量也随之线性增长,资源消耗比较高。而且每当备链路压测或者大促期间,日志量会发生明显的峰值,极有或引致计算集群系统过载或者数延迟还发生或造成数据的遗失。

SDWebImageDownloader

标记b,如果缓存没有要用更新缓存,这时候就需打开下载了,也即因故到了我们的SDWebImageDownloader类。

- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
                                                   options:(SDWebImageDownloaderOptions)options
                                                  progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                                 completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {
    __weak SDWebImageDownloader *wself = self;
  //这个方法一进来就调用了另一个方法,并且传了一个返回值为
  //SDWebImageDownloaderOperation类型的block过去,那个方法其实就是
  //管理operation并创建SDWebImageDownloadToken,具体后面再来分析
  //先看看这里block中做了什么吧
    return [self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^SDWebImageDownloaderOperation *{
        __strong __typeof (wself) sself = wself;
        NSTimeInterval timeoutInterval = sself.downloadTimeout;
        if (timeoutInterval == 0.0) {
            timeoutInterval = 15.0;
        }

        //创建缓存,并且默认是不进行URLcache的,除非用户设置了SDWebImageDownloaderUseNSURLCache
       //因为默认已经开启了图像缓存,就没必要再对请求缓存了
        NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:timeoutInterval];
        request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);   
        管理coockie//是否
        request.HTTPShouldUsePipelining = YES;  //开启管道下载
        if (sself.headersFilter) {
            //如果用户设置了请求头就使用用户自定义的否则就是用默认的
            request.allHTTPHeaderFields = sself.headersFilter(url, [sself.HTTPHeaders copy]);
        }
        else {
            request.allHTTPHeaderFields = sself.HTTPHeaders;
        }
        //创建SDWebImageDownloaderOperation
        SDWebImageDownloaderOperation *operation = [[sself.operationClass alloc] initWithRequest:request inSession:sself.session options:options];
        operation.shouldDecompressImages = sself.shouldDecompressImages;
        //设置URL验证
        if (sself.urlCredential) {
            operation.credential = sself.urlCredential;
        } else if (sself.username && sself.password) {
            operation.credential = [NSURLCredential credentialWithUser:sself.username password:sself.password persistence:NSURLCredentialPersistenceForSession];
        }

        if (options & SDWebImageDownloaderHighPriority) {
            operation.queuePriority = NSOperationQueuePriorityHigh;
        } else if (options & SDWebImageDownloaderLowPriority) {
            operation.queuePriority = NSOperationQueuePriorityLow;
        }

        [sself.downloadQueue addOperation:operation];
        if (sself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {
            // 如果是后进先出的方式的话要添加依赖,这样可以保证每次都是最后一个添加进来的队列先执行
            [sself.lastAddedOperation addDependency:operation];
            sself.lastAddedOperation = operation;
        }
        //可以看出这里主要是在创建一个request并设置各种参数,然后把它保存到operation中传递过去
        return operation;
    }];
}

哼吧,我们后续看传过来的是法子干了数什么

- (nullable SDWebImageDownloadToken *)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock
                                           completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock
                                                   forURL:(nullable NSURL *)url
                                           createCallback:(SDWebImageDownloaderOperation *(^)())createCallback {
    // The URL will be used as the key to the callbacks dictionary so it cannot be nil. If it is nil immediately call the completed block with no image or data.
    if (url == nil) {
        if (completedBlock != nil) {
            completedBlock(nil, nil, nil, NO);
        }
        return nil;
    }

    __block SDWebImageDownloadToken *token = nil;

    dispatch_barrier_sync(self.barrierQueue, ^{
        //先看看缓存中是否有对应的operation,没有的话就使用前面block返回的
        SDWebImageDownloaderOperation *operation = self.URLOperations[url];
        if (!operation) {
            operation = createCallback();
            self.URLOperations[url] = operation;

            __weak SDWebImageDownloaderOperation *woperation = operation;
            operation.completionBlock = ^{
             //设置完成的回调,就是移除这个operation
              SDWebImageDownloaderOperation *soperation = woperation;
              if (!soperation) return;
              if (self.URLOperations[url] == soperation) {
                  [self.URLOperations removeObjectForKey:url];
              };
            };
        }
        //创建SDWebImageDownloadToken
        id downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock];

        token = [SDWebImageDownloadToken new];
        token.url = url;
        token.downloadOperationCancelToken = downloadOperationCancelToken;
    });

    return token;
}

哼了,走至此处,整个工艺流程都算多了。累了邪?

。。。

哼吧,我也累了

。。。

苏一下
。。。

可还并未竣工

。。。。

复休息一下
。。。。
。。。
。。

哼了,休息够了起上吧!!!

乃意识并未,这里创办了请求却没察觉呼吁发送,只是将她上加至了一个自定义的operation中。所以这边还要起一个生死攸关之知识点啦,那就是起定义NSOperation,很明朗这里的SDWebImageDownloaderOperation就是为图片下载而量身定制。下面我们虽来看望这SDWebImageDownloaderOperation的实现吧。

- (nonnull instancetype)initWithRequest:(nullable NSURLRequest *)request
                              inSession:(nullable NSURLSession *)session
                                options:(SDWebImageDownloaderOptions)options {
    if ((self = [super init])) {
        _request = [request copy];
        _shouldDecompressImages = YES;
        _options = options;
        _callbackBlocks = [NSMutableArray new];
        _executing = NO;
        _finished = NO;
        _expectedSize = 0;
        _unownedSession = session;
        responseFromCached = YES; // Initially wrong until `- URLSession:dataTask:willCacheResponse:completionHandler: is called or not called
        _barrierQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderOperationBarrierQueue", DISPATCH_QUEUE_CONCURRENT);
    }
    return self;
}

初始化方法其实没什么好讲的,就是装各种性能,具体每个属性的意图可以看作者注释,就不一一介绍了。

自定义operation最关键的步子?

  • 倘是匪并作班
    直接促成main方法,在main方法中实践打定义任务,但是要留意少接触:
    1.创建释放池
    2.没错响应取消事件。
  • 并作班
    自定义并发的NSOperation需要以下步骤:
    1.start方式:该措施必须实现,
    2.main:该方法可选取,如果您以start方法吃定义了卿的天职,则是方式就好免落实,但普通为代码逻辑清晰,通常会以拖欠方法中定
    义自己的任务
    3.isExecuting isFinished
    根本作用是于线程状态改变时,产生适当的KVO通知
    4.isAsynchronous(代替之前使用的IsConcurrent) :必须盖并返YES;

好了,废话不多说,直接来拘禁start方法吧

- (void)start {
      //如果已经取消了,就重置该队列
    @synchronized (self) {
        if (self.isCancelled) {
            self.finished = YES;
            [self reset];
            return;
        }
//这是作者自定义的宏,就是表示如果是iOS或者tvOS,因为这两个平台都包含UIKit
#if SD_UIKIT
        Class UIApplicationClass = NSClassFromString(@"UIApplication");
        BOOL hasApplication = UIApplicationClass && [UIApplicationClass respondsToSelector:@selector(sharedApplication)];
        if (hasApplication && [self shouldContinueWhenAppEntersBackground]) {
            __weak __typeof__ (self) wself = self;
            UIApplication * app = [UIApplicationClass performSelector:@selector(sharedApplication)];
            self.backgroundTaskId = [app beginBackgroundTaskWithExpirationHandler:^{
                __strong __typeof (wself) sself = wself;
                //当应用程序留给后台的时间快要结束时(该时间有限),这个block将执行:
               //进行一些清理工作(主线程执行),清理失败会导致程序挂掉
                if (sself) {
                    [sself cancel];

                    [app endBackgroundTask:sself.backgroundTaskId];//结束标记的后台任务
                    sself.backgroundTaskId = UIBackgroundTaskInvalid;//销毁后台任务标识符
                }
            }];
        }
#endif
        NSURLSession *session = self.unownedSession;
        if (!self.unownedSession) {
            NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
            sessionConfig.timeoutIntervalForRequest = 15;

            //传一个nil的queue以让session创建一个串行队列
            self.ownedSession = [NSURLSession sessionWithConfiguration:sessionConfig
                                                              delegate:self
                                                         delegateQueue:nil];
            session = self.ownedSession;
        }

        self.dataTask = [session dataTaskWithRequest:self.request];
        self.executing = YES;  //通知改operation正在执行,注意作者是重写了setter方法的
    }

    [self.dataTask resume];
     //如果任务创建成功,就发送通知否则就把错误传回去
    if (self.dataTask) {
        for (SDWebImageDownloaderProgressBlock progressBlock in [self callbacksForKey:kProgressCallbackKey]) {
            progressBlock(0, NSURLResponseUnknownLength, self.request.URL);
        }
        dispatch_async(dispatch_get_main_queue(), ^{
            [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:self];
        });
    } else {
        [self callCompletionBlocksWithError:[NSError errorWithDomain:NSURLErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Connection can't be initialized"}]];
    }

#if SD_UIKIT
    Class UIApplicationClass = NSClassFromString(@"UIApplication");
    if(!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) {
        return;
    }
    //确认销毁后台任务标识符,因为此时一定是在前台执行的
    if (self.backgroundTaskId != UIBackgroundTaskInvalid) {
        UIApplication * app = [UIApplication performSelector:@selector(sharedApplication)];
        [app endBackgroundTask:self.backgroundTaskId];
        self.backgroundTaskId = UIBackgroundTaskInvalid;
    }
#endif
}

好了,终于over了,SD的整套工艺流程大概就是这般,但是其中还有好多本人未曾讲到的代码,还是值得咱们失去押一样关押的。
末段,如果发甚错误的地方希望大家指正

啊釜底抽薪当时类问题,最初的做法是采样,通过采样降低收集之日志量,从而稳定计算集群的负荷和水位,保障EagleEye自身工作的安宁,尽量减少业务峰值对咱的熏陶。但是带来的问题也是妇孺皆知的,统计数据在计算时用考虑采样率估算出真的多少,在征集数据量较小且采样率较高之场景下导致聚合后的数量不精确,无法表现业务真实的状态,从而也不怕失去了那个价值。

也彻底解决业务峰值对EagleEye计算集群的碰撞,将一些实时计算逻辑下没到业务方的机器中,使得业务量和所要采集的日志量解耦,保证计算集群的安澜。具体实现是以业务方的机上先以数据论指定维度做聚合(一般是为日维度),计算集群采集该统计数据后再度集结,极大的稳定性了算集群的负荷。

葡京在线开户 4

希冀4 计算能力下沉

计量能力下沉,也足以掌握成用计分布式化,消耗了作业方极小之一样部分资源,保证了EagleEye集群的平静。而且,集群的计算量不再随着业务量的滋长要增长,只照应用规模(应用数量、机器数量)和统计维度的加强要提高,不会见重新冒出由于业务量的一瞬峰值导致计算机群的载荷过大之问题,最终让EagleEye在都链路压测和大促期间还能够维持安静水位,并且出现精准的数目。

场景化链路 
EagleEye一直专注于中件层面的调用,而阿里巴巴的业务量巨大,系统也比较复杂,所以每部分的功用区划比较清楚,中间件层面的一对数量比较难以跟业务数据交互关联,对于链路跟踪、问题一定和针对指定工作场景的容量规划等都出有难度。

本年,EagleEye推出场景化链路的效应,开放了补偿加业务场景标的力,类似于压测流量打压测标,对点名的事体自及相应的政工场景标签,并涉嫌该标签下所有的中等件调用(包括劳动、缓存、数据库和消息等),一凡可以协助业务方开发同学再次好地有别于某个RPC流量中之事情语义,二凡是得清楚的梳理出某业务场景标下对应之RPC流量,对分析有重大指标,如缓存命中率,数据库RT等发生于生之提携。

葡京在线开户 5

图5 流量场景标

据悉此数据,也可重好的复盘全链路压测数据。在压测之前(也得以于常态下)对根本作业由上点名的竹签,压测后透过各级业务场景的流量得出相应的性基线,更好之恒核心链路中的题材和性能拼劲,提高压测的效率以及价值。 
精细化监控 
EagleEye的链路葡京在线开户数据对于问题的发现与定位有所重大的用意,更加丰富的数量形式与显现对加强意识的频率有显的升级换代。

每当尽双十一备战过程中,遇到并缓解了多疑难杂症。其中,单机问题占了生特别之比重。在分布式系统中,单机问题是较常见的同类似问题,
由于此类题材屡屡和工作代码不直有关,与容器或者机器出肯定的关联性,且出现的概率比较小,有得的随机性,导致该问题屡屡比较为难排除查。实际工作的呈现可能是RT的颠簸,也说不定是小概率的荒谬等等。

EagleEye的调用链虽然可以便捷定位此类题材,但是调用链是站于单次请求的观点上,在固定到某某IP之后好可能还亟需重分析更多的数量才会召开决定,针对此类的题目,EagleEye提供了左TopN分布及系统热点图等功效,帮助业务方开发同学快速定位问题。针对单机故障,往往对整体的指标影响不酷,通过动级别之督查数据比难定位,EagleEye在流计算吃统计了运用各个机器的缪情况,汇总并排序有Top10的机器,一旦出现单机故障,可以挺明朗的固化到具体的IP,并且根据拖欠IP对应之失实数量得以很快做出仲裁,缩短了开同学排查问题之时光。系统热点图在压测和大促期间对系健康度的显现特别清楚,一是好清晰看到是否留存去群点的机,二凡可以证明流量之去向是否对。

葡京在线开户 6

希冀6 系统热点图

再度增长的生态 
当阿里巴巴,EagleEye是平等悠悠问题排查的利器,一直服务让业务方的同班帮忙夫高速发现并定位问题,降低故障的持续时间,提升开发暨运维效率。其实,EagleEye底层还隐含在相同卖海量的数,在临平年遭受,我们不住地动以及打桩这卖数据的含义,希望表达其重可怜之价值,同时也指望基于这些多少建立平等效生态体系,帮助用户还好发展工作,期间为孕育发生成千上万出价之出品,为集团的技巧发展打下了基础。

圣秤项目:天秤基于EagleEye的状况数据及中间间件、系统指标等监控数据,结合其他多款监控产品构建一个系统稳定解决方案,意在解决问题很快发现和精准定位、大促常态化、压测常态化等问题。

侦察员计划 –
更轻量化的全链路压测:尖兵计划基于EagleEye的中档件、系统指标与压测数据,实现常态化全链路压测和问题意识,是保持双十一跟全链路压测顺利的怪杀器之一,相比去年八差都链路压测,今年环境加倍复杂,但是单待三软全链路压测就完事目标,为集团节省上千个人工,大幅提升交付上线质量和大促效率。

精准回归:依托EagleEye调用链采集与计量的能力,实现了测试用例精准推荐的效能,并于片以之精准测试着省了50%~70%之测试时间。精准测试通过EagleEye采集,数据回流的方案的出口,在大规模利用上(千万链路)做到了测试用例与运代码链路的准实时别。

天图项目:天图依赖了片EagleEye的链路数据,为用户提供面向复杂工作链路、高度分布式架构下的Application
Performance Management
(APM)方案,以完善、实时、可视化、智能的方式吃您速了解下与工作链路的全貌。

结语 
当年之双十一凡均等不善全面的双十一,可以说凡是技巧集团的杀获全胜,EagleEye在这次大考中呢交出了一致卖像样完美的答卷,无论是在全链路压测中尚是双十一当天,系统的安定和多少的实时性都落得了预期,为业务方的提供了强硬的支撑,提高了问题排查的效率。

但,未来的路程还挺丰富,智能化的进化步伐越来越快,业务方对EagleEye的数额质量的求为愈来愈强,今后EagleEye会专注于架构的多变和智能化的推动,进一步提高问题一定的效率,更好的支持起基于链路数据的平等片生态。