Thanos工作原理及组件简介

Posted 东风微鸣

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Thanos工作原理及组件简介相关的知识,希望对你有一定的参考价值。

Thanos 简介

Thanos 是一个「开源的,高可用的 Prometheus 系统,具有长期存储能力」。很多知名公司都在使用 Thanos,也是 CNCF 孵化项目的一部分。

Thanos 的一个主要特点就是通过使用对象存储(比如 S3)可以允许 “无限” 存储空间。对象存储可以是每个云提供商提供的对象存储也可以是 ceph、rook 或 minio 这样的解决方案。

工作原理

Thanos 和 Prometheus 并肩作战,从 Prometheus 开始升级到 Thanos 是很常见的。

Thanos 被分成几个组件,每个组件都只有一个目标(典型云原生架构),组件之间通过 gRPC 进行通信。

Thanos Sidecar

Thanos 和 Prometheus 一起运行(有一个边车),每 2 小时向一个对象存储库输出 Prometheus 指标。这使得 Prometheus 几乎是无状态的。Prometheus 仍然在内存中保存着 2 个小时的度量值,所以在发生宕机的情况下,你可能仍然会丢失 2 个小时的度量值(这个问题应该由你的 Prometheus 设置来处理,使用 HA/分片,而不是 Thanos)。

:notebook: 参考文档:

Prometheus 基本高可用架构

Thanos sidecar 与 Prometheus Operator 和 Kube Prometheus 栈一起,可以轻松部署。这个组件充当 Thanos 查询的存储。

Thanos Store(存储)

Thanos 存储充当一个网关,将查询转换为远程对象存储。它还可以在本地存储上缓存一些信息。基本上,这个组件允许你查询对象存储以获取指标。这个组件充当 Thanos 查询的存储。

Thanos Compactor

Thanos Compactor(压缩器) 是一个单体(它是不可扩展的),它负责压缩和降低存储在对象存储中的指标。下采样(数据老化)是随着时间的推移对指标粒度的宽松。例如,你可能想将你的指标保持 2 年或 3 年,但你不需要像昨天的指标那么多数据点。这就是压缩器的作用,它可以在对象存储上节省字节,从而节省成本。

Thanos Query

Thanos Query(查询)是 Thanos 的主要组件,它是向其发送 PromQL 查询的中心点。Thanos 查询暴露了一个与 Prometheus 兼容的端点。然后它将查询分派给所有的 “stores”。记住,Store 可能是任何其他提供指标的 Thanos 组件。Thanos 查询可以发送查询到另一个 Thanos 查询(他们可以堆叠)。

  • Thanos Store
  • Thanos Sidecar
  • Thanos Query

还负责对来自不同 Store 或 Prometheus 的相同指标进行重复数据删除。例如,如果你有一个度量值在 Prometheus 中,同时也在对象存储中,Thanos Query 可以对该指标值进行重复数据删除。在 Prometheus HA 设置的情况下,重复数据删除也基于 Prometheus 副本和分片。

Thanos Query Frontend(查询前端)

正如它的名字所暗示的,Thanos 查询前端是 Thanos 查询的前端,它的目标是将大型查询拆分为多个较小的查询,并缓存查询结果(在内存或 memcached 中)。

还有其他组件,比如在远程写的情况下 Thanos Receiver(接收器)、Thanos Ruler(规则)。

Thanos 部署架构

Sidecar 方式部署:

Receiver 方式部署:

三人行, 必有我师; 知识共享, 天下为公. 本文由东风微鸣技术博客 EWhisper.cn 编写.

4. Jetpack源码解析—LiveData的使用及工作原理

1. 背景

上一篇我们分析了Lifecycles组件的源码,本篇我们将继续分析LiveData组件

相关系列文章:

1. Jetpack源码解析—看完你就知道Navigation是什么了?

2. Jetpack源码解析—Navigation为什么切换Fragment会重绘?

3. Jetpack源码解析—用Lifecycles管理生命周期

2. 基础

2.1 简介

LiveData是一个可观察的数据持有者类,与常规observable不同,LiveData是生命周期感知的,这意味着它尊重其他应用程序组件的生命周期,例如ActivityFragmentService。此感知确保LiveData仅更新处于活动生命周期状态的应用程序组件观察者。

2.2 优点

