前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Android | 《看完不忘系列》之Glide

Android | 《看完不忘系列》之Glide

作者头像
Holiday
发布于 2020-08-10 07:05:03
发布于 2020-08-10 07:05:03
68400
代码可运行
举报
文章被收录于专栏:哈利迪ei哈利迪ei
运行总次数:0
代码可运行

《看完不忘系列》将以从树干到细枝的思路来分析一些技术框架,本文是开篇文章,将对开源项目Glide图片加载库进行介绍。如果老铁们看完还是忘了,就 回来揍我一顿 点赞收藏加关注,多看两遍~

概览

基于Glide最新版本4.11.0,未迁AndroidX的项目只能使用4.9.0,简单使用:

引入依赖,app/build.gradle:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'

一句代码,完成图片加载:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Glide.with(this) //指定上下文,可以是app、activity、fragment
    .load(url)  //网络图片地址
    .into(img);  //用于展示的imageView

用起来简洁优雅,然后我们先大致预览下Glide的一些职能,

树干:核心流程

Glide.with(this).load(url).into(img)为起点,拆成with购车load上牌into发车三个环节来分析。

with:购车

简单来说,with的功能就是根据传入的上下文context来获取图片请求管理器RequestManager,他用来管理和启动图片请求,

context可以传入app、activity、fragment,这决定了图片请求的生命周期。通常是使用粒度较细的context,即使用当前页面的context而不是全局的app。这样做的好处是,打开一个页面开启图片加载,然后退出页面,图片请求就会跟随页面销毁而被取消,而不是继续加载而浪费资源。

当context是app时,通过RequestManagerRetriever获得的RequestManager是一个全局单例,这类图片请求的生命周期将会跟随整个app,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class RequestManagerRetriever implements Handler.Callback {
    volatile RequestManager applicationManager;
    
    RequestManager getApplicationManager(Context context) {
        //双重检查锁,获取单例的RequestManager
        if (applicationManager == null) {
            synchronized (this) {
                if (applicationManager == null) {
                    Glide glide = Glide.get(context.getApplicationContext());
                    applicationManager = factory.build(glide,new ApplicationLifecycle(),
                        new EmptyRequestManagerTreeNode(),context.getApplicationContext());
                }
            }
        }
        //返回应用级别的RequestManager单例
        return applicationManager;
    }
}

当context是Activity时,创建一个SupportRequestManagerFragment(无界面的空fragment)添加到Activity,从而感知Activity的生命周期,同时创建RequestManager给空fragment持有,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class RequestManagerRetriever implements Handler.Callback {
    RequestManager supportFragmentGet(Context context,FragmentManager fm,
                                      Fragment parentHint,boolean isParentVisible) {
        //获取空fragment,无则创建
        SupportRequestManagerFragment current =
            getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
        RequestManager requestManager = current.getRequestManager();
        if (requestManager == null) {
            Glide glide = Glide.get(context);
            //如果空fragment没有RequestManager,就创建一个
            requestManager = factory.build(glide, current.getGlideLifecycle(), 
                                           current.getRequestManagerTreeNode(), context);
            //让空fragment持有RequestManager
            current.setRequestManager(requestManager);
        }
        //返回页面级别的RequestManager
        return requestManager;
    }
}

然后看到getSupportRequestManagerFragment方法,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class RequestManagerRetriever implements Handler.Callback {
    Map<FragmentManager, SupportRequestManagerFragment> pendingSupportRequestManagerFragments =
      new HashMap<>();
    
    SupportRequestManagerFragment getSupportRequestManagerFragment(
        final FragmentManager fm, Fragment parentHint, boolean isParentVisible) {
        //通过tag找到Activity中的空fragment
        SupportRequestManagerFragment current =
            (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
        if (current == null) {
            //findFragmentByTag没找到空fragment,有可能是延迟问题?再从Map中找一下
            current = pendingSupportRequestManagerFragments.get(fm);
            if (current == null) {
                //确实没有空fragment,就创建一个
                current = new SupportRequestManagerFragment();
    //...
                //缓存进Map
                pendingSupportRequestManagerFragments.put(fm, current);
                //空fragment添加到Activity,使其能感知Activity的生命周期
                fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
                //...
            }
        }
        return current;
    }
}

综上,通过with操作,

当context是app时,得到应用级别RequestManager全局单例;

当context是Activity时,每个页面都会被添加一个空fragment,由空fragment持有页面级别RequestManager

注意:如果with发生在子线程,不管context是谁,都返回应用级别RequestManager单例。 发散:添加空fragment来感知页面生命周期的思想,在Lifecycle的实现中也可以看到,见ReportFragment的injectIfNeededIn方法。(不过这个方法在Lifecycle的2.2.0版本中有所改动,Android 10开始的设备改成了使用Application.ActivityLifecycleCallbacks来感知,感兴趣可以康康)

至此,我们根据存款context买到了心仪的车RequestManager,下面开始上牌~

load:上牌

load方法得到了一个RequestBuilder图片请求构建器,见名知意猜一下,是用来创建图片请求的,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class RequestManager
    implements ComponentCallbacks2, LifecycleListener, ModelTypes<RequestBuilder<Drawable>> {

    RequestBuilder<Drawable> load(String string) {
        return asDrawable().load(string);
    }

    RequestBuilder<Drawable> asDrawable() {
        //需要加载的类型为Drawable
        return as(Drawable.class);
    }

    <ResourceType> RequestBuilder<ResourceType> as(Class<ResourceType> resourceClass) {
        //创建一个请求构建器
        return new RequestBuilder<>(glide, this, resourceClass, context);
    }
}

然后跟进asDrawable().load(string)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
    implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>> {

    RequestBuilder<TranscodeType> load(String string) {
        return loadGeneric(string);
    }

    RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
        //只是简单地赋值
        this.model = model;
        isModelSet = true;
        return this;
    }
}

