美文网首页分布式
Loghub 最佳实践

Loghub 最佳实践

作者: 东皇Amrzs | 来源:发表于2018-06-14 16:05 被阅读48次

    整理自:https://help.aliyun.com/document_detail/43841.html?spm=a2c4g.11186623.6.761.eQXd9D
    不过感觉具体细节确实没讲,我想要知道的是,在现实中的日志处理部分中的第4. 过程中不重复(需要消费者配合)。这里最佳实践里面并没有具体展开,只是提了一下柜员(消费者)配合的原理。

    如果A暂时离开或永久离开,其他柜员只要使用相同的规范:记录中已操作则处理下一个即可,如果没有则重复做,过程中需要保证原子性。

    银行的一天

    以19世纪银行为例。某个城市有若干用户(Producer),到银行去存取钱(User Operation),银行有若干个柜员(Consumer)。因为19世纪还没有电脑可以实时同步,因此每个柜员都有一个小账本能够记录对应信息,每天晚上把钱和账本拿到公司去对账。

    在分布式世界里,我们可以把柜员认为是固定内存和计算能力单机。用户是来自各个数据源的请求,Bank大厅是处理用户存取数据的日志库(Logstore)。

    • Log/LogGroup:用户发出的存取款等操作。
    • 用户(User):Log/LogGroup生产者。
    • 柜员(Clerk):银行处理用户请求的员工。
    • 银行大厅(Logstore):用户产生的操作请求先进入银行大厅,再交给柜员处理。
    • 分区(Shard):银行大厅用以安排用户请求的组织方式。

    问题1:保序(Ordering)

    银行有2个柜员(A,B),张三进了银行,在柜台A上存了1000元,A把张三1000元存在自己的账本上。张三到了下午觉得手头紧到B柜台取钱,B柜员一看账本,发现不对,张三并没有在这里存钱。

    从这个例子可以看到,存取款是一个严格有序的操作,需要同一个柜员(处理器)来处理同一个用户的操作,这样才能保持状态一致性。

    实现保序的方法很简单:排队,创建一个Shard,终端只有一个柜员A来处理。用户请求先进先出,一点问题都没有。但带来的问题是效率低下,假设有1000个用户来进行操作,即使有10个柜员也无济于事。

    这种场景怎么办?

    1. 假设有10个柜员,我们可以创建10个Shard。
    2. 如何保证对于同一个账户的操作是有序的?可以根据一致性Hash方式将用户进行映射。例如我们开10个队伍(Shard),每个柜员处理一个Shard,把不同银行账号或用户姓名,映射到特定Shard中。在这种情况下张三 Hash(Zhang)= Z 永远落在一个特定Shard中(区间包含Z),处理端面对的永远是柜员A。

    当然如果张姓用户比较多,也可以换其他策略。例如根据用户AccountID、ZipCode进行Hash,这样就可以使得每个Shard中操作请求更均匀。

    问题2:不丢失(At-Least Once)

    张三拿着存款在柜台A处理,柜员A处理到一半去接了个电话,等回来后以为业务已经办理好了,于是开始处理下一个用户的请求,张三的存款请求因此被丢失。

    虽然机器不会人为犯错,在线时间和可靠性要比柜员高。但难免也会遇到当机、或因负载高导致的处理中断,因为这样的场景丢失用户的存款,这是万万不行的。

    这种情况怎么办呢?

    A可以在自己日记本上(非账本)记录一个项目:当前已处理到Shard哪个位置,只有当张三的这个存款请求被完全确认后,柜员A才能叫下一个。

    带来问题是什么?可能会重复。比如A已经处理完张三请求(更新账本),准备在日记本上记录处理到哪个位置之时,突然被叫开了,当他回来后,发现张三请求没有记录下来,他会把张三请求再次处理一遍,这就会造成重复。

    问题3:不重复(Exactly Once)

    重复一定会带来问题吗?不一定。

    在幂等情况下,重复虽然会有浪费,但对结果没有影响。什么叫幂等:重复消费不对结果产生影响的操作叫做幂等。例如用户有一个操作 “查询余额”,该操作是一个只读操作,重复做不影响结果。对于非只读操作,例如注销用户这类操作,可以连续做两次。

    但现实生活中大部分操作不是幂等的,例如存款、取款等,重复进行计算会对结果带来致命的影响。解决的方式是什么呢?

    柜员(A)需要把账本完成 + 日记本标记Shard中处理完成作为一个事物合并操作,并记录下来(CheckPoint)。

    如果A暂时离开或永久离开,其他柜员只要使用相同的规范:记录中已操作则处理下一个即可,如果没有则重复做,过程中需要保证原子性。

    CheckPoint可以将Shard 中的元素位置(或时间)作为Key,放入一个可以持久化的对象中。代表当前元素已经被处理完成。

    业务挑战

    以上三个概念解释完成后,原理并不复杂。但在现实世界中,规模的变化与不确定性会使得以上三个问题便得更复杂。例如:

    1. 遇到发工资日子,用户数会大涨。
    2. 柜员(Clerk)毕竟不是机器人,他们需要休假,需要吃午饭。
    3. 银行经理为了整体服务体验,需要加快柜员,以什么作为判断标准?Shard中处理速度?
    4. 柜员在交接过程中,能否非常容易地传递账本与记录?

    现实中的一天

    8点银行开门

    只有一个Shard0,用户请求全部排在Shard0下,柜员A也正好可以处理。

    10点进入高峰期间

    银行经理决定把10点后Shard0分裂成2个新Shard(Shard1,Shard2),并且给了如下规定,姓名是[A-W]用户到Shard1中排队,姓名是[X, Y, Z] 到Shard 2 中排队等待处理,为什么这两个Shard区间不均匀?因为用户的姓氏本身就是不均匀的,通过这种映射方式可以保证柜员处理的均衡。

    10-12点请求消费状态:

    柜员A处理2个Shard非常吃力,于是经理派出柜员B、C出厂。因为只有2个Shard,B开始接管A负责一个Shard,C处于闲置状态。

    中午12点人越来越多

    银行经理觉得Shard1下柜员A压力太大,因此从Shard1中拆分出(Shard3,Shard4)两个新的Shard,Shard3由柜员A处理、Shard4由柜员C处理。在12点后原来排在Shard 1中的请求,分别到Shard3,Shard4中。

    12点后请求消费状态:

    流量持续到下午4点后,开始逐渐减少

    因此银行经理让柜员A、B休息,让C同事处理Shard2,Shard3,Shard4中的请求。并逐步将Shard2与Shard3合并成Shard5,最后将Shard5和Shard4合并成一个Shard,当处理完成Shard中所有请求后银行关门。

    现实中的日志处理

    上述过程可以抽象成日志处理的经典场景,如果要解决银行的业务需求,我们要提供弹性伸缩、并且灵活适配的日志基础框架,包括:

    1. 对Shard进行弹性伸缩,参考LogHub弹性伸缩(Merge/Split)
    2. 消费者上线与下线能够对Shard自动适配,过程中数据不丢失,参考LogHub Consumer Library-协同消费组自动负载均衡
    3. 过程中支持保序,参考LogHub支持保序写入和消费
    4. 过程中不重复(需要消费者配合)。
    5. 观察到消费进度,以便合理调配计算资源,参考通过控制台查看协同消费组进度
    6. 支持更多渠道日志接入(对银行而言开通网上银行、手机银行、支票等渠道,可以接入更多的用户请求),参考LogHub多种数据接入方式

    通过LogHub + LogHub Consumer Library 能够帮助您解决日志实时处理中的这些经典问题,只需把精力放在业务逻辑上,而不用去担心流量扩容、Failover等琐事。

    另外,StormSpark Streaming已经通过Consumer Library实现了对应的接口,欢迎使用。日志服务的更多技术资讯和讨论,请查看日志服务的主页日志处理圈子

    协同消费库(Consumer Library)是对日志服务中日志进行消费的高级模式,提供了消费组(ConsumerGroup)的概念对消费端进行抽象和管理,和直接使用SDK进行数据读取的区别在于,用户无需关心日志服务的实现细节,只需要专注于业务逻辑,另外,消费者之间的负载均衡、failover等用户也都无需关心。

    Spark StreamingStorm 以及Flink Connector都以Consumer Library作为基础实现。

    消费组消费

    协同消费库(Consumer Library)是对日志服务中日志进行消费的高级模式,提供了消费组(ConsumerGroup)的概念对消费端进行抽象和管理,和直接使用SDK进行数据读取的区别在于,用户无需关心日志服务的实现细节,只需要专注于业务逻辑,另外,消费者之间的负载均衡、failover等用户也都无需关心。

    Spark StreamingStorm 以及Flink Connector都以Consumer Library作为基础实现。

    功能

    使用消费库之前有两个概念需要理解,分别是消费组(ConsumerGroup)、消费者(Consumer)。

    • 消费组

      一个消费组由多个消费者构成,同一个消费组下面的消费者共同消费一个Logstore中的数据,消费者之间不会重复消费数据。

    • 消费者

      消费组的构成单元,实际承担消费任务,同一个消费组下面的消费者名称必须不同。

    在日志服务中,一个Logstore下面会有多个shard,协同消费库的功能就是将shard分配给一个消费组下面的消费者,分配方式遵循以下原则:

    • 每个shard只会分配到一个消费者。
    • 一个消费者可以同时拥有多个shard。

    新的消费者加入一个消费组,这个消费组下面的shard从属关系会调整,以达到消费负载均衡的目的,但是上面的分配原则不会变,分配过程对用户透明。

    协同消费库的另一个功能是保存checkpoint,方便程序故障恢复时能接着从断点继续消费,从而保证数据不会被重复消费。

    相关文章

      网友评论

        本文标题:Loghub 最佳实践

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