1. 确保UI符合数据状态
LiveData遵循观察者模式。 当生命周期状态改变时,LiveData会向Observer发出通知。 您可以把更新UI的代码合并在这些Observer对象中。不必去考虑导致数据变化的各个时机,每次数据有变化,Observer都会去更新UI。

2. 没有内存泄漏
Observer会绑定具有生命周期的对象,并在这个绑定的对象被销毁后自行清理。

3. 不会因停止Activity而发生崩溃
如果Observer的生命周期处于非活跃状态,例如在后退堆栈中的Activity,就不会收到任何LiveData事件的通知。

4.不需要手动处理生命周期
UI组件只需要去观察相关数据,不需要手动去停止或恢复观察。LiveData会进行自动管理这些事情,因为在观察时,它会感知到相应组件的生命周期变化。

5. 始终保持最新的数据
如果一个对象的生命周期变到非活跃状态,它将在再次变为活跃状态时接收最新的数据。 例如,后台Activity在返回到前台后立即收到最新数据。

6. 正确应对配置更改
如果一个Activity或Fragment由于配置更改(如设备旋转)而重新创建,它会立即收到最新的可用数据。

7.共享资源
您可以使用单例模式扩展LiveData对象并包装成系统服务,以便在应用程序中进行共享。LiveData对象一旦连接到系统服务,任何需要该资源的Observer都只需观察这个LiveData对象。

2.3 基本使用

在我们的Jetpack_Note中有使用demo,具体可查看LiveDataFragment
Demo中通过对一个LiveData对象进行生命周期的监听,实现将值打印在控制台中。首先声明一个LiveData对象:

private lateinit var liveData: MutableLiveData<String>

点击开始观察数据按钮,弹出控制台,我们可以看到控制台输出了onStart()日志,因为我们将liveData的值和Fragment的生命周期进行了绑定,当返回桌面或者销毁Fragment的时候,LiveData的值会变成相应的生命周期函数,并打印在控制台中:

class LiveDataFragment : Fragment() 

    private lateinit var liveData: MutableLiveData<String>

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? 
        return inflater.inflate(R.layout.fragment_live_data, container, false)
    


    override fun onActivityCreated(savedInstanceState: Bundle?) 
        super.onActivityCreated(savedInstanceState)
        liveData = MutableLiveData()
        btn_observer_data.setOnClickListener 
            if (FloatWindow.get() == null) 
                FloatWindowUtils.init(activity?.application!!)
            
            FloatWindowUtils.show()

            //创建一个观察者去更新UI
            val statusObserver = Observer<String>  lifeStatus ->
                FloatWindowUtils.addViewContent("LiveData-onChanged: $lifeStatus")
            
            liveData.observeForever(statusObserver)
        
    


    override fun onStart() 
        super.onStart()
        liveData.value = "onStart()"
    


    override fun onPause() 
        super.onPause()
        liveData.value = "onPause()"
    

    override fun onStop() 
        super.onStop()
        liveData.value = "onStop()"
    

    override fun onDestroy() 
        super.onDestroy()
        liveData.value = "onDestroy()"
    

**注意:**这里我使用了observeForever监听了所有生命周期方法,所以你会看到onDestroy()等生命周期函数的打印。

好了,Demo很简单,接下来我们来看一下源码,进行分析:

3. 源码分析:

3.1 observer()

我们声明了一个LiveData对象,并通过监听Fragment的生命周期来改变LiveData中的value值,LiveData实际上就像一个容器,Demo中存储了一个String类型的值,当这个值发生改变的时候,可以在回调中监听到他的改变。接下来我们就先从addObserver入手:

@MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) 
        assertMainThread("observe");
        if (owner.getLifecycle().getCurrentState() == DESTROYED) 
            // ignore
            return;
        
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        if (existing != null && !existing.isAttachedTo(owner)) 
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        
        if (existing != null) 
            return;
        
        owner.getLifecycle().addObserver(wrapper);
    

查看源码,我们调用observer()时,传递了两个参数,第一个是LifecycleOwner接口实例,而我们继承的Fragment本身就已经实现了这个接口,所以我们传this即可;第二个参数Observer就是我们观察的回调。接下来将这两个参数传递new出了一个新的对象:LifecycleBoundObserver,最后将LifecycleBoundObserverLifecycleOwner进行了绑定,其实这里面我们可以将LifecycleOwner就理解成我们的Fragment或者Activity的实例,因为它们都实现了LifecycleOwner