到这里,我们就完成了上牌,得到了一个RequestBuilder图片请求构建器。没错,上牌就这么简单,毕竟摇号已经够艰难了对吧?

into:发车

阶段一

来到into了,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
    implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>> {

    ViewTarget<ImageView, TranscodeType> into(ImageView view) {
        //...
        BaseRequestOptions<?> requestOptions = this;
        if (!requestOptions.isTransformationSet()
            && requestOptions.isTransformationAllowed()
            && view.getScaleType() != null) {
            //根据ImageView的ScaleType,来配置参数
            switch (view.getScaleType()) {
                case CENTER_CROP:
                    requestOptions = requestOptions.clone().optionalCenterCrop();
                    break;
                case CENTER_INSIDE:
                    requestOptions = requestOptions.clone().optionalCenterInside();
                    break;
                    //...
            }
        }
        return into(
            //前面提到,Target是展示图片的载体,这里他封装了ImageView
            glideContext.buildImageViewTarget(view, transcodeClass),null,
            requestOptions,Executors.mainThreadExecutor());
    }
}

继续跟进into重载方法,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
    implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>> {
    
    <Y extends Target<TranscodeType>> Y into(Y target,RequestListener<TranscodeType> targetListener,
                                             BaseRequestOptions<?> options,Executor callbackExecutor) {
        //...
        //创建图片请求
        Request request = buildRequest(target, targetListener, options, callbackExecutor);
        //获取Target载体已有的请求
        Request previous = target.getRequest();
        //如果两个请求等效,并且xxx(先忽略)
        if (request.isEquivalentTo(previous)
            && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
            if (!Preconditions.checkNotNull(previous).isRunning()) {
                //启动异步请求
                previous.begin();
            }
            return target;
        }
        requestManager.clear(target);
        //图片载体绑定图片请求,即imageView setTag为request
        target.setRequest(request);
        //启动异步请求
        requestManager.track(target, request);
        return target;
    }
}

跟进requestManager.track(target, request)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//RequestManager.java
void track(Target<?> target,Request request) {
    targetTracker.track(target);
    requestTracker.runRequest(request);
}

//RequestTracker.java
void runRequest(Request request) {
    requests.add(request);
    if (!isPaused) {
        //开启图片请求
        request.begin();
    } else {
        request.clear();
        //如果处于暂停状态,就把请求存起来,晚些处理
        pendingRequests.add(request);
    }
}

到这里,就启动了图片请求,

阶段二

那么,接下来重点关注的就是request.begin()了,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class SingleRequest<R> implements Request, SizeReadyCallback, ResourceCallback {

    void begin() {
        synchronized (requestLock) {
            //...
            if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
                //如果已经有了明确的尺寸,开始加载
                onSizeReady(overrideWidth, overrideHeight);
            } else {
                //没有的话先去获取尺寸,最终还是走onSizeReady
                target.getSize(this);
            }
            //...
        }
    }

    void onSizeReady(int width, int height) {
        synchronized (requestLock) {
            //...
            //engine.load,传了很多参数
            loadStatus =
                engine.load(glideContext,model,
                requestOptions.getSignature(),
                this.width,this.height,
                requestOptions.getResourceClass(),
                transcodeClass,priority,
                requestOptions.getDiskCacheStrategy(),
    //...
                this,callbackExecutor);
            //...
        }
    }
}