3.2 LifecycleBoundObserver

class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver 
        .....
        @Override
        boolean shouldBeActive() 
            return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
        
        //Activity生命周期变化时,回调方法
        @Override
        public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) 
            if (mOwner.getLifecycle().getCurrentState() == DESTROYED) 
                removeObserver(mObserver);
                return;
            
            //更新livedata活跃状态
            activeStateChanged(shouldBeActive());
        

        @Override
        boolean isAttachedTo(LifecycleOwner owner) 
            return mOwner == owner;
        
        //解除监听
        @Override
        void detachObserver() 
            mOwner.getLifecycle().removeObserver(this);
        
    

我们可以看到这里面与LifecycleOwner进行了绑定,并且实现了onStateChanged方法,当生命周期发生变化时执行activeStateChanged(shouldBeActive());方法;shouldBeActive() 返回了 要求生命周期至少是STARTED状态才被认为是activie状态;如果state是DESTROYED状态时,解绑LifecycleOwnerLiveData。接下来我们看下怎样更新livedata中数据值:

3.3 dispatchingValue()

我们追踪activeStateChanged方法发现,在里面做了一些活跃状态值的操作,并且当状态活跃时,更新数据值:

void activeStateChanged(boolean newActive) 
            if (newActive == mActive) 
                return;
            
            // immediately set active state, so we'd never dispatch anything to inactive
            // owner
            mActive = newActive;
            boolean wasInactive = LiveData.this.mActiveCount == 0;
            LiveData.this.mActiveCount += mActive ? 1 : -1;
            if (wasInactive && mActive) 
                onActive();
            
            if (LiveData.this.mActiveCount == 0 && !mActive) 
                onInactive();
            
            if (mActive) 
                dispatchingValue(this);
            
        
void dispatchingValue(@Nullable ObserverWrapper initiator) 
    ...
    //遍历LiveData的所有观察者执行下面代码
    for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) 
                    considerNotify(iterator.next().getValue());
                    if (mDispatchInvalidated) 
                        break;
                    
                
    ...

//将数据值回调到livedata.observer()回去
private void considerNotify(ObserverWrapper observer) 
        if (!observer.mActive) 
            return;
        
        if (!observer.shouldBeActive()) 
            observer.activeStateChanged(false);
            return;
        
        if (observer.mLastVersion >= mVersion) 
            return;
        
        observer.mLastVersion = mVersion;
        observer.mObserver.onChanged((T) mData);
    

从上面我们可以看到LiveData的数据更新以及数据回调的整个过程,但是当我们手动setValue()的时候过程是怎样的呢?你会发现,setValue()其实最后就是通过调用了dispatchingValue()方法。而关于postValue()在子线程更新数据的相关代码,这里就不做介绍了,其实你大可以想出来,就是使用的Handler

LiveData中的代码很简洁,400多行的代码,看起来也并不费劲,下面我们来分析下整个流程:

  • 通过使用LiveData对象,为它创建观察者Observer
  • 创建Observer时绑定Fragment生命周期
  • LifecycleBoundObserver生命周期变化时,dispatchValue下发更新LiveData中的值
  • LiveData主动setValue时,会主动dispatchValue,并且会considerNotify激活observer

4. 扩展

4.1 Map转换

我们在开发中经常会遇到这种场景,有时我们需要根据另外一个LiveData实例返不同的LiveData实例,然后在分发给Observer,Lifecycle包提供了Transformations类,可以帮助我们实现这样的场景:

通过**Transformations.map()**使用一个函数来转换存储在 LiveData对象中的值,并向下传递转换后的值:

LiveDataViewModel

class LiveDataViewModel : ViewModel() 
    val data = listOf(User(0,"Hankkin"), User(1,"Tony"),User(2,"Bob"),User(3,"Lucy"))

    val id = MutableLiveData<Int>()
    //map转换返回User实体
    val bean: LiveData<User> = Transformations.map(id, Function 
        return@Function findUserById(id.value!!)
    )
    //根据id查找User
    private fun findUserById(id: Int): User? 
        return data.find  it.id == id 
    

LiveDataFragment

//改变ViewModel中idLiveData中的值
        btn_observer_map.setOnClickListener 
            mId++
            viewModel.id.postValue(mId)
        
        //当idLiveData变化后,UserBean也会改变且更新Textview的文本
        viewModel.bean.observe(
            this,
            Observer  tv_livedata_map.text = if (it == null) "未查找到User" else "为你查找到的User为:$it.name" )

4.2 Map源码

@MainThread
    public static <X, Y> LiveData<Y> map(
            @NonNull LiveData<X> source,
            @NonNull final Function<X, Y> mapFunction) 
        final MediatorLiveData<Y> result = new MediatorLiveData<>();
        result.addSource(source, new Observer<X>() 
            @Override
            public void onChanged(@Nullable X x) 
                result.setValue(mapFunction.apply(x));
            
        );
        return result;
    

我们可以看到map的源码是通过MediatorLiveData中的addSource()方法来实现的,第一个参数为我们需要改变的LiveData值,也就是我们上面例子中的userid,第二个参数则是我们传过来的Fuction通过高阶函数,将值set到LiveData上。

下面我们看下addSource()方法:

@MainThread
    public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<? super S> onChanged) 
        Source<S> e = new Source<>(source, onChanged);
        Source<?> existing = mSources.putIfAbsent(source, e);
        if (existing != null && existing.mObserver != onChanged) 
            throw new IllegalArgumentException(
                    "This source was already added with the different observer");
        
        if (existing != null) 
            return;
        
        if (hasActiveObservers()) 
            e.plug();
        
    

这里把我们的LiveData和Observer封装成了Source对象,并且这个对象,不能重复添加,具体代码可查看Source:

private static class Source<V> implements Observer<V> 
        final LiveData<V> mLiveData;
        final Observer<? super V> mObserver;
        int mVersion = START_VERSION;

        Source(LiveData<V> liveData, final Observer<? super V> observer) 
            mLiveData = liveData;
            mObserver = observer;
        

        void plug() 
            mLiveData.observeForever(this);
        

        void unplug() 
            mLiveData.removeObserver(this);
        

        @Override
        public void onChanged(@Nullable V v) 
            if (mVersion != mLiveData.getVersion()) 
                mVersion = mLiveData.getVersion();
                mObserver.onChanged(v);
            
        
    

首先Source是一个观察者,可以看到,我们外部使用的Observer会以Source的成员变量的形式,添加到传入的LiveData中。值得注意的是,这里使用了mLiveData.observeForever(this);

observeForever()用法可以看到,我们并没有传递LifecycleOwner,因此它并不具备生命感知能力。从注释中也可见一斑:This means that the given observer will receive all events and will never be automatically removed.

map()的原理就是基于MediatorLiveData,MediatorLiveData内部会将传递进来的LiveData和Observer封装成内部类,然后放在内部维护的一个Map中。并且自动帮我们完成observeForever()和removeObserver()

5.总结

  • LiveData基于观察者模式实现,并且和LifecycleOwner进行绑定,而LifecycleOwner又被Fragment和Activity实现,所以它可以感知生命周期;在当前的LifecycleOwner不处于活动状态(例如onPasue()onStop())时,LiveData是不会回调observe()的,因为没有意义.
  • 同时LiveData只会在LifecycleOwner处于Active的状态下通知数据改变,果数据改变发生在非 active 状态,数据会变化,但是不发送通知,等 owner 回到 active 的状态下,再发送通知;
  • LiveData在DESTROYED时会移除Observer,取消订阅,不会出现内存泄漏
  • postValue在异步线程,setValue在主线程
  • 如果LiveData没有被observe(),那么此时你调用这个LiveData的postValue(…)/value=…,是没有任何作用

当然官方推荐我们LiveData配合ViewModel一起使用,因为LiveData一般都出现在ViewModel中,所以我们下篇文章会继续分析ViewModel.

以上是关于Thanos工作原理及组件简介的主要内容,如果未能解决你的问题,请参考以下文章

KUBERNETES01_部署方式的变迁为什么用Kubernetes工作原理组件交互原理动画演示

K8S 架构实现及工作原理

使用 Thanos 实现多集群(租户)监控

Kubernetes架构原理

Kubernetes 1.15 安装及组件关系(前期准备工作篇)

kubernetes 组件之 kubelet