跟进engine.load

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Engine implements EngineJobListener,MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {

    <R> LoadStatus load(
        GlideContext glideContext,
        //...
        Executor callbackExecutor) {
        //...
        EngineResource<?> memoryResource;
        synchronized (this) {
            //从内存加载
            memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
            if (memoryResource == null) {
                //如果内存里没有缓存,则加载
                return waitForExistingOrStartNewJob(
                    glideContext,
                    model,
                    //...
                    key,
                    startTime);
            }
        }
        cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
        return null;
    }

    <R> LoadStatus waitForExistingOrStartNewJob(...) {
        //...
        EngineJob<R> engineJob =engineJobFactory.build(...);
        DecodeJob<R> decodeJob =decodeJobFactory.build(...);
        jobs.put(key, engineJob);
        //添加回调,这个cb就是SingleRequest自己,todo1
        engineJob.addCallback(cb, callbackExecutor);
        //engineJob开启decodeJob
        engineJob.start(decodeJob);
        return new LoadStatus(cb, engineJob);
    }
}

DecodeJob是一个Runable,看看他的run方法,调用链如下:

DecodeJob.run -> DecodeJob.runWrapped -> DecodeJob.runGenerators -> SourceGenerator.startNext -> SourceGenerator.startNextLoad -> MultiModelLoader#MultiFetcher.loadData -> HttpUrlFetcher.loadData

来到HttpUrlFetcher

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class HttpUrlFetcher implements DataFetcher<InputStream> {
    
    void loadData(Priority priority, DataCallback<? super InputStream> callback) {
        try {
            //获取输入流,没有引入okhttp,则使用HttpURLConnection
            InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
            //回调出去
            callback.onDataReady(result);
        } catch (IOException e) {
            callback.onLoadFailed(e);
        } finally {
        }
    }
}

到这里,网络请求就完成了,下面看看图片是怎么设置上去的,前边留了个todo1,

//添加回调,这个cb就是SingleRequest自己,todo1

callback就是SingleRequest,被回调前有一些对象包装、解码操作,暂不深究,来到SingleRequest

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class SingleRequest<R> implements Request, SizeReadyCallback, ResourceCallback {

    void onResourceReady(Resource<?> resource, DataSource dataSource) {
        Resource<?> toRelease = null;
        try {
            synchronized (requestLock) {
                //...
                Object received = resource.get();
                //...
                //这里
                onResourceReady((Resource<R>) resource, (R) received, dataSource);
            }
        } finally {
            if (toRelease != null) {
                engine.release(toRelease);
            }
        }
    }

    void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
        //...
        try {
            //...
            //这里,回调给Target载体
            target.onResourceReady(result, animation);
        } finally {
        }
        notifyLoadSuccess();
    }
}

跟进target.onResourceReady,最终来到DrawableImageViewTarget

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class DrawableImageViewTarget extends ImageViewTarget<Drawable> {
    void setResource(Drawable resource) {
        //ImageView设置图片
        view.setImageDrawable(resource);
    }
}

结合阶段一和二,

终于,汽车发动起来了~

with购车load上牌into发车三个环节汇总起来,

可以看到,整个图片加载过程,就是with得到RequestManager,load得到RequestBuilder,然后into开启加载:

创建Request、开启Engine、运行DecodeJob线程、HttpUrlFetcher加载网络数据、回调给载体Target、载体为ImageView设置展示图片。

细枝:补充

线程切换

在子线程下载完图片后,如何回调主线程设置图片?在Executors类里,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private static final Executor MAIN_THREAD_EXECUTOR = new Executor() {
    //主线程Handler
    private final Handler handler = new Handler(Looper.getMainLooper());

    @Override
    public void execute(Runnable command) {
        //切回主线程
        handler.post(command);
    }
};

调用栈如下:

线程池

GlideBuilder类里会初始化一些线程池:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Glide build(Context context) {
    private GlideExecutor sourceExecutor; //加载图片
    private GlideExecutor diskCacheExecutor; //管理磁盘缓存
    private GlideExecutor animationExecutor; //管理动画
}

缓存

有内存缓存和磁盘缓存,在Engine.load时会去取,篇幅原因后面单独开篇来写。

webp动图

Fresco支持解析webp动图,Glide不支持,不过已经有了开源的方案,见GitHub - GlideWebpDecoder。

选型

FrescoGlide怎么选?

Fresco具有一定侵入性,需要继承SimpleDraweeView

Fresco调用繁琐,没有Glide的链式调用优雅,当然这个可以包一层来解决;

Fresco在5.0以下的系统进行了内存优化(Ashmem区),这个优势在当下的环境已经不值一提,因为这些系统占比已经非常低了,一些App的minSDK都已经设置成21了。

所以,更推荐使用Glide(个人拙见,仅供参考)

尾声

作为《看完不忘系列》的文章,本文删减了很多源码,重点在于理清Glide图片加载流程,大家看的时候最好能跟着思路去阅读源码~然后,Glide还有解码、缓存的流程没有分析,后面会单独开篇来写。

参考资料

  • 掘金 - 面试官:简历上最好不要写Glide,不是问源码那么简单
  • 掘金 - 锦囊篇|一文摸懂Glide
  • 掘金 - Android主流三方库源码分析(三、深入理解Glide源码)
  • 官方文档 & GitHub
  • GitHub - GlideWebpDecoder
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-07-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 哈利迪ei 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
Linkerd 升级到全新的 2.12 版本
Linkerd 最新的 2.12 版本已经发布了,这个庞大的版本为 Linkerd 引入了基于路由的策略,允许用户以完全零信任的方式定义和执行基于 HTTP 路由的授权策略。这些策略建立在 Linkerd 强大的工作负载身份之上,由双向 TLS 保护,并使用 Kubernetes 新推出的 Gateway API 的类型进行配置。
我是阳明
2022/09/29
4100
Linkerd 升级到全新的 2.12 版本
在 Linkerd 中使用 mTLS 保护应用程序通信
安全性是云原生应用程序的重中之重,虽然安全性是一个非常广泛的话题,但 Linkerd 依然可以发挥重要作用:其双向 TLS(mTLS)功能是为了在 Kubernetes 中实现零信任的安全方法。零信任安全是一种 IT 安全模型,要求试图访问专用网络上资源的每一个人和每一台设备(无论位于网络边界之内还是之外)都必须进行严格的身份验证。
我是阳明
2022/09/29
6630
在 Linkerd 中使用 mTLS 保护应用程序通信
Linkerd 2.10(Step by Step)—3. 自动轮换控制平面 TLS 与 Webhook TLS 凭证
Linkerd 的自动 mTLS 功能使用一组 TLS 凭据(TLS credentials)为代理生成 TLS 证书(TLS certificates):信任锚(trust anchor)、颁发者证书(issuer certificate)和私钥(private key)。虽然 Linkerd 每 24 小时自动轮换数据平面代理的 TLS 证书, 但它不会轮换用于颁发这些证书的 TLS 凭据。在本文档中,我们将描述如何使用外部解决方案 自动轮换颁发者证书和私钥。
为少
2021/07/07
6460
快速上手 Linkerd v2 Service Mesh
在本指南中,我们将引导您了解如何将 Linkerd 安装到您的 Kubernetes 集群中。然后我们将部署一个示例应用程序来展示 Linkerd 的功能。
我是阳明
2021/06/25
6580
快速上手 Linkerd v2  Service Mesh
Linkerd 2.10(Step by Step)—将 GitOps 与 Linkerd 和 Argo CD 结合使用
GitOps 是一种使用 Git 作为单一事实来源自动管理和交付 Kubernetes 基础设施和应用程序的方法。它通常利用一些软件代理来检测和协调 Git 中受版本控制的工件与集群中运行的工件之间的任何差异。
为少
2021/07/07
1.9K0
Kubernetes (K8S) 中安装部署APISIX
Apache APISIX 是一个基于 OpenResty 和 Etcd 实现的动态、实时、高性能、可扩展的微服务 API 网关,目前已经是 Apache 顶级项目。提供了丰富的流量管理功能,如负载均衡、动态路由、动态 upstream、A/B 测试、金丝雀发布、限速、熔断、防御恶意攻击、认证、监控指标、服务可观测性、服务治理等。可以使用 APISIX 来处理传统的南北流量以及服务之间的东西向流量。
王先森sec
2023/10/17
3.4K0
Kubernetes (K8S) 中安装部署APISIX
Linkerd 2.10(Step by Step)—暴露 Dashboard
您可以通过 ingress 暴露仪表板,而不是每次想要 查看发生了什么时都使用 linkerd viz dashboard。这也会暴露 Grafana。
为少
2021/07/07
9430
在 Kubernetes 中部署微服务架构 Istio
Istio 是 Service Mesh(服务网格)的主流实现方案。该方案降低了与微服务架构相关的复杂性,并提供了负载均衡、服务发现、流量管理、断路器、监控、故障注入和智能路由等功能特性。
iMike
2019/07/29
1.9K0
入门K8s:一键脚本搭建Linux服务器集群
好久没有写系列博客了,本文主要是对网上文章的总结篇,主要是将安装和运行代码做了一次真机实验,亲测可用。文章内包含的脚本和代码,多来自于网络,也有我自己的调整和配置,文章末尾对参考的文献做了列举,方便大家参考。
老张的哲学
2022/04/11
1.6K0
Prometheus监控神器-Kubernetes篇(二)
本篇使用StorageClass来持久化数据,搭建Statefulset的Grafana,并且在Dashboard导入前配置前面已经创建好的Prometheus的集群内部访问地址,同时配置ingress-nginx外部访问。
Kubernetes技术栈
2020/09/09
8620
使用loki和grafana展示ingress-nginx的日志
在kubernetes中,对于日志的收集,使用最多的是FEK, 不过有时候,FEK在架构上会略显重, ES的查询及全文检索功能其实使用的不是很多.LoKi做为日志架构的新面孔, 由grafana开源, 使用了与Prometheus同样的label理念, 同时摒弃了全文检索的能力, 因此比较轻便, 非常具有潜力。
没有故事的陈师傅
2021/01/04
2.5K0
使用loki和grafana展示ingress-nginx的日志
附037.Kubernetes_v1.29.2高可用部署架构二
该 Kubernetes 部署过程中,对于部署环节,涉及多个组件,主要有 kubeadm 、kubelet 、kubectl。
木二
2024/03/11
1.1K0
Linkerd 2.10(Step by Step)—使用 Helm 安装 Linkerd
Linkerd 可以选择通过 Helm 安装,而不是使用 linkerd install 命令。
为少
2021/07/07
8790
5.Prometheus监控入门之企业监控实战采集展示
描述: 本章主要讲解和实践Prometheus在企业中的应用场景的复现,采用了docker-compose的资源清单进行快速构建prometheus_server、prometheus_pushgateway、prometheus_alertmanager、grafana等环境。
全栈工程师修炼指南
2022/09/29
9180
5.Prometheus监控入门之企业监控实战采集展示
Linkerd 2.x 入门指南
在本指南中,我们将介绍如何将Linkerd安装到Kubernetes集群中。然后,我们将部署一个示例应用程序来展示Linkerd可以为你的服务做些什么。
CNCF
2019/12/04
2.4K1
Linkerd 2.x 入门指南
Linkerd服务网格中重试与超时和金丝雀发布
在构建分布式系统时,保证可靠性是一项关键任务。Linkerd 是一个功能强大的服务网格工具,通过其重试与超时机制,可以帮助应对临时错误和延迟问题,从而提高系统的可靠性。本文将深入探讨 Linkerd 中的重试与超时特性,以及它们如何帮助应对故障和提升用户体验。
王先森sec
2024/01/13
1960
Linkerd服务网格中重试与超时和金丝雀发布
Linkerd 2.10(Step by Step)—手动轮换控制平面 TLS 凭证
Linkerd 的自动 mTLS 功能 使用一组 TLS 凭据为代理生成 TLS 证书:信任锚、颁发者证书和私钥。信任锚的有效期有限:365 天(如果由 linkerd install 生成)或 自定义值(如果手动生成)。
为少
2021/07/07
6510
Istio服务网格的可观察性
前面我们学习了 Istio 中的流量管理功能,本节我们来学习如何配置 Istio来自动收集网格中的服务遥测。Istio为网格内所有的服务通信生成详细的遥测数据,这种遥测技术提供了服务的可观察性,使运维人员能够排查故障、维护和优化应用程序,而不会给服务的开发人员带来任何额外的负担。通过 Istio,运维人员可以全面了解到受监控的服务如何与其他服务以及Istio组件进行交互。
王先森sec
2023/04/24
9030
Istio服务网格的可观察性
【云原生实战】Kubernetes实战之基础概念
Kubernetes 为你提供了一个可弹性运行分布式系统的框架。 Kubernetes 会满足你的扩展要求、故障转移、部署模式等。 例如,Kubernetes 可以轻松管理系统的 Canary 部署。
陶然同学
2023/02/27
6270
【云原生实战】Kubernetes实战之基础概念
Linkerd 2.10(Step by Step)—4. 如何配置外部 Prometheus 实例
尽管 linkerd-viz 扩展带有自己的 Prometheus 实例, 但在某些情况下,由于各种原因使用外部实例更有意义。
为少
2021/07/07
4450
推荐阅读
相关推荐
Linkerd 升级到全新的 2.12 版本
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档